Watchdogs
Overview
The ESP-IDF has support for multiple types of watchdogs, with the two main ones being: The Interrupt Watchdog Timer and the Task Watchdog Timer (TWDT). The Interrupt Watchdog Timer and the TWDT can both be enabled using Project Configuration Menu, however the TWDT can also be enabled during runtime. The Interrupt Watchdog is responsible for detecting instances where FreeRTOS task switching is blocked for a prolonged period of time. The TWDT is responsible for detecting instances of tasks running without yielding for a prolonged period.
Interrupt watchdog
The interrupt watchdog makes sure the FreeRTOS task switching interrupt isn’t blocked for a long time. This is bad because no other tasks, including potentially important ones like the WiFi task and the idle task, can’t get any CPU runtime. A blocked task switching interrupt can happen because a program runs into an infinite loop with interrupts disabled or hangs in an interrupt.
The default action of the interrupt watchdog is to invoke the panic handler. causing a register dump and an opportunity for the programmer to find out, using either OpenOCD or gdbstub, what bit of code is stuck with interrupts disabled. Depending on the configuration of the panic handler, it can also blindly reset the CPU, which may be preferred in a production environment.
The interrupt watchdog is built around the hardware watchdog in timer group 1. If this watchdog for some reason cannot execute the NMI handler that invokes the panic handler (e.g. because IRAM is overwritten by garbage), it will hard-reset the SOC. If the panic handler executes, it will display the panic reason as “Interrupt wdt timeout on CPU0” or “Interrupt wdt timeout on CPU1” (as applicable).
Configuration
The interrupt watchdog is enabled by default via the CONFIG_ESP_INT_WDT configuration flag. The timeout is configured by setting CONFIG_ESP_INT_WDT_TIMEOUT_MS. The default timeout is higher if PSRAM support is enabled, as a critical section or interrupt routine that accesses a large amount of PSRAM will take longer to complete in some circumstances. The INT WDT timeout should always be longer than the period between FreeRTOS ticks (see CONFIG_FREERTOS_HZ).
Tuning
If you find the Interrupt watchdog timeout is triggering because an interrupt or critical section is running longer than the timeout period, consider rewriting the code: critical sections should be made as short as possible, with non-critical computation happening outside the critical section. Interrupt handlers should also perform the minimum possible amount of computation, consider pushing data into a queue from the ISR and processing it in a task instead. Neither critical sections or interrupt handlers should ever block waiting for another event to occur.
If changing the code to reduce the processing time is not possible or desirable, it’s possible to increase the CONFIG_ESP_INT_WDT_TIMEOUT_MS setting instead.
Task Watchdog Timer
The Task Watchdog Timer (TWDT) is responsible for detecting instances of tasks running for a prolonged period of time without yielding. This is a symptom of CPU starvation and is usually caused by a higher priority task looping without yielding to a lower-priority task thus starving the lower priority task from CPU time. This can be an indicator of poorly written code that spinloops on a peripheral, or a task that is stuck in an infinite loop.
By default the TWDT will watch the Idle task, however any task can subscribe to be watched by the TWDT. Each watched task must ‘reset’ the TWDT periodically to indicate that they have been allocated CPU time. If a task does not reset within the TWDT timeout period, a warning will be printed with information about which tasks failed to reset the TWDT in time and which tasks are currently running.
It is also possible to redefine the function esp_task_wdt_isr_user_handler in the user code, in order to receive the timeout event and handle it differently.
The TWDT is built around the Hardware Watchdog Timer in Timer Group 0. The TWDT
can be initialized by calling esp_task_wdt_init()
which will configure
the hardware timer. A task can then subscribe to the TWDT using
esp_task_wdt_add()
in order to be watched. Each subscribed task must
periodically call esp_task_wdt_reset()
to reset the TWDT. Failure by
any subscribed tasks to periodically call esp_task_wdt_reset()
indicates that one or more tasks have been starved of CPU time or are stuck in a
loop somewhere.
A watched task can be unsubscribed from the TWDT using
esp_task_wdt_delete()
. A task that has been unsubscribed should no
longer call esp_task_wdt_reset()
. Once all tasks have unsubscribed
form the TWDT, the TWDT can be deinitialized by calling
esp_task_wdt_deinit()
.
The default timeout period for the TWDT is set using config item
CONFIG_ESP_TASK_WDT_TIMEOUT_S. This should be set to at least as long as you expect any
single task will need to monopolise the CPU (for example, if you expect the app will do a long
intensive calculation and should not yield to other tasks). It is also possible to change this
timeout at runtime by calling esp_task_wdt_init()
.
The following config options control TWDT configuration at startup. They are all enabled by default:
CONFIG_ESP_TASK_WDT - the TWDT is initialized automatically during startup. If this option is disabled, it is still possible to initialize the Task WDT at runtime by calling
esp_task_wdt_init()
.CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 - CPU0 Idle task is subscribed to the TWDT during startup. If this option is disabled, it is still possible to subscribe the idle task by calling
esp_task_wdt_add()
at any time.CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 - CPU1 Idle task is subscribed to the TWDT during startup.
JTAG and watchdogs
While debugging using OpenOCD, the CPUs will be halted every time a breakpoint is reached. However if the watchdog timers continue to run when a breakpoint is encountered, they will eventually trigger a reset making it very difficult to debug code. Therefore OpenOCD will disable the hardware timers of both the interrupt and task watchdogs at every breakpoint. Moreover, OpenOCD will not reenable them upon leaving the breakpoint. This means that interrupt watchdog and task watchdog functionality will essentially be disabled. No warnings or panics from either watchdogs will be generated when the ESP32-S3 is connected to OpenOCD via JTAG.
XTAL32K Watchdog Timer (XTWDT)
The XTAL32K watchdog makes sure the (optional) external 32 KHz crystal or oscillator is functioning correctly.
When XTAL32K_CLK works as the clock source of RTC_SLOW_CLK and stops oscillating, the XTAL32K watchdog timer will detect this and generate an interrupt. It also provides functionality for automatically switching over to the internal, but less accurate oscillator as the RTC_SLOW_CLK source.
Since the switch to the backup clock is done in hardware it can also happen during deep sleep. This means that even if XTAL32K_CLK stops functioning while the chip in deep sleep, waiting for a timer to expire, it will still be able to wake-up as planned.
If the XTAL32K_CLK starts functioning normally again, you can call esp_xt_wdt_restore_clk to switch back to this clock source and re-enable the watchdog timer.
Configuration
When the external 32KHz crystal or oscillator is selected (CONFIG_ESP32S3_RTC_CLK_SRC) the XTAL32K watchdog can be enabled via the CONFIG_ESP_XT_WDT configuration flag. The timeout is configured by setting CONFIG_ESP_XT_WDT_TIMEOUT. The automatic backup clock functionality is enabled via the ref:CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE configuration.
Functions
-
void esp_int_wdt_init(void)
Initialize the non-CPU-specific parts of interrupt watchdog. This is called in the init code if the interrupt watchdog is enabled in menuconfig.
Task Watchdog API Reference
A full example using the Task Watchdog is available in esp-idf: system/task_watchdog
Header File
Functions
-
esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
Initialize the Task Watchdog Timer (TWDT)
This function configures and initializes the TWDT. If the TWDT is already initialized when this function is called, this function will update the TWDT’s timeout period and panic configurations instead. After initializing the TWDT, any task can elect to be watched by the TWDT by subscribing to it using esp_task_wdt_add().
备注
esp_task_wdt_init() must only be called after the scheduler started
- 参数
timeout – [in] Timeout period of TWDT in seconds
panic – [in] Flag that controls whether the panic handler will be executed when the TWDT times out
- 返回
ESP_OK: Initialization was successful
ESP_ERR_NO_MEM: Initialization failed due to lack of memory
-
esp_err_t esp_task_wdt_deinit(void)
Deinitialize the Task Watchdog Timer (TWDT)
This function will deinitialize the TWDT. Calling this function whilst tasks are still subscribed to the TWDT, or when the TWDT is already deinitialized, will result in an error code being returned.
- 返回
ESP_OK: TWDT successfully deinitialized
ESP_ERR_INVALID_STATE: Error, tasks are still subscribed to the TWDT
ESP_ERR_NOT_FOUND: Error, TWDT has already been deinitialized
-
esp_err_t esp_task_wdt_add(TaskHandle_t handle)
Subscribe a task to the Task Watchdog Timer (TWDT)
This function subscribes a task to the TWDT. Each subscribed task must periodically call esp_task_wdt_reset() to prevent the TWDT from elapsing its timeout period. Failure to do so will result in a TWDT timeout. If the task being subscribed is one of the Idle Tasks, this function will automatically enable esp_task_wdt_reset() to called from the Idle Hook of the Idle Task. Calling this function whilst the TWDT is uninitialized or attempting to subscribe an already subscribed task will result in an error code being returned.
- 参数
handle – [in] Handle of the task. Input NULL to subscribe the current running task to the TWDT
- 返回
ESP_OK: Successfully subscribed the task to the TWDT
ESP_ERR_INVALID_ARG: Error, the task is already subscribed
ESP_ERR_NO_MEM: Error, could not subscribe the task due to lack of memory
ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
-
esp_err_t esp_task_wdt_reset(void)
Reset the Task Watchdog Timer (TWDT) on behalf of the currently running task.
This function will reset the TWDT on behalf of the currently running task. Each subscribed task must periodically call this function to prevent the TWDT from timing out. If one or more subscribed tasks fail to reset the TWDT on their own behalf, a TWDT timeout will occur. If the IDLE tasks have been subscribed to the TWDT, they will automatically call this function from their idle hooks. Calling this function from a task that has not subscribed to the TWDT, or when the TWDT is uninitialized will result in an error code being returned.
- 返回
ESP_OK: Successfully reset the TWDT on behalf of the currently running task
ESP_ERR_NOT_FOUND: Error, the current running task has not subscribed to the TWDT
ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
-
esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
Unsubscribes a task from the Task Watchdog Timer (TWDT)
This function will unsubscribe a task from the TWDT. After being unsubscribed, the task should no longer call esp_task_wdt_reset(). If the task is an IDLE task, this function will automatically disable the calling of esp_task_wdt_reset() from the Idle Hook. Calling this function whilst the TWDT is uninitialized or attempting to unsubscribe an already unsubscribed task from the TWDT will result in an error code being returned.
- 参数
handle – [in] Handle of the task. Input NULL to unsubscribe the current running task.
- 返回
ESP_OK: Successfully unsubscribed the task from the TWDT
ESP_ERR_INVALID_ARG: Error, the task is already unsubscribed
ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
-
esp_err_t esp_task_wdt_status(TaskHandle_t handle)
Query whether a task is subscribed to the Task Watchdog Timer (TWDT)
This function will query whether a task is currently subscribed to the TWDT, or whether the TWDT is initialized.
- 参数
handle – [in] Handle of the task. Input NULL to query the current running task.
- 返回
:
ESP_OK: The task is currently subscribed to the TWDT
ESP_ERR_NOT_FOUND: The task is currently not subscribed to the TWDT
ESP_ERR_INVALID_STATE: The TWDT is not initialized, therefore no tasks can be subscribed