WDT: implement interrupt wdt and task wdt for ESP32-C2

ESP32-C2 has a single group timer, thus it will use it for the interrupt watchdog,
which is more critical than the task watchdog. The latter is implement in
software thanks to the `esp_timer`component.
This commit is contained in:
Omar Chebib 2022-07-07 14:54:15 +08:00
parent 5b39159e66
commit 53c7dd4efc
37 changed files with 500 additions and 155 deletions

View File

@ -1015,7 +1015,7 @@ UT_S2_SDSPI:
UT_C2:
extends: .unit_test_esp32c2_template
parallel: 21
parallel: 22
tags:
- ESP32C2_IDF
- UT_T1_1

View File

@ -22,8 +22,6 @@
#include "test_utils.h"
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
//IDF-4035
static const char* TAG = "test_event";
#define TEST_CONFIG_ITEMS_TO_REGISTER 5
@ -2021,4 +2019,3 @@ TEST_CASE("can post events from interrupt handler", "[event]")
}
#endif // CONFIG_ESP_EVENT_POST_FROM_ISR
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)

View File

@ -48,6 +48,7 @@
#include "esp_private/sleep_retention.h"
#include "esp_private/esp_clk.h"
#include "esp_private/startup_internal.h"
#include "esp_private/esp_task_wdt.h"
#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
@ -663,6 +664,13 @@ static inline bool can_power_down_vddsdio(const uint32_t vddsdio_pd_sleep_durati
esp_err_t esp_light_sleep_start(void)
{
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_err_t timerret = ESP_OK;
/* If a task watchdog timer is running, we have to stop it. */
timerret = esp_task_wdt_stop();
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
s_config.ccount_ticks_record = esp_cpu_get_cycle_count();
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock);
@ -821,6 +829,14 @@ esp_err_t esp_light_sleep_start(void)
}
portEXIT_CRITICAL(&light_sleep_lock);
s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
/* Restart the Task Watchdog timer as it was stopped before sleeping. */
if (timerret == ESP_OK) {
esp_task_wdt_restart();
}
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
return err;
}

View File

@ -33,7 +33,7 @@ entries:
freertos_hooks:esp_vApplicationIdleHook (noflash)
if PM_SLP_IRAM_OPT = y:
task_wdt:idle_hook_cb (noflash)
task_wdt:reset_hw_timer (noflash)
task_wdt:task_wdt_timer_feed (noflash)
task_wdt:find_entry_and_check_all_reset (noflash)
task_wdt:find_entry_from_task_handle_and_check_all_reset (noflash)
task_wdt:esp_task_wdt_reset (noflash)
@ -43,6 +43,9 @@ entries:
archive: libesp_timer.a
entries:
if PM_SLP_IRAM_OPT = y:
# esp_timer_feed is called from task_wdt_timer_feed, so put it
# in IRAM if task_wdt_timer_feed itself is in IRAM.
esp_timer:esp_timer_feed (noflash)
if ESP_TIMER_IMPL_TG0_LAC = y:
esp_timer_impl_lac:esp_timer_impl_lock (noflash)
esp_timer_impl_lac:esp_timer_impl_unlock (noflash)

View File

@ -21,11 +21,14 @@ else()
"startup.c"
"system_time.c"
"stack_check.c"
"task_wdt.c"
"ubsan.c"
"xt_wdt.c"
"debug_stubs.c")
if(CONFIG_ESP_TASK_WDT)
list(APPEND srcs "task_wdt.c")
endif()
if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)
list(APPEND srcs "eh_frame_parser.c")
endif()

View File

@ -370,7 +370,6 @@ menu "ESP System Settings"
config ESP_INT_WDT
bool "Interrupt watchdog"
default n if IDF_TARGET_ESP32C2 # add support in IDF-4114
default y
help
This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
@ -395,18 +394,35 @@ menu "ESP System Settings"
Also detect if interrupts on CPU 1 are disabled for too long.
config ESP_TASK_WDT
bool "Initialize Task Watchdog Timer on startup"
bool "Enable Task Watchdog Timer"
default y
select FREERTOS_ENABLE_TASK_SNAPSHOT
help
The Task Watchdog Timer can be used to make sure individual tasks are still
running. Enabling this option will cause the Task Watchdog Timer to be
initialized automatically at startup. The Task Watchdog timer can be
initialized after startup as well (see Task Watchdog Timer API Reference)
running. Enabling this option will enable the Task Watchdog Timer. It can be
either initialized automatically at startup or initialized after startup
(see Task Watchdog Timer API Reference)
config ESP_TASK_WDT_USE_ESP_TIMER
# Software implementation of Task Watchdog, handy for targets with only a single
# Timer Group, such as the ESP32-C2
bool
depends on ESP_TASK_WDT
default y if IDF_TARGET_ESP32C2
default n if !IDF_TARGET_ESP32C2
select ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
config ESP_TASK_WDT_INIT
bool "Initialize Task Watchdog Timer on startup"
depends on ESP_TASK_WDT
default y
help
Enabling this option will cause the Task Watchdog Timer to be initialized
automatically at startup.
config ESP_TASK_WDT_PANIC
bool "Invoke panic handler on Task Watchdog timeout"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
default n
help
If this option is enabled, the Task Watchdog Timer will be configured to
@ -415,7 +431,7 @@ menu "ESP System Settings"
config ESP_TASK_WDT_TIMEOUT_S
int "Task Watchdog timeout period (seconds)"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
range 1 60
default 5
help
@ -424,7 +440,7 @@ menu "ESP System Settings"
config ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
bool "Watch CPU0 Idle Task"
depends on ESP_TASK_WDT
depends on ESP_TASK_WDT_INIT
default y
help
If this option is enabled, the Task Watchdog Timer will watch the CPU0
@ -435,10 +451,10 @@ menu "ESP System Settings"
config ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
bool "Watch CPU1 Idle Task"
depends on ESP_TASK_WDT && !FREERTOS_UNICORE
depends on ESP_TASK_WDT_INIT && !FREERTOS_UNICORE
default y
help
If this option is enabled, the Task Wtachdog Timer will wach the CPU1
If this option is enabled, the Task Watchdog Timer will wach the CPU1
Idle Task.
config ESP_XT_WDT

