Watchdog timers on the nRF9160

We programmers never write bugs. And when we do, these bugs sometimes bring the program screeching to a halt. While this is typically better than accidentally leaking user data or launching nuclear missiles, it is still decidedly inconvenient1 for the user to have to contact customer service, wait on hold for half an hour, and then be asked “Have you tried turning it off an on again?”.

The solution, as always…

Automate it.

A watchdog timer is a hardware peripheral that vaguely resembles2 the professional variety of man’s best friend: When a watchdog timer is not fed frequently enough, it barks. That barking manifests as resetting the device. No more annoyed customer service agent!

Since the timer is a peripheral, its operation is unaffected by the buggy program. Corrupt all the memory you want, the timer shall count down. When your program eventually crashes, deadlocks, or refuses to yield, the timer will reach zero and reset the system, making everything right again.

Zephyr

Watchdog timers look easy enough to use, as described and exemplified. The tough part was figuring out how to use them in my application on the nRF9160.

Unlike the sample application above, my application is, in the parlance of Trusted Execution Environment (TEE), an untrusted application (UA). That is, its configuration includes CONFIG_TRUSTED_EXECUTION_NONSECURE=y (implicit when using the nrf9160_pca10090ns board).

UAs have restricted access to certain services and peripherals, including watchdog timers. But the trusted application (TA), which runs before the UA, can relax these restrictions. In particular, if the TA includes CONFIG_SPM_NRF_WDT_NS=y in its configuration, then watchdog timers will be usable by the UA.

When using the nrf9160_pca10090ns board, a TA is automatically built and merged with the UA image. Overriding its configuration is simply a matter of adding to our project a file named spm.conf, whose contents are the same as the original SPM configuration with the addition of the config variable mentioned above:

# Configuration copied from the nRF Secure Partition Manager (SPM) sample:
CONFIG_IS_SPM=y
CONFIG_GPIO=n

# Make watchdog timers non-secure (so they can be used in the non-secure image).
CONFIG_SPM_NRF_WDT_NS=y

Then, we add the following line to our CMakeLists.txt so that our new configuration file is used when building SPM instead of the normal one:

set(spm_CONF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/spm.conf)

I leave it as an exercise to the reader to find a more proper solution, for example by creating a TA of your own by copying and modifying the SPM sample, which would involve learning how to build and configure multiple images. In any case, the current solution has minimalism going for it.


  1. Or, in the case of life-support systems, decidedly fatal. [return]
  2. The analogy breaks down pretty quickly. Firstly, it is trained to bark at the absence of a thing, instead of its presence. Secondly, that thing is food3, not intruders. Really, it’s more like the overbred house pet variety. [return]
  3. Or, if you are cruel (and even worse at analogy), kicks. [return]