Merge branch 'feature/add_int_task_wdt_esp32c2_v5.0' into 'release/v5.0'

WDT: implement interrupt wdt and task wdt for ESP32-C2 (backport v5.0)

See merge request espressif/esp-idf!20980
This commit is contained in:
Zim Kalinowski 2022-12-05 16:38:35 +08:00
commit 759f7ec13d
46 changed files with 1099 additions and 206 deletions

View File

@ -980,7 +980,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"
@ -658,6 +659,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);
@ -828,6 +836,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

@ -32,16 +32,24 @@ 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)
task_wdt:esp_task_wdt_reset_user (noflash)
if ESP_TASK_WDT_USE_ESP_TIMER = y:
task_wdt_impl_esp_timer:esp_task_wdt_impl_timer_feed (noflash)
else:
task_wdt_impl_timergroup:esp_task_wdt_impl_timer_feed (noflash)
[mapping:esp_timer_pm]
archive: libesp_timer.a
entries:
if PM_SLP_IRAM_OPT = y:
# esp_timer_restart is called from task_wdt_timer_feed, so put it
# in IRAM if task_wdt_timer_feed itself is in IRAM.
if ESP_TASK_WDT_USE_ESP_TIMER = y:
esp_timer:esp_timer_restart (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,20 @@ 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_EN)
list(APPEND srcs "task_wdt/task_wdt.c")
if(CONFIG_ESP_TASK_WDT_USE_ESP_TIMER)
list(APPEND srcs "task_wdt/task_wdt_impl_esp_timer.c")
else()
list(APPEND srcs "task_wdt/task_wdt_impl_timergroup.c")
endif()
endif()
if(CONFIG_ESP_SYSTEM_USE_EH_FRAME)
list(APPEND srcs "eh_frame_parser.c")
endif()

View File

@ -367,7 +367,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,
@ -391,19 +390,36 @@ menu "ESP System Settings"
help
Also detect if interrupts on CPU 1 are disabled for too long.
config ESP_TASK_WDT
bool "Initialize Task Watchdog Timer on startup"
config ESP_TASK_WDT_EN
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_EN
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_EN
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
@ -412,7 +428,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
@ -421,7 +437,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
@ -432,10 +448,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_EN
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_EN
#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_EN
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_EN
#endif

View File