View File

@ -96,12 +96,14 @@ static void IRAM_ATTR esp_crosscore_isr(void *arg) {
esp_backtrace_print(100);
}
#if CONFIG_ESP_TASK_WDT
if (my_reason_val & REASON_TWDT_ABORT) {
extern void task_wdt_timeout_abort_xtensa(bool);
/* Called from a crosscore interrupt, thus, we are not the core that received
* the TWDT interrupt, call the function with `false` as a parameter. */
task_wdt_timeout_abort_xtensa(false);
}
#endif // CONFIG_ESP_TASK_WDT
#endif // CONFIG_IDF_TARGET_ARCH_XTENSA
}
@ -171,7 +173,9 @@ void IRAM_ATTR esp_crosscore_int_send_print_backtrace(int core_id)
esp_crosscore_int_send(core_id, REASON_PRINT_BACKTRACE);
}
#if CONFIG_ESP_TASK_WDT
void IRAM_ATTR esp_crosscore_int_send_twdt_abort(int core_id) {
esp_crosscore_int_send(core_id, REASON_TWDT_ABORT);
}
#endif // CONFIG_ESP_TASK_WDT
#endif

View File

@ -6,10 +6,32 @@
#pragma once
#include "system_internal.h"
#include "soc/periph_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_TIMER_GROUPS > 1
/* If we have two hardware timer groups, use the second one for interrupt watchdog. */
#define WDT_LEVEL_INTR_SOURCE ETS_TG1_WDT_LEVEL_INTR_SOURCE
#define IWDT_PRESCALER MWDT1_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US MWDT1_TICKS_PER_US
#define IWDT_INSTANCE WDT_MWDT1
#define IWDT_INITIAL_TIMEOUT_S 5
#else
#define WDT_LEVEL_INTR_SOURCE ETS_TG0_WDT_LEVEL_INTR_SOURCE
#define IWDT_PRESCALER MWDT0_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US MWDT0_TICKS_PER_US
#define IWDT_INSTANCE WDT_MWDT0
#define IWDT_INITIAL_TIMEOUT_S 5
#endif // SOC_TIMER_GROUPS > 1
/**
* @brief Initialize the non-CPU-specific parts of interrupt watchdog.
*

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "esp_err.h"
#if CONFIG_ESP_TASK_WDT
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Stop the Task Watchdog Timer (TWDT)
*
* This function will temporarily stop the timer until it is restarted/resumed by a call to esp_task_wdt_restart().
* @note esp_task_wdt_stop() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully stopped
* - Other: Failed to stop the TWDT
*/
esp_err_t esp_task_wdt_stop(void);
/**
* @brief Restart the Task Watchdog Timer (TWDT)
*
* This function will restart/resume the timer after it has been stopped by esp_task_wdt_stop().
* @note esp_task_wdt_restart() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully stopped
* - Other: Failed to stop the TWDT
*/
esp_err_t esp_task_wdt_restart(void);
#ifdef __cplusplus
}
#endif
#endif // CONFIG_ESP_TASK_WDT

View File

@ -1,16 +1,8 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -20,11 +12,25 @@ extern "C" {
#include "esp_system.h"
#if !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
/* All the targets that have more than one timer group are using
* APB clock by default, which frequency is 80MHz.
* Thus, we can determine the default parameter for the prescaler here */
#define MWDT0_TICK_PRESCALER 40000
#define MWDT0_TICKS_PER_US 500
#define MWDT1_TICK_PRESCALER 40000
#define MWDT1_TICKS_PER_US 500
#else
/* The targets that have a single timer group use XTAL clock as the
* default clock. XTAL clock frequency is 40MHz. */
#define MWDT0_TICK_PRESCALER 20000
#define MWDT0_TICKS_PER_US 500
#endif
/**
* @brief Internal function to restart PRO and APP CPUs.
*

View File

@ -36,7 +36,8 @@ typedef struct esp_task_wdt_user_handle_s * esp_task_wdt_user_handle_t;
* this function will update the TWDT's current configuration. This funciton will also subscribe the idle tasks if
* configured to do so. For other tasks, users can subscribe them using esp_task_wdt_add() or esp_task_wdt_add_user().
*
* @note esp_task_wdt_init() must only be called after the scheduler is started
* @note esp_task_wdt_init() must only be called after the scheduler is started. Moreover, it must not be called by
* multiple tasks simultaneously.
* @param[in] config Configuration structure
* @return
* - ESP_OK: Initialization was successful
@ -51,6 +52,7 @@ esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
* are still subscribed to the TWDT, or when the TWDT is already deinitialized, will result in an error code being
* returned.
*
* @note esp_task_wdt_deinit() must not be called by multiple tasks simultaneously.
* @return
* - ESP_OK: TWDT successfully deinitialized
* - Other: Failed to deinitialize TWDT
@ -150,6 +152,16 @@ esp_err_t esp_task_wdt_delete_user(esp_task_wdt_user_handle_t user_handle);
*/
esp_err_t esp_task_wdt_status(TaskHandle_t task_handle);
/**
* @brief User ISR callback placeholder
*
* This function is called by task_wdt_isr function (ISR for when TWDT times out). It can be defined in user code to
* handle TWDT events.
*
* @note It has the same limitations as the interrupt function. Do not use ESP_LOGx functions inside.
*/
void __attribute__((weak)) esp_task_wdt_isr_user_handler(void);
#ifdef __cplusplus
}
#endif

View File

