WDT, Watchdog Timer - mhightower83/Arduino-ESP8266-misc GitHub Wiki

Watchdog Timer

Hardware WDT

Software WDT

I found this site late similar details here

My notes and explorations with WDT inspired by the source code found here.

On the ESP8266, there are two independent Watchdog Timers one hardware and one software using an NMI timer. The WDT hardware supports a level-1 interrupt timer and a hardware reset timer. NON-OS SDK does not supply an ISR handler for the WDT level-1 interrupt. The event is ignored; however, the elapsed time is added to the total time for a WDT Reset to occur. The SDK has a separate software-based WDT using a timer with NMI support.

TODO: As it pertains to NMI, Some details for NON-OS SDK are mixed up with RTOS-SDK, need to untangle

Hardware WDT

Some defines:

#define REG_WRITE(_r, _v)       (*(volatile uint32_t *)(_r)) = (_v)
#define REG_READ(_r)            (*(volatile uint32_t *)(_r))
//
//Watch dog reg {{
#define PERIPHS_WDT_BASEADDR        0x60000900

#define WDT_CTL_ADDRESS             0
#define WDT_OP_ADDRESS              0x4             // WDT Primary Interval
#define WDT_OP_ND_ADDRESS           0x8             // WDT Seconday Interval
#define WDT_RST_ADDRESS             0x14

#define WDT_CTL_RSTLEN_MASK         0x38
#define WDT_CTL_RSPMOD_MASK         0x6
#define WDT_CTL_EN_MASK             0x1

#define WDT_CTL_RSTLEN_LSB          0x3
#define WDT_CTL_RSPMOD_LSB          0x1
#define WDT_CTL_EN_LSB              0

#define WDT_FEED_VALUE              0x73

#define WDT_REG_READ(_reg)                  REG_READ(PERIPHS_WDT_BASEADDR + _reg)
#define WDT_REG_WRITE(_reg, _val)           REG_WRITE(PERIPHS_WDT_BASEADDR + _reg, _val)
#define CLEAR_WDT_REG_MASK(_reg, _mask)     WDT_REG_WRITE(_reg, WDT_REG_READ(_reg) & (~_mask))
#define SET_WDT_REG_MASK(_reg, _mask, _val) SET_PERI_REG_BITS((PERIPHS_WDT_BASEADDR + _reg), _mask, _val, 0)
#undef WDT_FEED
#define WDT_FEED()                          WDT_REG_WRITE(WDT_RST_ADDRESS, WDT_FEED_VALUE)

//}}

Code to change the hardware WDT times

    xt_rsil(15);

    CLEAR_WDT_REG_MASK(WDT_CTL_ADDRESS, BIT0);  // This will disable HWDT
    const uint32_t panic_time_param = 11;

    our_wdt_init(panic_time_param);

    // Assume both WDT Interrupt and WDT Reset are turned on.
    // The sum of these two is the time before HWDT
    // Note these appear to be reversed in the RTOS SDK examples

    // Duration of "Not Fed Interrupt" before WDT Interrupt is asserted.
    WDT_REG_WRITE(WDT_OP_ADDRESS, panic_time_param);    // 2^n * 0.8ms, mask 0xf, n = 11 -> (2^11 = 2048) * 0.8 * 0.001 = 1.6384


    // Duration after the "Not Fed Interrupt" before the Hardware WDT reset is asserted.
    WDT_REG_WRITE(WDT_OP_ND_ADDRESS, 13);               // 2^n * 0.8ms, mask 0xf, n = 13 -> (2^13 = 8192) * 0.8 * 0.001 = 6.5536


    SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_RSTLEN_MASK, 7 << WDT_CTL_RSTLEN_LSB, 0);

WDT_CTL_RSPMOD_MASK allows the selection of either a level-1 interrupt or hardware reset after the timer, defined at WDT_OP_ADDRESS, elapses. Or you can use both. In this scenario, WDT_OP_ADDRESS holds the level-1 interrupt timer value and WDT_OP_ND_ADDRESS holds the timer value for the hardware reset. The two timers work in a cascade. First, the level-1 interrupt timer has to expire then the hardware reset timer must expire before the ESP8266 reboots.

Values for WDT_CTL_RSPMOD_MASK:

  • BIT1 0x02 - 0 Hardware Reset enabled
  • BIT2 0x04 - 0 level-1 interrupt enabled - install handler for "#define ETS_WDT_INUM 8"
  • 0, Both WDT Interrupt and WDT Reset are enabled, using WDT_OP_ADDRESS and WDT_OP_ND_ADDRESS respectfully. If an ISR is not attached, then the two counter values are summed for the duration before HWDT Reset. NON-OS SDK implements an NMI Timer-based Soft WDT instead of using a level-1 WDT handler.
  • 1, Disables WDT Reset. We have WDT Interrupt after the duration defined by WDT_OP_ADDRESS. To receive another "interrupt", you must feed the dog.
  • 2, Disables WDT Interrupt. We have WDT Reset after the duration defined by WDT_OP_ADDRESS
  • 3, same as 2. You cannot disable the WDT with this option.
    // interrupt then reset
    SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_RSPMOD_MASK, 0 << WDT_CTL_RSPMOD_LSB, 0);

    // start task watch dog1
    SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 1 << WDT_CTL_EN_LSB, 0);

Software WDT

I have some of this mixed up. NON-OS SDK appears to use level-1 interrupts to implement the Soft WDT. RTOS-SDK (need to recheck) is using NMI In SDK v3.0.5, a 64-bit counter creates an NMI interrupt every 3 seconds. Without calls to feed the Watchdog, the NMI interrupt handler code calls system_restart_local() on the second interrupt. This function is wrapped by the Arduino ESP8266 Postmortem and presents a stack trace before allowing the SDK to continue. Calls to system_soft_wdt_feed(), system_soft_wdt_stop(), or system_soft_wdt_restart() will all feed both the hardware Watchdog and the NMI Interrupt-based Software Watchdog. Without feeding the Watchdog, the time it takes to get a Soft WDT event ranges from 3 to under 6 seconds. This has to do with the timing of your call to feed and the 3-second interval NMI interrupt timer.

The Soft WDT NMI interrupt interval can be changed by adding lines like those below. Note unless you alter the hardware WDT you could hit its 8.2-second timer and reboot without notice.

#include <user_interface.h>
extern uint32_t soft_wdt_interval;
soft_wdt_interval = 4 * 1000 * 1000; // us, about 4 - 8 secs before Soft WDT Exception.
system_soft_wdt_restart(); 

system_soft_wdt_stop() effectively does a system_soft_wdt_feed() and restores the compile time 3-second interval timer. It does not stop anything. This undoes the above example. For feeding the WDT, use system_soft_wdt_feed().