@ -61,6 +61,7 @@ void esp_crosscore_int_send_gdb_call(int core_id);
*/
void esp_crosscore_int_send_print_backtrace(int core_id);
#if CONFIG_ESP_TASK_WDT_EN
/**
* Send an interrupt to a CPU indicating it call `task_wdt_timeout_abort_xtensa`.
* This will make the CPU abort, using the interrupted task frame.
@ -72,7 +73,9 @@ void esp_crosscore_int_send_print_backtrace(int core_id);
* @param core_id Core that should abort
*/
void esp_crosscore_int_send_twdt_abort(int core_id);
#endif
#endif // CONFIG_ESP_TASK_WDT_EN
#endif // !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32C2
#ifdef __cplusplus
}

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,57 @@
/*
* 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_EN
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type used to define the context of a Task WatchDog Timer implementation.
* This is used internally in the TWDT driver, it is implementation specific.
*/
typedef void* twdt_ctx_t;
/**
* @brief Type of the function used as an ISR callback.
*/
typedef void (*twdt_isr_callback)(void*);
/**
* @brief Stop the Task Watchdog Timer (TWDT)
*
* This function will temporarily stop the timer until it is restarted 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 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_EN

View File

@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "../esp_task_wdt.h"
#include "esp_private/esp_task_wdt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Allocate and initialize the Task Watchdog Timer (TWDT) with the given configuration.
*
* @param[in] config Pointer to the configuration structure
* @param[out] obj Abstract context for the current timer, this will be passed to all the other functions
*
* @return
* - ESP_OK: Successfully initialized and configured the timer
* - Other: Failed to initialize the timer
*/
esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
twdt_isr_callback callback,
twdt_ctx_t *obj);
/**
* @brief Reconfigure a timer.
*
* The timer must be stopped when calling this function. The timer will not be restarted at the end of this
* function.
*
* @param[in] config Pointer to the configuration structure
*
* @return
* - ESP_OK: Successfully reconfigured the timer
* - Other: Failed to reconfigure the timer
*/
esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config);
/**
* @brief Free the Task Watchdog Timer (TWDT).
*
* @param[in] obj Abstract implementation context
*
*/
void esp_task_wdt_impl_timer_free(twdt_ctx_t obj);
/**
* @brief Feed the Task Watchdog Timer (TWDT)
*
* Feed the timer underneath to prevent it from triggering for the next period (configured at initialization).
*
* @param[in] obj Abstract implementation context
* @return
* - ESP_OK: timer successfully feeded
* - Other: failed to feed the timer
*/
esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj);
/**
* @brief Function invoked as soon as the Task Watchdog Timer (TWDT) ISR callback is called.
*
* @param[in] obj Abstract implementation context
*/
void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj);
/**
* @brief Stop the Task Watchdog Timer (TWDT).
*
* @param[in] obj Abstract implementation context
*
*/
esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj);
/**
* @brief Restart the Task Watchdog Timer (TWDT)
*
* This function will restart/resume the timer after it has been stopped.
*
* @param[in] obj Abstract implementation context
* @return
* - ESP_OK: timer successfully stopped
* - Other: failed to stop the timer
*/
esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj);
#ifdef __cplusplus
}
#endif

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
@ -19,12 +11,28 @@ extern "C" {
#endif
#include "esp_system.h"
#include "soc/soc_caps.h"
#if SOC_TIMER_GROUPS >= 2
/* 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 a 40MHz clock for the
* Timer Group 0. Let's adapt the prescaler value accordingly.
*/
#define MWDT0_TICK_PRESCALER 20000
#define MWDT0_TICKS_PER_US 500
#endif
/**
* @brief Internal function to restart PRO and APP CPUs.
*

View File

@ -32,18 +32,36 @@ typedef struct esp_task_wdt_user_handle_s * esp_task_wdt_user_handle_t;
/**
* @brief 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 current configuration. This funciton will also subscribe the idle tasks if
* This function configures and initializes the TWDT. This function will 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().
* This function won't start the timer if no task have been registered yet.
*
* @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
* - ESP_ERR_INVALID_STATE: Already initialized
* - Other: Failed to initialize TWDT
*/
esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
/**
* @brief Reconfigure the Task Watchdog Timer (TWDT)
*
* The function reconfigures the running TWDT. It must already be initialized when this function is called.
*
* @note esp_task_wdt_reconfigure() must not be called by multiple tasks simultaneously.
*
* @param[in] config Configuration structure
*
* @return
* - ESP_OK: Reconfiguring was successful
* - ESP_ERR_INVALID_STATE: TWDT not initialized yet
* - Other: Failed to initialize TWDT
*/
esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config);
/**
* @brief Deinitialize the Task Watchdog Timer (TWDT)
*
@ -51,6 +69,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 +169,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
@ -287,7 +288,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

@ -26,6 +26,7 @@ CONFIG_TASK_WDT_PANIC CONFIG_ESP_TASK_WDT_PANI
CONFIG_TASK_WDT_TIMEOUT_S CONFIG_ESP_TASK_WDT_TIMEOUT_S
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
CONFIG_ESP_TASK_WDT CONFIG_ESP_TASK_WDT_INIT
CONFIG_ESP32_DEBUG_STUBS_ENABLE CONFIG_ESP_DEBUG_STUBS_ENABLE
CONFIG_BROWNOUT_DET CONFIG_ESP_BROWNOUT_DET

View File

@ -11,24 +11,24 @@
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/wdt_hal.h"
#include "freertos/task_snapshot.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "esp_debug_helpers.h"
#include "esp_freertos_hooks.h"
#include "esp_task_wdt.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/system_internal.h"
#include "esp_private/crosscore_int.h"
#include "freertos/task_snapshot.h"
#include "esp_private/esp_task_wdt.h"
#include "esp_private/esp_task_wdt_impl.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);
@ -38,6 +38,9 @@ extern void panic_print_registers(const void *frame, int core);
* a different context than the one it's called from. */
extern void xt_unhandled_exception(void *frame);
/* Forward declaration of the idle hook callback */
static bool idle_hook_cb(void);
/* Global flag set to make the `panic` mechanism think a real `abort()` was
* called. This is used in the ISR handler, in case we have to panic when
* a task doesn't feed its timer. */
@ -48,13 +51,6 @@ bool g_twdt_isr = false;
// --------------------------------------------------- Definitions -----------------------------------------------------
// ----------------------- Macros --------------------------
// HAL related variables and constants
#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
// ---------------------- Typedefs -------------------------
/**
@ -71,11 +67,11 @@ struct twdt_entry {
// Structure used to hold run time configuration of the TWDT
typedef struct twdt_obj twdt_obj_t;
struct twdt_obj {
wdt_hal_context_t hal;
twdt_ctx_t impl_ctx;
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;
bool waiting_for_task; // Flag to start the timer as soon as a task is added
};
// ----------------------- Objects -------------------------
@ -92,50 +88,17 @@ static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
// ----------------------------------------------------- Private -------------------------------------------------------
// ---------------------- 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
*
* Idle hook callback called by the idle tasks to feed the TWDT
*
* @return Whether the idle tasks should continue idling
*/
static bool idle_hook_cb(void)
{
#if CONFIG_FREERTOS_SMP
esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
#else
esp_task_wdt_reset();
#endif
return true;
}
// ----------------------- Helpers -------------------------
/**
* @brief Reset hardware timer and reset flags of each entry
* @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 reset_hw_timer(void)
static void task_wdt_timer_feed(void)
{
// 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);
//Clear the has_reset flag in each entry
esp_task_wdt_impl_timer_feed(p_twdt_obj->impl_ctx);
/* Clear the has_reset flag in each entry */
twdt_entry_t *entry;
SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
entry->has_reset = false;
@ -229,8 +192,13 @@ 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);
// Start the timer if it has not been started yet and was waiting on a task to registered
if (p_twdt_obj->waiting_for_task) {
esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
p_twdt_obj->waiting_for_task = false;
}
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;
@ -269,9 +237,16 @@ static esp_err_t delete_entry(bool is_task, void *entry_data)
}
// Remove entry
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();
/* Stop the timer if we don't have any more tasks/objects to watch */
if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
p_twdt_obj->waiting_for_task = true;
esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
} else {
p_twdt_obj->waiting_for_task = false;
}
/* Reset hardware timer if all remaining tasks have reset and if the list of tasks is not empty */
if (!p_twdt_obj->waiting_for_task && all_reset) {
task_wdt_timer_feed();
}
portEXIT_CRITICAL(&spinlock);
free(entry);
@ -297,12 +272,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 +299,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++;
@ -498,6 +473,25 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
#endif // CONFIG_IDF_TARGET_ARCH_RISCV
// ---------------------- Callbacks ------------------------
/**
* @brief Idle hook callback
*
* Idle hook callback called by the idle tasks to feed the TWDT
*
* @return Whether the idle tasks should continue idling
*/
static bool idle_hook_cb(void)
{
#if CONFIG_FREERTOS_SMP
esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
#else // CONFIG_FREERTOS_SMP
esp_task_wdt_reset();
#endif // CONFIG_FREERTOS_SMP
return true;
}
/**
* @brief TWDT timeout ISR function
*
@ -509,10 +503,8 @@ static void task_wdt_timeout_handling(int cores_fail, bool panic)
static void task_wdt_isr(void *arg)
{
portENTER_CRITICAL_ISR(&spinlock);
// 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);
esp_task_wdt_impl_timeout_triggered(p_twdt_obj->impl_ctx);
// If there are no entries, there's nothing to do.
if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
portEXIT_CRITICAL_ISR(&spinlock);
@ -578,8 +570,15 @@ 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);
@ -592,81 +591,153 @@ 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);
/* 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, NOT STARTED */
ret = esp_task_wdt_impl_timer_allocate(config, task_wdt_isr, &obj->impl_ctx);
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);
// Update which core's idle tasks are subscribed
uint32_t old_core_mask = p_twdt_obj->idle_core_mask;
/* No error so far, we can assign it to the driver object */
p_twdt_obj = obj;
/* Update which core's idle tasks are subscribed */
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);
}
if (config->idle_core_mask) {
// Subscribe the new cores idle tasks
/* Subscribe the new cores idle tasks */
subscribe_idle(config->idle_core_mask);
}
ret = ESP_OK;
/* Start the timer only if we are watching some tasks */
if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
p_twdt_obj->waiting_for_task = false;
esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
} else {
p_twdt_obj->waiting_for_task = true;
}
return ESP_OK;
err:
free(obj);
return ret;
}
esp_err_t esp_task_wdt_reconfigure(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 not initialized yet");
uint32_t old_core_mask = 0;
esp_err_t ret = ESP_OK;
/* Stop the timer to make sure we don't get into the ISR while reconfiguring the TWDT */
portENTER_CRITICAL(&spinlock);
ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
if (ret != ESP_OK) {
goto err;
}
/* We can start reconfiguring the tasks */
p_twdt_obj->panic = config->trigger_panic;
/* Reconfigure the timer underneath (without restarting it) */
ret = esp_task_wdt_impl_timer_reconfigure(p_twdt_obj->impl_ctx, config);
if (ret != ESP_OK) {
goto err;
}
old_core_mask = p_twdt_obj->idle_core_mask;
/* If the new mask is different than the old one, we have to subscribe the new idle tasks */
if (old_core_mask != config->idle_core_mask) {
p_twdt_obj->idle_core_mask = config->idle_core_mask;
/* Unsubscribe all previously watched core idle tasks */
unsubscribe_idle(old_core_mask);
if (config->idle_core_mask) {
/* Subscribe the new cores idle tasks */
subscribe_idle(config->idle_core_mask);
}
}
/* Start the timer only if we are watching some tasks */
if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
}
portEXIT_CRITICAL(&spinlock);
err:
return ESP_OK;
}
esp_err_t esp_task_wdt_stop(void)
{
esp_err_t ret = ESP_OK;
/* If the timer has not been initialized, do not attempt to stop it */
if (p_twdt_obj == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
portENTER_CRITICAL(&spinlock);
ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
portEXIT_CRITICAL(&spinlock);
}
return ret;
}
esp_err_t esp_task_wdt_restart(void)
{
esp_err_t ret = ESP_OK;
/* If the timer has not been initialized, do not attempt to stop it */
if (p_twdt_obj == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
portENTER_CRITICAL(&spinlock);
ret = esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
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
esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
// Free driver resources
ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle)); // Deregister interrupt
free(obj); // Free p_twdt_obj
esp_task_wdt_impl_timer_free(p_twdt_obj->impl_ctx);
// 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 +784,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 +808,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