@ -20,16 +20,9 @@
#include "esp_freertos_hooks.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_private/system_internal.h"
#if CONFIG_ESP_INT_WDT
#define WDT_INT_NUM ETS_T1_WDT_INUM
#define IWDT_INSTANCE WDT_MWDT1
#define IWDT_PRESCALER MWDT1_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US MWDT1_TICKS_PER_US
#define IWDT_INITIAL_TIMEOUT_S 5
static wdt_hal_context_t iwdt_context;
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
@ -131,7 +124,6 @@ void esp_int_wdt_init(void)
void esp_int_wdt_cpu_init(void)
{
#if SOC_TIMER_GROUPS > 1
assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to be at least twice the RTOS tick period!");
// Register tick hook for current CPU to feed the INT WDT
esp_register_freertos_tick_hook_for_cpu(tick_hook, esp_cpu_get_core_id());
@ -139,11 +131,11 @@ void esp_int_wdt_cpu_init(void)
* Register INT WDT interrupt for current CPU. We do this manually as the timeout interrupt should call an assembly
* panic handler (see riscv/vector.S and xtensa_vectors.S).
*/
esp_intr_disable_source(WDT_INT_NUM);
esp_rom_route_intr_matrix(esp_cpu_get_core_id(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
esp_intr_disable_source(ETS_INT_WDT_INUM);
esp_rom_route_intr_matrix(esp_cpu_get_core_id(), WDT_LEVEL_INTR_SOURCE, ETS_INT_WDT_INUM);
#if SOC_CPU_HAS_FLEXIBLE_INTC
esp_cpu_intr_set_type(WDT_INT_NUM, INTR_TYPE_LEVEL);
esp_cpu_intr_set_priority(WDT_INT_NUM, SOC_INTERRUPT_LEVEL_MEDIUM);
esp_cpu_intr_set_type(ETS_INT_WDT_INUM, INTR_TYPE_LEVEL);
esp_cpu_intr_set_priority(ETS_INT_WDT_INUM, SOC_INTERRUPT_LEVEL_MEDIUM);
#endif
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
@ -157,11 +149,7 @@ void esp_int_wdt_cpu_init(void)
_lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
}
#endif
esp_intr_enable_source(WDT_INT_NUM);
#else // SOC_TIMER_GROUPS > 1
// TODO: Clean up code for ESP32-C2, IDF-4114
ESP_EARLY_LOGW("INT_WDT", "ESP32-C2 only has one timer group");
#endif // SOC_TIMER_GROUPS > 1
esp_intr_enable_source(ETS_INT_WDT_INUM);
}
#endif // CONFIG_ESP_INT_WDT

View File

@ -17,6 +17,7 @@
#include "hal/timer_hal.h"
#include "hal/wdt_types.h"
#include "hal/wdt_hal.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_private/panic_internal.h"
#include "port/panic_funcs.h"

View File

@ -13,6 +13,7 @@
#include "esp_private/panic_reason.h"
#include "riscv/rvruntime-frames.h"
#include "esp_private/cache_err_int.h"
#include "soc/timer_periph.h"
#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
#if CONFIG_IDF_TARGET_ESP32C2
@ -289,7 +290,7 @@ void panic_soc_fill_info(void *f, panic_info_t *info)
info->reason = pseudo_reason[PANIC_RSN_CACHEERR];
info->details = print_cache_err_details;
} else if (frame->mcause == ETS_T1_WDT_INUM) {
} else if (frame->mcause == ETS_INT_WDT_INUM) {
/* Watchdog interrupt occured, get the core on which it happened
* and update the reason/message accordingly. */

View File

@ -24,11 +24,13 @@
#include "esp_private/system_internal.h"
#include "esp_private/crosscore_int.h"
#include "freertos/task_snapshot.h"
#include "esp_timer.h"
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
#include "esp_private/eh_frame_parser.h"
#endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
#if CONFIG_IDF_TARGET_ARCH_RISCV && !CONFIG_ESP_SYSTEM_USE_EH_FRAME
/* Function used to print all the registers pointed by the given frame .*/
extern void panic_print_registers(const void *frame, int core);
@ -50,10 +52,15 @@ bool g_twdt_isr = false;
// ----------------------- Macros --------------------------
// HAL related variables and constants
// Use a hardware timer implementation or a software implementation
#define TWDT_HARDWARE_IMPL !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
#if TWDT_HARDWARE_IMPL
// HAL related variables and constants only defined in a hardware implementation
#define TWDT_INSTANCE WDT_MWDT0
#define TWDT_TICKS_PER_US MWDT0_TICKS_PER_US
#define TWDT_PRESCALER MWDT0_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#endif // TWDT_HARDWARE_IMPL
// ---------------------- Typedefs -------------------------
@ -71,11 +78,16 @@ struct twdt_entry {
// Structure used to hold run time configuration of the TWDT
typedef struct twdt_obj twdt_obj_t;
struct twdt_obj {
#if TWDT_HARDWARE_IMPL
wdt_hal_context_t hal;
intr_handle_t intr_handle;
#else // TWDT_HARDWARE_IMPL
esp_timer_handle_t sw_timer; // We use esp_timer to simulate a hardware WDT
uint32_t period_ms;
#endif // TWDT_HARDWARE_IMPL
SLIST_HEAD(entry_list_head, twdt_entry) entries_slist;
uint32_t idle_core_mask; // Current core's who's idle tasks are subscribed
bool panic; // Flag to trigger panic when TWDT times out
intr_handle_t intr_handle;
};
// ----------------------- Objects -------------------------
@ -94,19 +106,6 @@ static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
// ---------------------- Callbacks ------------------------
/**
* @brief User ISR callback placeholder
*
* This function is called by task_wdt_isr function (ISR for when TWDT times out). It can be redefined in user code to
* handle TWDT events.
*
* @note It has the same limitations as the interrupt function. Do not use ESP_LOGI functions inside.
*/
void __attribute__((weak)) esp_task_wdt_isr_user_handler(void)
{
}
/**
* @brief Idle hook callback
*
@ -118,23 +117,95 @@ static bool idle_hook_cb(void)
{
#if CONFIG_FREERTOS_SMP
esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
#else
#else // CONFIG_FREERTOS_SMP
esp_task_wdt_reset();
#endif
#endif // CONFIG_FREERTOS_SMP
return true;
}
// ----------------------- Helpers -------------------------
#if !TWDT_HARDWARE_IMPL
/**
* @brief Reset hardware timer and reset flags of each entry
* Private API provided by esp_timer component to feed a timer without
* the need of disabling it, removing it and inserting it manually.
*/
static void reset_hw_timer(void)
esp_err_t esp_timer_feed(esp_timer_handle_t timer);
#endif // !TWDT_HARDWARE_IMPL
static esp_err_t task_wdt_timer_stop(twdt_obj_t *obj)
{
esp_err_t ret = ESP_OK;
if (obj == NULL) {
return ESP_ERR_INVALID_STATE;
}
#if TWDT_HARDWARE_IMPL
// All tasks have reset; Feed the underlying timer.
wdt_hal_write_protect_disable(&obj->hal);
wdt_hal_disable(&obj->hal);
wdt_hal_write_protect_enable(&obj->hal);
#else // TWDT_HARDWARE_IMPL
if (obj->sw_timer == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
esp_timer_stop(obj->sw_timer);
}
#endif // TWDT_HARDWARE_IMPL
return ret;
}
static esp_err_t task_wdt_timer_restart(twdt_obj_t *obj)
{
esp_err_t ret = ESP_OK;
if (obj == NULL) {
return ESP_ERR_INVALID_STATE;
}
#if TWDT_HARDWARE_IMPL
// All tasks have reset; Feed the underlying timer.
wdt_hal_write_protect_disable(&obj->hal);
wdt_hal_enable(&obj->hal);
wdt_hal_feed(&obj->hal);
wdt_hal_write_protect_enable(&obj->hal);
#else // TWDT_HARDWARE_IMPL
if (obj->sw_timer == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
esp_timer_start_periodic(obj->sw_timer, obj->period_ms * 1000);
}
#endif // TWDT_HARDWARE_IMPL
return ret;
}
/**
* @brief Reset the timer and reset flags of each entry
* When entering this function, the spinlock has already been taken, no need to take it back.
*/
static void task_wdt_timer_feed(void)
{
#if TWDT_HARDWARE_IMPL
// All tasks have reset; time to reset the hardware timer.
wdt_hal_write_protect_disable(&p_twdt_obj->hal);
wdt_hal_feed(&p_twdt_obj->hal);
wdt_hal_write_protect_enable(&p_twdt_obj->hal);
#else // TWDT_HARDWARE_IMPL
/* No matter if feeding succeeded or not, we have to reset each list entry's flags.
* Thus, ignore the return value. */
esp_timer_feed(p_twdt_obj->sw_timer);
#endif // TWDT_HARDWARE_IMPL
//Clear the has_reset flag in each entry
twdt_entry_t *entry;
SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
@ -230,7 +301,7 @@ static esp_err_t add_entry(bool is_task, void *entry_data, twdt_entry_t **entry_
// Add entry to list
SLIST_INSERT_HEAD(&p_twdt_obj->entries_slist, entry, slist_entry);
if (all_reset) { //Reset hardware timer if all other tasks in list have reset in
reset_hw_timer();
task_wdt_timer_feed();
}
portEXIT_CRITICAL(&spinlock);
*entry_ret = entry;
@ -271,7 +342,7 @@ static esp_err_t delete_entry(bool is_task, void *entry_data)
SLIST_REMOVE(&p_twdt_obj->entries_slist, entry, twdt_entry, slist_entry);
// Reset hardware timer if all remaining tasks have reset
if (all_reset) {
reset_hw_timer();
task_wdt_timer_feed();
}
portEXIT_CRITICAL(&spinlock);
free(entry);
@ -297,12 +368,12 @@ static void unsubscribe_idle(uint32_t core_mask)
esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, core_num);
ESP_ERROR_CHECK(esp_task_wdt_delete_user(core_user_handles[core_num]));
core_user_handles[core_num] = NULL;
#else
#else // CONFIG_FREERTOS_SMP
TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCPU(core_num);
assert(idle_task_handle);
esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, core_num);
ESP_ERROR_CHECK(esp_task_wdt_delete(idle_task_handle));
#endif
#endif // CONFIG_FREERTOS_SMP
}
core_mask >>= 1;
core_num++;
@ -324,12 +395,12 @@ static void subscribe_idle(uint32_t core_mask)
snprintf(core_user_names[core_num], CORE_USER_NAME_LEN, "CPU %d", (uint8_t)core_num);
ESP_ERROR_CHECK(esp_task_wdt_add_user((const char *)core_user_names[core_num], &core_user_handles[core_num]));
ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
#else
#else // CONFIG_FREERTOS_SMP
TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCPU(core_num);
assert(idle_task_handle);
ESP_ERROR_CHECK(esp_task_wdt_add(idle_task_handle));
ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
#endif
#endif // CONFIG_FREERTOS_SMP
}
core_mask >>= 1;
core_num++;
@ -509,10 +580,13 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
static void task_wdt_isr(void *arg)
{
portENTER_CRITICAL_ISR(&spinlock);
#if TWDT_HARDWARE_IMPL
// Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
wdt_hal_write_protect_disable(&p_twdt_obj->hal);
wdt_hal_handle_intr(&p_twdt_obj->hal); // Feeds WDT and clears acknowledges interrupt
wdt_hal_write_protect_enable(&p_twdt_obj->hal);
#endif // TWDT_HARDWARE_IMPL
// If there are no entries, there's nothing to do.
if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
portEXIT_CRITICAL_ISR(&spinlock);
@ -578,52 +652,121 @@ static void task_wdt_isr(void *arg)
}
portEXIT_CRITICAL_ISR(&spinlock);
// Run user ISR handler
esp_task_wdt_isr_user_handler();
/* Run user ISR handler.
* This function has been declared as weak, thus, it may be possible that it was not defines.
* to check this, we can directly test its address. In any case, the linker will get rid of
* this `if` when linking, this means that if the function was not defined, the whole `if`
* block will be discarded (zero runtime overhead), else only the function call will be kept.
*/
if (esp_task_wdt_isr_user_handler != NULL) {
esp_task_wdt_isr_user_handler();
}
// Trigger configured timeout behavior (e.g., panic or print backtrace)
assert(cpus_fail != 0);
task_wdt_timeout_handling(cpus_fail, panic);
}
static esp_err_t task_wdt_timer_allocate(twdt_obj_t *obj, const esp_task_wdt_config_t *config)
{
#if TWDT_HARDWARE_IMPL
esp_err_t ret = esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &obj->intr_handle);
if (ret == ESP_OK) {
periph_module_enable(PERIPH_TIMG0_MODULE);
wdt_hal_init(&obj->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
// Assign the driver object
wdt_hal_write_protect_disable(&obj->hal);
// Configure 1st stage timeout and behavior
wdt_hal_config_stage(&obj->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
// Configure 2nd stage timeout and behavior
wdt_hal_config_stage(&obj->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
// Enable the WDT
wdt_hal_enable(&obj->hal);
wdt_hal_write_protect_enable(&obj->hal);
}
return ret;
#else // TWDT_HARDWARE_IMPL
const esp_timer_create_args_t timer_args = {
.callback = task_wdt_isr,
.arg = NULL,
.dispatch_method = ESP_TIMER_ISR,
.name = "Task software watchdog",
.skip_unhandled_events = true
};
/* Software Task timer. As we don't have a spare hardware watchdog timer, we will use esp_timer to simulate one. */
esp_err_t ret = esp_timer_create(&timer_args, &obj->sw_timer);
ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, reterr, TAG, "could not start periodic timer");
/* Configure it as a periodic timer, so that we check the Tasks everytime it is triggered.
* Its parameter is in microseconds, but the config's is in milliseconds, convert it. */
obj->period_ms = config->timeout_ms;
ret = esp_timer_start_periodic(obj->sw_timer, config->timeout_ms * 1000);
ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, freeret, TAG, "could not start periodic timer");
return ret;
freeret:
/* If we reach this point, it means that we were unable to program the timer as a periodic one, so
* no need to stop it before deleting it. */
esp_timer_delete(obj->sw_timer);
reterr:
return ret;
#endif // TWDT_HARDWARE_IMPL
}
static void task_wdt_timer_disable(twdt_obj_t *obj)
{
esp_err_t ret = ESP_OK;
ret = task_wdt_timer_stop(obj);
#if TWDT_HARDWARE_IMPL
// Stop hardware timer and the interrupt associated
wdt_hal_deinit(&obj->hal);
esp_intr_disable(obj->intr_handle);
#endif // TWDT_HARDWARE_IMPL
assert(ret == ESP_OK);
}
static void task_wdt_timer_free(twdt_obj_t *obj)
{
#if TWDT_HARDWARE_IMPL
ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle)); // Deregister interrupt
#else // TWDT_HARDWARE_IMPL
esp_timer_delete(obj->sw_timer);
#endif // TWDT_HARDWARE_IMPL
}
// ----------------------------------------------------- Public --------------------------------------------------------
esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
{
ESP_RETURN_ON_FALSE((config != NULL && config->idle_core_mask < (1 << portNUM_PROCESSORS)), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
ESP_RETURN_ON_FALSE(p_twdt_obj == NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT already initialized");
esp_err_t ret;
esp_err_t ret = ESP_OK;
twdt_obj_t *obj = NULL;
if (p_twdt_obj == NULL) {
// Allocate and initialize TWDT driver object
obj = calloc(1, sizeof(twdt_obj_t));
ESP_GOTO_ON_FALSE((obj != NULL), ESP_ERR_NO_MEM, err, TAG, "insufficient memory");
SLIST_INIT(&obj->entries_slist);
obj->panic = config->trigger_panic;
ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &obj->intr_handle));
portENTER_CRITICAL(&spinlock);
// Configure hardware timer
periph_module_enable(PERIPH_TIMG0_MODULE);
wdt_hal_init(&obj->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
// Assign the driver object
p_twdt_obj = obj;
portEXIT_CRITICAL(&spinlock);
uint32_t old_core_mask = 0;
// Allocate and initialize the global object
obj = calloc(1, sizeof(twdt_obj_t));
ESP_GOTO_ON_FALSE((obj != NULL), ESP_ERR_NO_MEM, err, TAG, "insufficient memory");
SLIST_INIT(&obj->entries_slist);
obj->panic = config->trigger_panic;
// Allocate the timer itself
ret = task_wdt_timer_allocate(obj, config);
if (ret != ESP_OK) {
goto err;
}
portENTER_CRITICAL(&spinlock);
wdt_hal_write_protect_disable(&p_twdt_obj->hal);
// Configure 1st stage timeout and behavior
wdt_hal_config_stage(&p_twdt_obj->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
// Configure 2nd stage timeout and behavior
wdt_hal_config_stage(&p_twdt_obj->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
// Enable the WDT
wdt_hal_enable(&p_twdt_obj->hal);
wdt_hal_write_protect_enable(&p_twdt_obj->hal);
// No error so far, we can assign it to the driver object
p_twdt_obj = obj;
// Update which core's idle tasks are subscribed
uint32_t old_core_mask = p_twdt_obj->idle_core_mask;
old_core_mask = p_twdt_obj->idle_core_mask;
p_twdt_obj->idle_core_mask = config->idle_core_mask;
portEXIT_CRITICAL(&spinlock);
if (old_core_mask) {
// Unsubscribe all previously watched core idle tasks
unsubscribe_idle(old_core_mask);
@ -633,40 +776,55 @@ esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
subscribe_idle(config->idle_core_mask);
}
ret = ESP_OK;
return ESP_OK;
err:
free(obj);
return ret;
}
esp_err_t esp_task_wdt_stop(void)
{
esp_err_t ret = ESP_OK;
portENTER_CRITICAL(&spinlock);
ret = task_wdt_timer_stop(p_twdt_obj);
portEXIT_CRITICAL(&spinlock);
return ret;
}
esp_err_t esp_task_wdt_restart(void)
{
esp_err_t ret = ESP_OK;
portENTER_CRITICAL(&spinlock);
ret = task_wdt_timer_restart(p_twdt_obj);
portEXIT_CRITICAL(&spinlock);
return ret;
}
esp_err_t esp_task_wdt_deinit(void)
{
esp_err_t ret;
ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
esp_err_t ret;
// Unsubscribe all previously watched core idle tasks
unsubscribe_idle(p_twdt_obj->idle_core_mask);
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE_ISR(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "Tasks/users still subscribed");
// Disable hardware timer and the interrupt
wdt_hal_write_protect_disable(&p_twdt_obj->hal);
wdt_hal_disable(&p_twdt_obj->hal);
wdt_hal_write_protect_enable(&p_twdt_obj->hal);
wdt_hal_deinit(&p_twdt_obj->hal);
esp_intr_disable(p_twdt_obj->intr_handle);
// Unassign driver object
twdt_obj_t *obj = p_twdt_obj;
p_twdt_obj = NULL;
portEXIT_CRITICAL(&spinlock);
// Disable the timer
task_wdt_timer_disable(p_twdt_obj);
// Free driver resources
ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle)); // Deregister interrupt
free(obj); // Free p_twdt_obj
task_wdt_timer_free(p_twdt_obj);
// Free the global object
free(p_twdt_obj);
p_twdt_obj = NULL;
return ESP_OK;
err:
portEXIT_CRITICAL(&spinlock);
subscribe_idle(p_twdt_obj->idle_core_mask); // Resubscribe idle tasks
return ret;
}
@ -713,7 +871,7 @@ esp_err_t esp_task_wdt_reset(void)
// Mark entry as reset and issue timer reset if all entries have been reset
entry->has_reset = true; // Reset the task if it's on the task list
if (all_reset) { // Reset if all other tasks in list have reset in
reset_hw_timer();
task_wdt_timer_feed();
}
ret = ESP_OK;
err:
@ -737,7 +895,7 @@ esp_err_t esp_task_wdt_reset_user(esp_task_wdt_user_handle_t user_handle)
// Mark entry as reset and issue timer reset if all entries have been reset
entry->has_reset = true; // Reset the task if it's on the task list
if (all_reset) { // Reset if all other tasks in list have reset in
reset_hw_timer();
task_wdt_timer_feed();
}
ret = ESP_OK;
err:

View File

@ -222,6 +222,7 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_INT_WDT after interrupt watchdog
do_int_wdt_hw,
check_reset_reason_int_wdt);
#if CONFIG_ESP_TASK_WDT
static void do_task_wdt(void)
{
setup_values();
@ -251,6 +252,7 @@ TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_TASK_WDT after task watchdog",
"[reset_reason][reset="RESET"]",
do_task_wdt,
check_reset_reason_task_wdt);
#endif // CONFIG_ESP_TASK_WDT
static void do_rtc_wdt(void)
{

View File

@ -4,6 +4,10 @@
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "sdkconfig.h"
#if CONFIG_ESP_TASK_WDT
#include <stdbool.h>
#include "unity.h"
#include "esp_rom_sys.h"
@ -19,8 +23,6 @@ void esp_task_wdt_isr_user_handler(void)
timeout_flag = true;
}
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
//IDF-5055
TEST_CASE("Task WDT task timeout", "[task_wdt]")
{
timeout_flag = false;
@ -37,7 +39,6 @@ TEST_CASE("Task WDT task timeout", "[task_wdt]")
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
TEST_CASE("Task WDT task feed", "[task_wdt]")
{
@ -58,8 +59,6 @@ TEST_CASE("Task WDT task feed", "[task_wdt]")
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
//IDF-5055
TEST_CASE("Task WDT user timeout", "[task_wdt]")
{
const char *user_name = "test_user";
@ -78,7 +77,6 @@ TEST_CASE("Task WDT user timeout", "[task_wdt]")
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete_user(user_handle));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)
TEST_CASE("Task WDT user feed", "[task_wdt]")
{
@ -100,3 +98,5 @@ TEST_CASE("Task WDT user feed", "[task_wdt]")
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete_user(user_handle));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
#endif // CONFIG_ESP_TASK_WDT

View File

@ -143,6 +143,49 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
return ESP_OK;
}
/**
* Function to feed a timer. It is not part of the public header
* file on purpose as it shall only be used by the Task WDT component.
*/
esp_err_t esp_timer_feed(esp_timer_handle_t timer)
{
esp_err_t ret = ESP_OK;
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!is_initialized() || !timer_armed(timer) || timer->period == 0 ) {
return ESP_ERR_INVALID_STATE;
}
esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
timer_list_lock(dispatch_method);
const int64_t now = esp_timer_impl_get_time();
const uint64_t period = timer->period;
/* Currently we are guaranteed that the remaining delay between now and the timer's
* alarm is less than the period, so the following won't cause the alarm to be
* triggered earlier than before feeding occurs. */
const uint64_t alarm = now + period;
/* We need to remove the timer to the list of timers and reinsert it at
* the right position. In fact, the timers are sorted by their alarm value
* (earliest first) */
ret = timer_remove(timer);
if (ret == ESP_OK) {
/* Remove function got rid of the alarm and period fields, restore them */
timer->alarm = alarm;
timer->period = period;
ret = timer_insert(timer, false);
}
timer_list_unlock(dispatch_method);
return ret;
}
esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
{
if (timer == NULL) {

View File

@ -286,6 +286,7 @@ inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr)
inline bool esp_core_dump_in_isr_context(void)
{
#if CONFIG_ESP_TASK_WDT
/* This function will be used to check whether a panic occurred in an ISR.
* In that case, the execution frame must be switch to the interrupt stack.
* However, in case where the task watchdog ISR calls the panic handler,
@ -295,6 +296,9 @@ inline bool esp_core_dump_in_isr_context(void)
* TODO: IDF-5694. */
extern bool g_twdt_isr;
return xPortInterruptedFromISRContext() && !g_twdt_isr;
#else // CONFIG_ESP_TASK_WDT
return xPortInterruptedFromISRContext();
#endif // CONFIG_ESP_TASK_WDT
}
inline core_dump_task_handle_t esp_core_dump_get_current_task_handle()

View File

@ -252,7 +252,7 @@ static void main_task(void *args)
#endif
//Initialize task wdt if configured to do so
#if CONFIG_ESP_TASK_WDT
#if CONFIG_ESP_TASK_WDT_INIT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
.idle_core_mask = 0,
@ -267,7 +267,7 @@ static void main_task(void *args)
twdt_config.idle_core_mask |= (1 << 1);
#endif
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
#endif // CONFIG_ESP_TASK_WDT
#endif // CONFIG_ESP_TASK_WDT_INIT
app_main();
vTaskDelete(NULL);
@ -275,7 +275,7 @@ static void main_task(void *args)
void esp_startup_start_app_common(void)
{
#if CONFIG_ESP_INT_WDT
#if CONFIG_ESP_INT_WDT_INIT
esp_int_wdt_init();
//Initialize the interrupt watch dog for CPU0.
esp_int_wdt_cpu_init();

View File

@ -331,7 +331,7 @@ static void main_task(void *args)
#endif
//Initialize TWDT if configured to do so
#if CONFIG_ESP_TASK_WDT
#if CONFIG_ESP_TASK_WDT_INIT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
.idle_core_mask = 0,

View File

@ -111,7 +111,7 @@ static void main_task(void* args)
#endif
//Initialize TWDT if configured to do so
#if CONFIG_ESP_TASK_WDT
#if CONFIG_ESP_TASK_WDT_INIT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000,
.idle_core_mask = 0,

View File

@ -570,7 +570,7 @@ TEST_CASE("mbedtls RSA Generate Key", "[mbedtls][timeout=60]")
const unsigned int key_size = 2048;
const int exponent = 65537;
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT && CONFIG_ESP_TASK_WDT && !CONFIG_ESP_TASK_WDT_INIT
/* Check that generating keys doesnt starve the watchdog if interrupt-based driver is used */
esp_task_wdt_config_t twdt_config = {
.timeout_ms = 1000,
@ -578,7 +578,7 @@ TEST_CASE("mbedtls RSA Generate Key", "[mbedtls][timeout=60]")
.trigger_panic = true,
};
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
#endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT
#endif // CONFIG_MBEDTLS_MPI_USE_INTERRUPT && CONFIG_ESP_TASK_WDT && !CONFIG_ESP_TASK_WDT_INIT
mbedtls_rsa_init(&ctx);
mbedtls_ctr_drbg_init(&ctr_drbg);
@ -592,9 +592,9 @@ TEST_CASE("mbedtls RSA Generate Key", "[mbedtls][timeout=60]")
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT
#if CONFIG_MBEDTLS_MPI_USE_INTERRUPT && CONFIG_ESP_TASK_WDT && !CONFIG_ESP_TASK_WDT_INIT
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
#endif //CONFIG_MBEDTLS_MPI_USE_INTERRUPT
#endif // CONFIG_MBEDTLS_MPI_USE_INTERRUPT && CONFIG_ESP_TASK_WDT && !CONFIG_ESP_TASK_WDT_INIT
}

View File

@ -121,10 +121,10 @@ _vector_table:
.option push
.option norvc
j _panic_handler /* exception handler, entry 0 */
.rept (ETS_T1_WDT_INUM - 1)
.rept (ETS_INT_WDT_INUM - 1)
j _interrupt_handler /* 24 identical entries, all pointing to the interrupt handler */
.endr
j _panic_handler /* Call panic handler for ETS_T1_WDT_INUM interrupt (soc-level panic)*/
j _panic_handler /* Call panic handler for ETS_INT_WDT_INUM interrupt (soc-level panic)*/
j _panic_handler /* Call panic handler for ETS_CACHEERR_INUM interrupt (soc-level panic)*/
#ifdef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
j _panic_handler /* Call panic handler for ETS_MEMPROT_ERR_INUM interrupt (soc-level panic)*/

View File

@ -403,3 +403,6 @@
//Invalid interrupt for number interrupt matrix
#define ETS_INVALID_INUM 6
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM)

