|
NetBurner 3.5.8
PDF Version |
Example Path: examples/PlatformSpecific/SOMRT1061/Watchdog
Supported Platforms: SOMRT1061
This example shows how to use the hardware watchdog on the SOMRT1061 module (i.MX RT1061) to automatically recover a hung application by rebooting it. A watchdog is a hardware timer that resets the processor unless the application keeps restarting it ("servicing" it). If the application locks up and stops servicing the watchdog, the timer expires and the module reboots itself back into a working state.
The example is driven by a simple serial menu on the debug console, and on every boot it reports why the module last reset.
Arming and servicing the watchdog is done through a small helper API in src/nbwatchdog.h:
That is the whole idea: arm it once, then call WatchdogService() regularly from healthy code. Stop servicing it (because you crashed or hung) and the module reboots on its own.
Connect a terminal to the debug serial port. The menu:
| Key | Action |
|---|---|
| R | Show why the module last reset |
| E | Arm the watchdog (prompts for a timeout in seconds) |
| F | Service the watchdog once |
| A | Toggle automatic servicing on/off |
| X | Stop servicing -> demonstrate the automatic reboot |
| B | ForceReboot() – a plain software reboot, for comparison |
| ? | Redisplay the menu |
A typical session:
The API is just four calls (all in src/nbwatchdog.h):
Usage is: arm once with a timeout, then service it from healthy code more often than that timeout.
A few rules worth knowing:
On the SOMRT1061 a watchdog timeout does not simply reset-and-reboot the way you might expect. A raw i.MX hardware watchdog reset resets the CPU core, but the module does not cleanly reboot from it – it hangs, and only a power cycle recovers it. (Two reasons: the watchdog's external WDOG_B reset pin is not routed on the module, and a clean reboot on this module requires a specific software-reset sequence – ForceReboot() / DoSWReset() in arch/cortex-m7/cpu/MIMXRT10xx/source/cpu_hal.cpp – that a bare hardware reset does not perform.)
So WatchdogEnable() does not rely on the raw reset. Instead it arms WDOG1 with a pre-timeout interrupt that fires just before the hardware timeout, and that interrupt performs a clean ForceReboot() – the proper, bootable reset path on this module. The net effect is exactly what you want:
not serviced in time -> clean, automatic reboot back into the application.
The raw (hanging) reset is only ever reached if the core is so wedged that interrupts no longer run at all (for example a hard fault with interrupts masked) – and a board in that state needs a power cycle regardless. For the normal case of a hung or stuck task, the pre-timeout interrupt catches it and reboots cleanly.
This example services WDOG1 from its own small task (WatchdogServiceTask in src/main.cpp), not from the menu loop. That is deliberate. The menu loop blocks on gets() waiting for a keypress; if the servicing lived in that loop, an idle console (nobody typing) would stop the servicing and reboot the module. A separate service task keeps the watchdog serviced no matter what the rest of the application is doing. The menu's X command simply tells that task to stop, which is how the demonstration reboot is triggered.
In a real application you would go one step further and make the service task service the watchdog only while your critical work is making progress (for example, only if a "heartbeat" counter bumped by that work keeps changing) – so that a hang in the work itself still trips the watchdog.
Reprogramming the module writes the new application to flash, and a flash write stalls normal task scheduling – long enough that an armed watchdog would otherwise reset the module in the middle of the update. If that happened, and the application re-armed the watchdog on boot, updates could become impossible to complete and the application unrecoverable (short of the Alternate Boot Monitor).
There is no good reason to arm the watchdog but leave updates able to trip it, so WatchdogEnable() takes care of it automatically: it registers the NetBurner watchdog service hook (watchdog_service_function), which the flash/update routines call throughout an update. Nothing extra is required on your part – an armed watchdog simply survives a firmware update.
Because a watchdog is a reset source, it is worth being clear about how reset actually reaches things on this module.
A watchdog reboot does not drive these pins to external chips. The watchdog resets the i.MX RT1061 internally; peripherals on the board come back because the boot firmware re-initialises them. For example the Ethernet PHY (DP83825I) is reset by a dedicated GPIO, Pin_B0_07 (board net E_RST), each time the network stack starts (arch/cortex-m7/cpu/MIMXRT10xx/source/ethernet.cpp), and the external HyperRAM/PSRAM is re-initialised by the FlexSPI controller. If your baseboard needs its devices reset in lock-step with the module, wire them to RSTO** (or to RSTI-driven logic) – do not expect the watchdog to reach them.
On every boot the example decodes the processor's reset-status registers (SRC->SRSR and WDOG1->WRSR) and prints a human-readable cause (menu key R): power-on, external reset pin, software reset, and so on.
After arming and pressing X:
Because the watchdog cannot be turned off once armed, application code that arms it at boot and then hangs before servicing it can create a boot loop. This example avoids that by staying un-armed at boot – so arming it here can never brick the board. If your own code does get a board stuck, use the module's Alternate Boot Monitor** to load a recovery application; see the NNDK documentation (/nburn/docs/NetBurner/...) for the SOMRT1061 recovery procedure.
The i.MX RT1061 also contains a second watchdog (WDOG2) and a separate real-time watchdog (RTWDOG). On the SOMRT1061 neither can be used as a dependable watchdog, so this example uses WDOG1 exclusively. WDOG1 is the general-purpose application watchdog and is all you need on this module.