@ -0,0 +1,150 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "hal/wdt_hal.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_debug_helpers.h"
#include "esp_timer.h"
#include "esp_private/esp_task_wdt_impl.h"
/**
* Context for the software implementation of the Task WatchDog Timer.
* This will be passed as a parameter to public functions below. */
typedef struct {
esp_timer_handle_t sw_timer;
uint32_t period_ms;
} twdt_ctx_soft_t;
/**
* Declare the initial context as static. It will be passed to the
* task_wdt implementation as the implementation context in the
* init function. */
static twdt_ctx_soft_t init_context;
static const char *TAG = "task_wdt_impl_soft";
esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
twdt_isr_callback callback,
twdt_ctx_t *obj)
{
twdt_ctx_soft_t *ctx = &init_context;
const esp_timer_create_args_t timer_args = {
.callback = callback,
.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, &ctx->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.
* No need to start the timer here, it will be started later with `esp_task_wdt_impl_timer_restart` */
ctx->period_ms = config->timeout_ms;
/* Return our context to the caller */
*obj = (twdt_ctx_t) ctx;
reterr:
return ret;
}
esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
{
esp_err_t ret = ESP_OK;
twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
if (config == NULL || ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
/* The timer is stopped, we only need to update the period in our context, next time we start the
* timer with `esp_task_wdt_impl_timer_restart`, we will pass the context's period to the
* underlying esp_timer instance. */
ctx->period_ms = config->timeout_ms;
}
return ret;
}
void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
{
const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
if (ctx != NULL && ctx->sw_timer != NULL) {
ESP_ERROR_CHECK(esp_timer_delete(ctx->sw_timer));
}
}
esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
if (ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
/* Feed the periodic timer by restarting it, specifying the same period */
ret = esp_timer_restart(ctx->sw_timer, ctx->period_ms * 1000);
}
return ret;
}
void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
{
(void) obj;
}
esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
if (ctx == NULL || ctx->sw_timer == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
ret = esp_timer_stop(ctx->sw_timer);
}
return ret;
}
esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
if (ctx == NULL || ctx->sw_timer == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
esp_timer_start_periodic(ctx->sw_timer, ctx->period_ms * 1000);
}
return ret;
}

