mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
TWDT: refactor the TWDT to be driver agnostic
This refactoring brings a private API for the TWDT implementation, which can now use a hardware timer (Timer Group) or a software timer (esp_timer)
This commit is contained in:
parent
53c7dd4efc
commit
30d12af191
@ -38,6 +38,10 @@ entries:
|
||||
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
|
||||
@ -45,7 +49,8 @@ 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_TASK_WDT_USE_ESP_TIMER = y:
|
||||
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)
|
||||
|
@ -26,7 +26,13 @@ else()
|
||||
"debug_stubs.c")
|
||||
|
||||
if(CONFIG_ESP_TASK_WDT)
|
||||
list(APPEND srcs "task_wdt.c")
|
||||
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)
|
||||
|
@ -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
|
||||
/**
|
||||
* 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
|
||||
#endif // !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32C2
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -15,6 +15,17 @@
|
||||
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)
|
||||
*
|
||||
|
101
components/esp_system/include/esp_private/esp_task_wdt_impl.h
Normal file
101
components/esp_system/include/esp_private/esp_task_wdt_impl.h
Normal 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
|
@ -11,8 +11,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if !CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
|
||||
#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.
|
||||
@ -24,8 +25,9 @@ extern "C" {
|
||||
|
||||
#else
|
||||
|
||||
/* The targets that have a single timer group use XTAL clock as the
|
||||
* default clock. XTAL clock frequency is 40MHz. */
|
||||
/* 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
|
||||
|
||||
|
@ -32,19 +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. 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)
|
||||
*
|
||||
|
@ -11,20 +11,18 @@
|
||||
#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_timer.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"
|
||||
@ -40,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. */
|
||||
@ -50,18 +51,6 @@ bool g_twdt_isr = false;
|
||||
|
||||
// --------------------------------------------------- Definitions -----------------------------------------------------
|
||||
|
||||
// ----------------------- Macros --------------------------
|
||||
|
||||
// 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 -------------------------
|
||||
|
||||
/**
|
||||
@ -78,16 +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 {
|
||||
#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
|
||||
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
|
||||
bool waiting_for_task; // Flag to start the timer as soon as a task is added
|
||||
};
|
||||
|
||||
// ----------------------- Objects -------------------------
|
||||
@ -104,109 +88,17 @@ static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
|
||||
|
||||
// ----------------------------------------------------- Private -------------------------------------------------------
|
||||
|
||||
// ---------------------- 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;
|
||||
}
|
||||
|
||||
// ----------------------- Helpers -------------------------
|
||||
|
||||
#if !TWDT_HARDWARE_IMPL
|
||||
|
||||
/**
|
||||
* Private API provided by esp_timer component to feed a timer without
|
||||
* the need of disabling it, removing it and inserting it manually.
|
||||
*/
|
||||
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
|
||||
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;
|
||||
@ -300,6 +192,11 @@ 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
|
||||
task_wdt_timer_feed();
|
||||
}
|
||||
@ -340,8 +237,15 @@ 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) {
|
||||
/* 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);
|
||||
@ -569,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
|
||||
*
|
||||
@ -580,12 +503,7 @@ 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
|
||||
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)) {
|
||||
@ -667,78 +585,6 @@ static void task_wdt_isr(void *arg)
|
||||
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)
|
||||
@ -747,56 +593,123 @@ esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
|
||||
ESP_RETURN_ON_FALSE(p_twdt_obj == NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT already initialized");
|
||||
esp_err_t ret = ESP_OK;
|
||||
twdt_obj_t *obj = NULL;
|
||||
uint32_t old_core_mask = 0;
|
||||
|
||||
// Allocate and initialize the global object
|
||||
/* 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);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
// No error so far, we can assign it to the driver object
|
||||
/* No error so far, we can assign it to the driver object */
|
||||
p_twdt_obj = obj;
|
||||
|
||||
// Update which core's idle tasks are subscribed
|
||||
old_core_mask = p_twdt_obj->idle_core_mask;
|
||||
/* Update which core's idle tasks are subscribed */
|
||||
p_twdt_obj->idle_core_mask = config->idle_core_mask;
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
portENTER_CRITICAL(&spinlock);
|
||||
ret = task_wdt_timer_stop(p_twdt_obj);
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
|
||||
/* 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;
|
||||
portENTER_CRITICAL(&spinlock);
|
||||
ret = task_wdt_timer_restart(p_twdt_obj);
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -813,10 +726,10 @@ esp_err_t esp_task_wdt_deinit(void)
|
||||
ESP_GOTO_ON_FALSE_ISR(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "Tasks/users still subscribed");
|
||||
|
||||
// Disable the timer
|
||||
task_wdt_timer_disable(p_twdt_obj);
|
||||
esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
|
||||
|
||||
// Free driver resources
|
||||
task_wdt_timer_free(p_twdt_obj);
|
||||
esp_task_wdt_impl_timer_free(p_twdt_obj->impl_ctx);
|
||||
|
||||
// Free the global object
|
||||
free(p_twdt_obj);
|
155
components/esp_system/task_wdt/task_wdt_impl_esp_timer.c
Normal file
155
components/esp_system/task_wdt/task_wdt_impl_esp_timer.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* Private API provided by esp_timer component to feed a timer without
|
||||
* the need of disabling it, removing it and inserting it manually.
|
||||
*/
|
||||
esp_err_t esp_timer_feed(esp_timer_handle_t timer);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
ret = esp_timer_feed(ctx->sw_timer);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
184
components/esp_system/task_wdt/task_wdt_impl_timergroup.c
Normal file
184
components/esp_system/task_wdt/task_wdt_impl_timergroup.c
Normal 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;
|
||||
}
|
@ -40,6 +40,60 @@ TEST_CASE("Task WDT task timeout", "[task_wdt]")
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_task_wdt_deinit());
|
||||
}
|
||||
|
||||
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]")
|
||||
{
|
||||
timeout_flag = false;
|
||||
|
@ -866,6 +866,49 @@ 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_periodic_timer_feed(void* timer1_fed)
|
||||
{
|
||||
*((int*) timer1_fed) = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed function is not part of the esp_timer header file: it's a public in the sense that it is not static,
|
||||
* but it is only meant to be used in IDF components.
|
||||
*/
|
||||
esp_err_t esp_timer_feed(esp_timer_handle_t timer);
|
||||
|
||||
TEST_CASE("periodic esp_timer can be fed", "[esp_timer]")
|
||||
{
|
||||
const int delay_ms = 100;
|
||||
int timer_fed = 0;
|
||||
esp_timer_handle_t timer1;
|
||||
esp_timer_create_args_t create_args = {
|
||||
.callback = &test_periodic_timer_feed,
|
||||
.arg = &timer_fed,
|
||||
.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 feed the timer */
|
||||
vTaskDelay((delay_ms / 2) * portTICK_PERIOD_MS);
|
||||
/* Check that the alarm was not triggered */
|
||||
TEST_ASSERT_EQUAL(0, timer_fed);
|
||||
/* Reaching this point, the timer will be triggered in delay_ms/2.
|
||||
* Let's feed the timer now. */
|
||||
TEST_ESP_OK(esp_timer_feed(timer1));
|
||||
/* Sleep for a bit more than delay_ms/2 */
|
||||
vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
|
||||
/* If the alarm was triggered, feed didn't work */
|
||||
TEST_ASSERT_EQUAL(0, timer_fed);
|
||||
/* Else, wait for another delay_ms/2, which should trigger the alarm */
|
||||
vTaskDelay(((delay_ms / 2) + 1) * portTICK_PERIOD_MS);
|
||||
TEST_ASSERT_EQUAL(1, timer_fed);
|
||||
|
||||
TEST_ESP_OK( esp_timer_stop(timer1) );
|
||||
TEST_ESP_OK( esp_timer_delete(timer1) );
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
static int64_t old_time[2];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user