View File

@ -211,7 +211,7 @@
//CPU0 Interrupt number reserved in riscv/vector.S, not touch this.
#define ETS_T1_WDT_INUM 24 // Remove TODO: IDF-4246
#define ETS_T0_WDT_INUM 24
#define ETS_CACHEERR_INUM 25
#define ETS_MEMPROT_ERR_INUM 26
#define ETS_DPORT_INUM 28
@ -234,3 +234,6 @@
//Interrupt medium level, used for INT WDT for example
#define SOC_INTERRUPT_LEVEL_MEDIUM 4
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T0_WDT_INUM)

View File

@ -173,11 +173,7 @@ typedef union {
*/
typedef union {
struct {
uint32_t reserved_0:12;
/** wdt_appcpu_reset_en : R/W; bitpos: [12]; default: 0;
* WDT reset CPU enable.
*/
uint32_t wdt_appcpu_reset_en:1;
uint32_t reserved_0:13;
/** wdt_procpu_reset_en : R/W; bitpos: [13]; default: 0;
* WDT reset CPU enable.
*/
@ -239,7 +235,7 @@ typedef union {
uint32_t wdt_divcnt_rst:1;
uint32_t reserved_1:15;
/** wdt_clk_prescale : R/W; bitpos: [31:16]; default: 1;
* MWDT clock prescaler value. MWDT clock period = 12.5 ns *
* MWDT clock prescaler value. MWDT clock period = 25 ns *
* TIMG_WDT_CLK_PRESCALE.
*/
uint32_t wdt_clk_prescale:16;

View File

@ -235,3 +235,6 @@
//Interrupt medium level, used for INT WDT for example
#define SOC_INTERRUPT_LEVEL_MEDIUM 4
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM)

View File

@ -235,3 +235,6 @@
//Interrupt medium level, used for INT WDT for example
#define SOC_INTERRUPT_LEVEL_MEDIUM 4
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM)

View File

@ -265,3 +265,6 @@
//Invalid interrupt for number interrupt matrix
#define ETS_INVALID_INUM 6
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM)

View File

@ -287,3 +287,6 @@
//Invalid interrupt for number interrupt matrix
#define ETS_INVALID_INUM 6
// Interrupt number for the Interrupt watchdog
#define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM)

View File

@ -3,5 +3,4 @@ api-guides/esp-ble-mesh/ble-mesh-feature-list
api-guides/esp-ble-mesh/ble-mesh-terminology
api-guides/esp-ble-mesh/ble-mesh-architecture
api-guides/esp-ble-mesh/ble-mesh-faq
api-reference/system/wdts
api-reference/bluetooth/esp-ble-mesh

View File

@ -19,13 +19,15 @@ The various watchdog timers can be enabled using the :ref:`project-configuration
Interrupt Watchdog Timer (IWDT)
-------------------------------
{IDF_IWDT_TIMER_GROUP:default="Timer Group 1", esp32c2="Timer Group 0"}
The purpose of the IWDT is to ensure that interrupt service routines (ISRs) are not blocked from running for a prolonged period of time (i.e., the IWDT timeout period). Blocking ISRs from running in a timely manner is undesirable as it can increases ISR latency, and also prevents task switching (as task switching is executed form an ISR). The things that can block ISRs from running include:
- Disabling interrupts
- Critical Sections (also disables interrupts)
- Other same/higher priority ISRs (will block same/lower priority ISRs from running it completes execution)
The IWDT utilizes the watchdog timer in Timer Group 1 as its underlying hardware timer and leverages the FreeRTOS tick interrupt on each CPU to feed the watchdog timer. If the tick interrupt on a particular CPU is not run at within the IWDT timeout period, it is indicative that something is blocking ISRs from being run on that CPU (see the list of reasons above).
The IWDT utilizes the watchdog timer in {IDF_IWDT_TIMER_GROUP} as its underlying hardware timer and leverages the FreeRTOS tick interrupt on each CPU to feed the watchdog timer. If the tick interrupt on a particular CPU is not run at within the IWDT timeout period, it is indicative that something is blocking ISRs from being run on that CPU (see the list of reasons above).
When the IWDT times out, the default action is to invoke the panic handler and display the panic reason as ``Interrupt wdt timeout on CPU0`` or ``Interrupt wdt timeout on CPU1`` (as applicable). Depending on the panic handler's configured behavior (see :ref:`CONFIG_ESP_SYSTEM_PANIC`), users can then debug the source of the IWDT timeout (via the backtrace, OpenOCD, gdbstub etc) or simply reset the chip (which may be preferred in a production environment).
@ -59,7 +61,13 @@ Task Watchdog Timer (TWDT)
The Task Watchdog Timer (TWDT) is used to monitor particular tasks, ensuring that they are able to execute within a given timeout period. The TWDT primarily watches the {IDF_TARGET_IDLE_TASKS}, however any task can subscribe to be watched by the TWDT. By watching the {IDF_TARGET_IDLE_TASKS}, the TWDT can detect instances of tasks running for a prolonged period of time wihtout yielding. This can be an indicator of poorly written code that spinloops on a peripheral, or a task that is stuck in an infinite loop.
The TWDT is built around the Hardware Watchdog Timer in Timer Group 0. When a timeout occurs, an interrupt is triggered. Users can redefine the function `esp_task_wdt_isr_user_handler` in the user code, in order to receive the timeout event and handle it differently.
.. only:: not esp32c2
The TWDT is built around the Hardware Watchdog Timer in Timer Group 0. When a timeout occurs, an interrupt is triggered. Users can define the function `esp_task_wdt_isr_user_handler` in the user code, in order to receive the timeout event and extend the default behavior.
.. only:: esp32c2
The {IDF_TARGET_NAME} has only a single Timer Group, used by Interrupt Watchdog (IWDT). Thus, the Task Watchdog is built around the `esp_timer` component in order to implement a software timer. When a timeout occurs, an interrupt is triggered, notifying the `esp_timer`'s main task. The later will then execute the TWDT callback previously registered. Users can define the function `esp_task_wdt_isr_user_handler` in the user code, in order to receive the timeout event and extend the default behavior.
Usage
^^^^^
@ -93,13 +101,14 @@ The default timeout period for the TWDT is set using config item :ref:`CONFIG_ES
For more information, you can refer to :doc:`SPI Flash <../storage/spi_flash>`.
The following config options control TWDT configuration at startup. They are all enabled by default:
The following config options control TWDT configuration. They are all enabled by default:
{IDF_TARGET_IDLE_TASK:default="Idle task", esp32="CPU0 Idle task", esp32s3="CPU0 Idle task"}
.. list::
- :ref:`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 :cpp:func:`esp_task_wdt_init`.
- :ref:`CONFIG_ESP_TASK_WDT` - enables TWDT feature. If this option is disabled, TWDT cannot be used, even if initialized at runtime.
- :ref:`CONFIG_ESP_TASK_WDT_INIT` - 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 :cpp:func:`esp_task_wdt_init`.
- :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0` - {IDF_TARGET_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 :cpp:func:`esp_task_wdt_init` again.
:not CONFIG_FREERTOS_UNICORE: - :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1` - CPU1 Idle task is subscribed to the TWDT during startup.

View File

@ -115,6 +115,8 @@ Task Watchdog Timers
- Configuration is now passed as a configuration structure.
- The function will now handle subscribing of the idle tasks if configured to do so.
- The former :ref:`CONFIG_ESP_TASK_WDT` configuration option has been renamed to :ref:`CONFIG_ESP_TASK_WDT_INIT` and a new :ref:`CONFIG_ESP_TASK_WDT` option has been introduced.
FreeRTOS
--------

View File

@ -71,7 +71,7 @@ void task_func(void *arg)
void app_main(void)
{
#if !CONFIG_ESP_TASK_WDT
#if !CONFIG_ESP_TASK_WDT_INIT
// If the TWDT was not initialized automatically on startup, manually intialize it now
esp_task_wdt_config_t twdt_config = {
.timeout_ms = TWDT_TIMEOUT_MS,
@ -80,7 +80,7 @@ void app_main(void)
};
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
printf("TWDT initialized\n");
#endif // CONFIG_ESP_TASK_WDT
#endif // CONFIG_ESP_TASK_WDT_INIT
// Create a task
run_loop = true;
@ -94,10 +94,10 @@ void app_main(void)
run_loop = false;
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
#if !CONFIG_ESP_TASK_WDT
#if !CONFIG_ESP_TASK_WDT_INIT
// If we manually initialized the TWDT, deintialize it now
ESP_ERROR_CHECK(esp_task_wdt_deinit());
printf("TWDT deinitialized\n");
#endif // CONFIG_ESP_TASK_WDT
#endif // CONFIG_ESP_TASK_WDT_INIT
printf("Example complete\n");
}

View File

@ -612,7 +612,6 @@ components/esp_system/include/esp_freertos_hooks.h
components/esp_system/include/esp_int_wdt.h
components/esp_system/include/esp_private/dbg_stubs.h
components/esp_system/include/esp_private/panic_internal.h
components/esp_system/include/esp_private/system_internal.h
components/esp_system/include/esp_task.h
components/esp_system/port/arch/riscv/expression_with_stack.c
components/esp_system/port/arch/xtensa/expression_with_stack.c

View File

@ -11,7 +11,7 @@ CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_SPI_FLASH_ENABLE_COUNTERS=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_INIT=n
CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS=y
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y