View File

@ -0,0 +1,184 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "hal/wdt_hal.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_private/system_internal.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/esp_task_wdt_impl.h"
#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
#define TWDT_PERIPH_MODULE PERIPH_TIMG0_MODULE
#define TWDT_INTR_SOURCE ETS_TG0_WDT_LEVEL_INTR_SOURCE
/**
* Context for the software implementation of the Task WatchDog Timer.
* This will be passed as a parameter to public functions below. */
typedef struct {
wdt_hal_context_t hal;
intr_handle_t intr_handle;
} twdt_ctx_hard_t;
/**
* Declare the initial context as static. It will be passed to the
* task_wdt implementation as the implementation context in the
* init function. */
static twdt_ctx_hard_t init_context;
esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
twdt_isr_callback callback,
twdt_ctx_t *obj)
{
esp_err_t ret = ESP_OK;
twdt_ctx_hard_t *ctx = &init_context;
if (config == NULL || obj == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
esp_intr_alloc(TWDT_INTR_SOURCE, 0, callback, NULL, &ctx->intr_handle);
}
if (ret == ESP_OK) {
periph_module_enable(TWDT_PERIPH_MODULE);
wdt_hal_init(&ctx->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
wdt_hal_write_protect_disable(&ctx->hal);
// Configure 1st stage timeout and behavior
wdt_hal_config_stage(&ctx->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(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
// No need to enable to enable the WDT here, it will be enabled with `esp_task_wdt_impl_timer_restart`
wdt_hal_write_protect_enable(&ctx->hal);
/* Return the implementation context to the caller */
*obj = (twdt_ctx_t) ctx;
}
return ret;
}
esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
{
esp_err_t ret = ESP_OK;
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (config == NULL || ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
wdt_hal_write_protect_disable(&ctx->hal);
/* Reconfigure the 1st and 2nd stage timeout */
wdt_hal_config_stage(&ctx->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
wdt_hal_config_stage(&ctx->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
wdt_hal_write_protect_enable(&ctx->hal);
}
return ret;
}
void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
{
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (ctx != NULL) {
/* Stop hardware timer and the interrupt associated */
wdt_hal_deinit(&ctx->hal);
ESP_ERROR_CHECK(esp_intr_disable(ctx->intr_handle));
/* Disable the Timer Group module */
periph_module_enable(TWDT_PERIPH_MODULE);
/* Deregister interrupt */
ESP_ERROR_CHECK(esp_intr_free(ctx->intr_handle));
}
}
esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
wdt_hal_write_protect_disable(&ctx->hal);
wdt_hal_feed(&ctx->hal);
wdt_hal_write_protect_enable(&ctx->hal);
}
return ret;
}
void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
{
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (ctx != NULL) {
/* Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset) */
wdt_hal_write_protect_disable(&ctx->hal);
wdt_hal_handle_intr(&ctx->hal); // Feeds WDT and clears acknowledges interrupt
wdt_hal_write_protect_enable(&ctx->hal);
}
}
esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
wdt_hal_write_protect_disable(&ctx->hal);
wdt_hal_disable(&ctx->hal);
wdt_hal_write_protect_enable(&ctx->hal);
}
return ret;
}
esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
{
esp_err_t ret = ESP_OK;
twdt_ctx_hard_t* ctx = (twdt_ctx_hard_t*) obj;
if (ctx == NULL) {
ret = ESP_ERR_INVALID_STATE;
}
if (ret == ESP_OK) {
wdt_hal_write_protect_disable(&ctx->hal);
wdt_hal_enable(&ctx->hal);
wdt_hal_feed(&ctx->hal);
wdt_hal_write_protect_enable(&ctx->hal);
}
return ret;
}

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_EN
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_EN
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_EN
#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,60 @@ 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 inactive when no task to watch", "[task_wdt]")
{
/* Make sure a timeout is NOT trigger when we have no task to watch */
timeout_flag = false;
esp_task_wdt_config_t twdt_config = {
.timeout_ms = TASK_WDT_TIMEOUT_MS,
.idle_core_mask = 0,
.trigger_panic = false,
};
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
esp_rom_delay_us(2 * TASK_WDT_TIMEOUT_MS * 1000);
TEST_ASSERT_EQUAL(false, timeout_flag);
/* Add a task to watch, it should start the watchdog */
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
esp_rom_delay_us(TASK_WDT_TIMEOUT_MS * 1000);
TEST_ASSERT_EQUAL(true, timeout_flag);
/* Remove the task we just addded and make sure the WDT is stopped*/
timeout_flag = false;
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
esp_rom_delay_us(2 * TASK_WDT_TIMEOUT_MS * 1000);
TEST_ASSERT_EQUAL(false, timeout_flag);
/* Success, terminate the test */
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
TEST_CASE("Task WDT can be reconfigured", "[task_wdt]")
{
/* Make sure a timeout is NOT trigger when we have no task to watch */
timeout_flag = false;
esp_task_wdt_config_t twdt_config = {
.timeout_ms = TASK_WDT_TIMEOUT_MS / 2,
.idle_core_mask = 0,
.trigger_panic = false,
};
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_init(&twdt_config));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_add(NULL));
/* Timer started, check that a timeout is raised after a while */
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
TEST_ASSERT_EQUAL(true, timeout_flag);
/* Reconfigure the timer with a bigger timeout. The timer is restarted
* after reconfiguring it. */
twdt_config.timeout_ms = TASK_WDT_TIMEOUT_MS;
timeout_flag = false;
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_reconfigure(&twdt_config));
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
TEST_ASSERT_EQUAL(false, timeout_flag);
/* Should be triggered now, we've spent TASK_WDT_TIMEOUT_MS waiting */
esp_rom_delay_us((TASK_WDT_TIMEOUT_MS / 2 + 1) * 1000);
TEST_ASSERT_EQUAL(true, timeout_flag);
/* Success, terminate the test */
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_delete(NULL));
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
}
TEST_CASE("Task WDT task feed", "[task_wdt]")
{
@ -58,8 +113,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 +131,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 +152,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_EN

View File

@ -164,6 +164,22 @@ esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
*/
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period);
/**
* @brief Restart a currently running timer
*
* If the given timer is a one-shot timer, the timer is restarted immediately and will timeout once in `timeout_us` microseconds.
* If the given timer is a periodic timer, the timer is restarted immediately with a new period of `timeout_us` microseconds.
*
* @param timer timer Handle created using esp_timer_create
* @param timeout_us Timeout, in microseconds relative to the current time.
* In case of a periodic timer, also represents the new period.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the handle is invalid
* - ESP_ERR_INVALID_STATE if the timer is not running
*/
esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us);
/**
* @brief Stop the timer
*

View File

@ -141,6 +141,51 @@ esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
return ESP_OK;
}
esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us)
{
esp_err_t ret = ESP_OK;
if (timer == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!is_initialized() || !timer_armed(timer)) {
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;
/* 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) {
/* Two cases here:
* - if the alarm was a periodic one, i.e. `period` is not 0, the given timeout_us becomes the new period
* - if the alarm was a one-shot one, i.e. `period` is 0, it remains non-periodic. */
if (period != 0) {
/* Remove function got rid of the alarm and period fields, restore them */
const uint64_t new_period = MAX(timeout_us, esp_timer_impl_get_min_period_us());
timer->alarm = now + new_period;
timer->period = new_period;
} else {
/* The new one-shot alarm shall be triggered timeout_us after the current time */
timer->alarm = now + timeout_us;
timer->period = 0;
}
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

@ -866,6 +866,89 @@ TEST_CASE("Test a latency between a call of callback and real event", "[esp_time
TEST_ESP_OK(esp_timer_delete(periodic_timer));
}
static void test_timer_triggered(void* timer1_trig)
{
int* timer = (int *)timer1_trig;
*timer = *timer + 1;
}
TEST_CASE("periodic esp_timer can be restarted", "[esp_timer]")
{
const int delay_ms = 100;
int timer_trig = 0;
esp_timer_handle_t timer1;
esp_timer_create_args_t create_args = {
.callback = &test_timer_triggered,
.arg = &timer_trig,
.name = "timer1",
};
TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000));
/* Sleep for delay_ms/2 and restart the timer */
vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
/* Check that the alarm was not triggered */
TEST_ASSERT_EQUAL(0, timer_trig);
/* Reaching this point, the timer will be triggered in delay_ms/2.
* Let's restart the timer now with the same period. */
TEST_ESP_OK(esp_timer_restart(timer1, delay_ms * 1000));
/* Sleep for a bit more than delay_ms/2 */
vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
/* If the alarm was triggered, restart didn't work */
TEST_ASSERT_EQUAL(0, timer_trig);
/* Else, wait for another delay_ms/2, which should trigger the alarm */
vTaskDelay(((delay_ms / 2) + 2) * portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL(1, timer_trig);
/* Now wait for another delay_ms to make sure the timer is still periodic */
timer_trig = 0;
vTaskDelay((delay_ms * portTICK_PERIOD_MS) + 1);
/* Make sure the timer was triggered */
TEST_ASSERT_EQUAL(1, timer_trig);
/* Reduce the period of the timer to delay/2 */
timer_trig = 0;
TEST_ESP_OK(esp_timer_restart(timer1, delay_ms / 2 * 1000));
vTaskDelay((delay_ms * portTICK_PERIOD_MS) + 1);
/* Check that the alarm was triggered twice */
TEST_ASSERT_EQUAL(2, timer_trig);
TEST_ESP_OK( esp_timer_stop(timer1) );
TEST_ESP_OK( esp_timer_delete(timer1) );
}
TEST_CASE("one-shot esp_timer can be restarted", "[esp_timer]")
{
const int delay_ms = 100;
int timer_trig = 0;
esp_timer_handle_t timer1;
esp_timer_create_args_t create_args = {
.callback = &test_timer_triggered,
.arg = &timer_trig,
.name = "timer1",
};
TEST_ESP_OK(esp_timer_create(&create_args, &timer1));
TEST_ESP_OK(esp_timer_start_once(timer1, delay_ms * 1000));
vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
/* Check that the alarm was not triggered */
TEST_ASSERT_EQUAL(0, timer_trig);
/* Reaching this point, the timer will be triggered in delay_ms/2.
* Let's restart the timer now with the same timeout. */
TEST_ESP_OK(esp_timer_restart(timer1, delay_ms * 1000));
vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
/* If the alarm was triggered, restart didn't work */
TEST_ASSERT_EQUAL(0, timer_trig);
/* Else, wait for another delay_ms/2, which should trigger the alarm */
vTaskDelay(((delay_ms / 2) + 2) * portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL(1, timer_trig);
/* Make sure the timer is NOT periodic, wait for another delay and make sure
* our callback was not called */
timer_trig = 0;
vTaskDelay(delay_ms * 2 * portTICK_PERIOD_MS);
/* Make sure the timer was triggered */
TEST_ASSERT_EQUAL(0, timer_trig);
TEST_ESP_OK( esp_timer_delete(timer1) );
}
#ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
static int64_t old_time[2];

View File

@ -294,6 +294,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_EN
/* 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,
@ -303,6 +304,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_EN
return xPortInterruptedFromISRContext();
#endif // CONFIG_ESP_TASK_WDT_EN
}
inline core_dump_task_handle_t esp_core_dump_get_current_task_handle()

View File

@ -254,7 +254,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,
@ -269,7 +269,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);
@ -277,7 +277,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

@ -333,7 +333,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_EN && !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_EN && !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_EN && !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_EN && !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

@ -286,3 +286,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

@ -1 +0,0 @@
api-reference/system/wdts

View File

@ -19,13 +19,15 @@ The various watchdog timers can be enabled using the :ref:`project-configuration
Interrupt Watchdog Timer (IWDT)
-------------------------------
{IDF_TARGET_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_TARGET_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_EN` - 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 ``CONFIG_ESP_TASK_WDT`` configuration option has been renamed to :ref:`CONFIG_ESP_TASK_WDT_INIT` and a new :ref:`CONFIG_ESP_TASK_WDT_EN` option has been introduced.
FreeRTOS
--------

View File

@ -14,7 +14,7 @@ def test_flash_suspend_example(dut: Dut) -> None:
dut.expect_exact('found partition')
res = dut.expect(r'During Erase, ISR callback function\(in flash\) response time:\s+(\d+(\.\d{1,2})) us')
response_time = res.group(1).decode('utf8')
assert 0 <= float(response_time) < 40
assert 0 <= float(response_time) < 120
res = dut.expect(r'During Erase, ISR callback function\(in iram\) response time:\s+(\d+(\.\d{1,2})) us')
response_time = res.group(1).decode('utf8')

View File

@ -21,7 +21,7 @@ Before project configuration and build, be sure to set the correct chip target u
### Configure the project
Program should run correctly without needing any special configuration. However, users can disable `CONFIG_ESP_TASK_WDT` which will prevent the TWDT from being automatically initialized on startup. If disabled, the example will manually initialize the TWDT.
Program should run correctly without needing any special configuration. However, users can disable `CONFIG_ESP_TASK_WDT_INIT` which will prevent the TWDT from being automatically initialized on startup. If disabled, the example will manually initialize the TWDT.
### Build and Flash

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

@ -631,7 +631,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/port/arch/riscv/expression_with_stack.c
components/esp_system/port/arch/xtensa/expression_with_stack.c
components/esp_system/port/public_compat/brownout.h

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