esp_system: Refactor task_wdt

This commit refactors the task watchdog as follows:

- Renamed variables, types, and functions
- Replaced manual linked list implementation with SLIST()
- Moved calloc()/free() calls out of critical sections
- Shortened ISR critical sections
- Updated API description
- Updated code formatting
pull/8990/head
Darian Leung 2022-04-14 18:08:08 +08:00
rodzic 4bd5c4ef53
commit 4877a9fcba
3 zmienionych plików z 332 dodań i 361 usunięć

Wyświetl plik

@ -1,16 +1,8 @@
// Copyright 2015-2016 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: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -23,125 +15,98 @@ extern "C" {
#endif
/**
* @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 timeout period and panic configurations instead. After initializing
* the TWDT, any task can elect to be watched by the TWDT by subscribing to it
* using esp_task_wdt_add().
*
* @param[in] timeout Timeout period of TWDT in seconds
* @param[in] panic Flag that controls whether the panic handler will be
* executed when the TWDT times out
*
* @return
* - ESP_OK: Initialization was successful
* - ESP_ERR_NO_MEM: Initialization failed due to lack of memory
*
* @note esp_task_wdt_init() must only be called after the scheduler
* started
*/
* @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 timeout period and panic configurations instead. After initializing the TWDT,
* any task can elect to be watched by the TWDT by subscribing to it using esp_task_wdt_add().
*
* @note esp_task_wdt_init() must only be called after the scheduler started
* @param[in] timeout Timeout period of TWDT in seconds
* @param[in] panic Flag that controls whether the panic handler will be executed when the TWDT times out
* @return
* - ESP_OK: Initialization was successful
* - ESP_ERR_NO_MEM: Initialization failed due insufficient memory
*/
esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic);
/**
* @brief Deinitialize the Task Watchdog Timer (TWDT)
*
* This function will deinitialize the TWDT. Calling this function whilst tasks
* are still subscribed to the TWDT, or when the TWDT is already deinitialized,
* will result in an error code being returned.
* This function will deinitialize the TWDT. Calling this function whilst tasks are still subscribed to the TWDT, or
* when the TWDT is already deinitialized, will result in an error code being returned.
*
* @return
* - ESP_OK: TWDT successfully deinitialized
* - ESP_ERR_INVALID_STATE: Error, tasks are still subscribed to the TWDT
* - ESP_ERR_NOT_FOUND: Error, TWDT has already been deinitialized
* - ESP_ERR_INVALID_STATE: TWDT was never initialized, or tasks are still subscribed
*/
esp_err_t esp_task_wdt_deinit(void);
/**
* @brief Subscribe a task to the Task Watchdog Timer (TWDT)
*
* This function subscribes a task to the TWDT. Each subscribed task must
* periodically call esp_task_wdt_reset() to prevent the TWDT from elapsing its
* timeout period. Failure to do so will result in a TWDT timeout. If the task
* being subscribed is one of the Idle Tasks, this function will automatically
* enable esp_task_wdt_reset() to called from the Idle Hook of the Idle Task.
* Calling this function whilst the TWDT is uninitialized or attempting to
* subscribe an already subscribed task will result in an error code being
* returned.
*
* @param[in] handle Handle of the task. Input NULL to subscribe the current
* running task to the TWDT
*
* @return
* - ESP_OK: Successfully subscribed the task to the TWDT
* - ESP_ERR_INVALID_ARG: Error, the task is already subscribed
* - ESP_ERR_NO_MEM: Error, could not subscribe the task due to lack of
* memory
* - ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
*/
* @brief Subscribe a task to the Task Watchdog Timer (TWDT)
*
* This function subscribes a task to the TWDT. Each subscribed task must periodically call esp_task_wdt_reset() to
* prevent the TWDT from elapsing its timeout period. Failure to do so will result in a TWDT timeout. If the task being
* subscribed is one of the Idle Tasks, this function will automatically enable esp_task_wdt_reset() to called from the
* Idle Hook of the Idle Task.
*
* Calling this function whilst the TWDT is uninitialized or attempting to subscribe an already subscribed task will
* result in an error code being returned.
*
* @param handle Handle of the task. Input NULL to subscribe the current running task to the TWDT
* @return
* - ESP_OK: Successfully subscribed the task to the TWDT
* - ESP_ERR_INVALID_ARG: The task is already subscribed
* - ESP_ERR_NO_MEM: Could not subscribe the insufficient memory
* - ESP_ERR_INVALID_STATE: TWDT was never initialized
*/
esp_err_t esp_task_wdt_add(TaskHandle_t handle);
/**
* @brief Reset the Task Watchdog Timer (TWDT) on behalf of the currently
* running task
*
* This function will reset the TWDT on behalf of the currently running task.
* Each subscribed task must periodically call this function to prevent the
* TWDT from timing out. If one or more subscribed tasks fail to reset the
* TWDT on their own behalf, a TWDT timeout will occur. If the IDLE tasks have
* been subscribed to the TWDT, they will automatically call this function from
* their idle hooks. Calling this function from a task that has not subscribed
* to the TWDT, or when the TWDT is uninitialized will result in an error code
* being returned.
*
* @return
* - ESP_OK: Successfully reset the TWDT on behalf of the currently
* running task
* - ESP_ERR_NOT_FOUND: Error, the current running task has not subscribed
* to the TWDT
* - ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
*/
* @brief Reset the Task Watchdog Timer (TWDT) on behalf of the currently running task
*
* This function will reset the TWDT on behalf of the currently running task. Each subscribed task must periodically
* call this function to prevent the TWDT from timing out. If one or more subscribed tasks fail to reset the TWDT on
* their own behalf, a TWDT timeout will occur. If the IDLE tasks have been subscribed to the TWDT, they will
* automatically call this function from their idle hooks. Calling this function from a task that has not subscribed to
* the TWDT, or when the TWDT is uninitialized will result in an error code being returned.
*
* @return
* - ESP_OK: Successfully reset the TWDT on behalf of the currently running task
* - ESP_ERR_NOT_FOUND: The task is not subscribed
* - ESP_ERR_INVALID_STATE: TWDT was never initialized
*/
esp_err_t esp_task_wdt_reset(void);
/**
* @brief Unsubscribes a task from the Task Watchdog Timer (TWDT)
*
* This function will unsubscribe a task from the TWDT. After being
* unsubscribed, the task should no longer call esp_task_wdt_reset(). If the
* task is an IDLE task, this function will automatically disable the calling
* of esp_task_wdt_reset() from the Idle Hook. Calling this function whilst the
* TWDT is uninitialized or attempting to unsubscribe an already unsubscribed
* task from the TWDT will result in an error code being returned.
*
* @param[in] handle Handle of the task. Input NULL to unsubscribe the
* current running task.
*
* @return
* - ESP_OK: Successfully unsubscribed the task from the TWDT
* - ESP_ERR_INVALID_ARG: Error, the task is already unsubscribed
* - ESP_ERR_INVALID_STATE: Error, the TWDT has not been initialized yet
*/
* @brief Unsubscribes a task from the Task Watchdog Timer (TWDT)
*
* This function will unsubscribe a task from the TWDT. After being unsubscribed, the task should no longer call
* esp_task_wdt_reset(). If the task is an IDLE task, this function will automatically disable the calling of
* esp_task_wdt_reset() from the Idle Hook. Calling this function whilst the TWDT is uninitialized or attempting to
* unsubscribe an already unsubscribed task from the TWDT will result in an error code being returned.
*
* @param[in] handle Handle of the task. Input NULL to unsubscribe the current running task.
* @return
* - ESP_OK: Successfully unsubscribed the task from the TWDT
* - ESP_ERR_NOT_FOUND: The task is not subscribed
* - ESP_ERR_INVALID_STATE: TWDT was never initialized
*/
esp_err_t esp_task_wdt_delete(TaskHandle_t handle);
/**
* @brief Query whether a task is subscribed to the Task Watchdog Timer (TWDT)
*
* This function will query whether a task is currently subscribed to the TWDT,
* or whether the TWDT is initialized.
*
* @param[in] handle Handle of the task. Input NULL to query the current
* running task.
*
* @return:
* - ESP_OK: The task is currently subscribed to the TWDT
* - ESP_ERR_NOT_FOUND: The task is currently not subscribed to the TWDT
* - ESP_ERR_INVALID_STATE: The TWDT is not initialized, therefore no tasks
* can be subscribed
*/
* @brief Query whether a task is subscribed to the Task Watchdog Timer (TWDT)
*
* This function will query whether a task is currently subscribed to the TWDT, or whether the TWDT is initialized.
*
* @param[in] handle Handle of the task. Input NULL to query the current running task.
* @return:
* - ESP_OK: The task is currently subscribed to the TWDT
* - ESP_ERR_NOT_FOUND: The task is not subscribed
* - ESP_ERR_INVALID_STATE: TWDT was never initialized
*/
esp_err_t esp_task_wdt_status(TaskHandle_t handle);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -14,8 +14,10 @@
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include <sys/queue.h>
#include <esp_types.h>
#include "esp_err.h"
#include "esp_check.h"
#include "esp_intr_alloc.h"
#include "esp_attr.h"
#include "esp_debug_helpers.h"
@ -29,50 +31,90 @@
#include "hal/timer_types.h"
#include "hal/wdt_hal.h"
// --------------------------------------------------- Definitions -----------------------------------------------------
static const char *TAG = "task_wdt";
// ----------------------- Macros --------------------------
//Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret'
#define ASSERT_EXIT_CRIT_RETURN(cond, ret) ({ \
if(!(cond)){ \
portEXIT_CRITICAL(&twdt_spinlock); \
return ret; \
} \
})
//Empty define used in ASSERT_EXIT_CRIT_RETURN macro when returning in void
#define VOID_RETURN
//HAL related variables and constants
// 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
static wdt_hal_context_t twdt_context;
#define TWDT_PRESCALER MWDT0_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
//Structure used for each subscribed task
typedef struct twdt_task_t twdt_task_t;
struct twdt_task_t {
// ---------------------- Typedefs -------------------------
// Structure used for each subscribed task
typedef struct twdt_entry twdt_entry_t;
struct twdt_entry {
SLIST_ENTRY(twdt_entry) slist_entry;
TaskHandle_t task_handle;
bool has_reset;
twdt_task_t *next;
};
//Structure used to hold run time configuration of the TWDT
typedef struct twdt_config_t twdt_config_t;
struct twdt_config_t {
twdt_task_t *list; //Linked list of subscribed tasks
uint32_t timeout; //Timeout period of TWDT
bool panic; //Flag to trigger panic when TWDT times out
// 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;
SLIST_HEAD(entry_list_head, twdt_entry) entries_slist;
bool panic; // Flag to trigger panic when TWDT times out
intr_handle_t intr_handle;
};
static twdt_config_t *twdt_config = NULL;
static portMUX_TYPE twdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
// ----------------------- Objects -------------------------
/*
* Idle hook callback for Idle Tasks to reset the TWDT. This callback will only
* be registered to the Idle Hook of a particular core when the corresponding
* Idle Task subscribes to the TWDT.
static const char *TAG = "task_wdt";
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
static twdt_obj_t *p_twdt_obj = NULL;
// ----------------------------------------------------- Private -------------------------------------------------------
/**
* @brief Find an entry from its task handle, and checks if all other entries have been reset
*
* @param[in] handle Task handle
* @param[out] all_reset Whether all entries have been reset
* @return Entry, or NULL if not found
*/
static twdt_entry_t *find_entry_from_handle_and_check_all_reset(TaskHandle_t handle, bool *all_reset)
{
twdt_entry_t *target = NULL;
bool found_non_reset = false;
twdt_entry_t *entry;
SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
if (entry->task_handle == handle) {
target = entry;
} else if (entry->has_reset == false) {
found_non_reset = true;
}
}
*all_reset = !found_non_reset;
return target;
}
/**
* @brief Reset hardware timer and entry flags
*/
static void reset_hw_timer(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);
//C lear 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;
}
}
/**
* @brief Idle hook callback
*
* Idle hook callback for Idle Tasks to reset the TWDT. This callback will only be registered to the Idle Hook of a
* particular core when the corresponding Idle Task subscribes to the TWDT.
*
* @return Always returns true
*/
static bool idle_hook_cb(void)
{
@ -80,316 +122,281 @@ static bool idle_hook_cb(void)
return true;
}
/*
* Internal function that looks for the target task in the TWDT task list.
* Returns the list item if found and returns null if not found. Also checks if
* all the other tasks have reset. Should be called within critical.
*/
static twdt_task_t *find_task_in_twdt_list(TaskHandle_t handle, bool *all_reset)
{
twdt_task_t *target = NULL;
*all_reset = true;
for(twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
if(task->task_handle == handle){
target = task; //Get pointer to target task list member
}else{
if(task->has_reset == false){ //If a task has yet to reset
*all_reset = false;
}
}
}
return target;
}
/*
* Resets the hardware timer and has_reset flags of each task on the list.
* Called within critical
*/
static void reset_hw_timer(void)
{
//All tasks have reset; time to reset the hardware timer.
wdt_hal_write_protect_disable(&twdt_context);
wdt_hal_feed(&twdt_context);
wdt_hal_write_protect_enable(&twdt_context);
//Clear all has_reset flags in list
for (twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
task->has_reset=false;
}
}
/*
* 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.
/**
* @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)
{
}
/*
* ISR for when TWDT times out. Checks for which tasks have not reset. Also
* triggers panic if configured to do so
/**
* @brief TWDT timeout ISR function
*
* Tee ISR checks which entries have not been reset, prints some debugging information, and triggers a panic if
* configured to do so.
*
* @param arg ISR argument
*/
static void task_wdt_isr(void *arg)
{
portENTER_CRITICAL_ISR(&twdt_spinlock);
twdt_task_t *twdttask;
const char *cpu;
//Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
wdt_hal_write_protect_disable(&twdt_context);
wdt_hal_handle_intr(&twdt_context); //Feeds WDT and clears acknowledges interrupt
wdt_hal_write_protect_enable(&twdt_context);
//We are taking a spinlock while doing I/O (ESP_EARLY_LOGE) here. Normally, that is a pretty
//bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case,
//something bad already happened and reporting this is considered more important
//than the badness caused by a spinlock here.
//Return immediately if no tasks have been added to task list
ASSERT_EXIT_CRIT_RETURN((twdt_config->list != NULL), VOID_RETURN);
//Watchdog got triggered because at least one task did not reset in time.
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);
// If there are no entries, there's nothing to do.
if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
portEXIT_CRITICAL_ISR(&spinlock);
return;
}
// Find what entries triggered the TWDT timeout (i.e., which entries have not been reset)
/*
Note: We are currently in a critical section, thus under normal circumstances, logging should not be allowed.
However, TWDT timeouts count as fatal errors, thus reporting the fatal error is considered more important than
minimizing interrupt latency. Thus we allow logging in critical sections in this narrow case.
*/
ESP_EARLY_LOGE(TAG, "Task watchdog got triggered. The following tasks did not reset the watchdog in time:");
for (twdttask=twdt_config->list; twdttask!=NULL; twdttask=twdttask->next) {
if (!twdttask->has_reset) {
cpu=xTaskGetAffinity(twdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
if (xTaskGetAffinity(twdttask->task_handle)==tskNO_AFFINITY) {
cpu=DRAM_STR("CPU 0/1");
twdt_entry_t *entry;
SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
if (!entry->has_reset) {
BaseType_t task_affinity = xTaskGetAffinity(entry->task_handle);
const char *cpu;
if (task_affinity == 0) {
cpu = DRAM_STR("CPU 0");
} else if (task_affinity == 1) {
cpu = DRAM_STR("CPU 1");
} else {
cpu = DRAM_STR("CPU 0/1");
}
ESP_EARLY_LOGE(TAG, " - %s (%s)", pcTaskGetName(twdttask->task_handle), cpu);
ESP_EARLY_LOGE(TAG, " - %s (%s)", pcTaskGetName(entry->task_handle), cpu);
}
}
ESP_EARLY_LOGE(TAG, "%s", DRAM_STR("Tasks currently running:"));
for (int x=0; x<portNUM_PROCESSORS; x++) {
for (int x = 0; x < portNUM_PROCESSORS; x++) {
ESP_EARLY_LOGE(TAG, "CPU %d: %s", x, pcTaskGetName(xTaskGetCurrentTaskHandleForCPU(x)));
}
portEXIT_CRITICAL_ISR(&spinlock);
// Run user ISR handler
esp_task_wdt_isr_user_handler();
if (twdt_config->panic){ //Trigger Panic if configured to do so
// Trigger configured timeout behavior (e.g., panic or print backtrace)
if (p_twdt_obj->panic) {
ESP_EARLY_LOGE(TAG, "Aborting.");
portEXIT_CRITICAL_ISR(&twdt_spinlock);
esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
abort();
} else {
} else { // Print
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32C2 // TODO: ESP32-C3 IDF-2986
int current_core = xPortGetCoreID();
//Print backtrace of current core
// Print backtrace of current core
ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
esp_backtrace_print(100);
#if !CONFIG_FREERTOS_UNICORE
//Print backtrace of other core
// Print backtrace of other core
ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", !current_core);
esp_crosscore_int_send_print_backtrace(!current_core);
#endif
#endif
}
portEXIT_CRITICAL_ISR(&twdt_spinlock);
}
/*
* Initializes the TWDT by allocating memory for the config data
* structure, obtaining the idle task handles/registering idle hooks, and
* setting the hardware timer registers. If reconfiguring, it will just modify
* wdt_config and reset the hardware timer.
*/
// ----------------------------------------------------- Public --------------------------------------------------------
esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
{
portENTER_CRITICAL(&twdt_spinlock);
if(twdt_config == NULL){ //TWDT not initialized yet
//Allocate memory for wdt_config
twdt_config = calloc(1, sizeof(twdt_config_t));
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NO_MEM);
esp_err_t ret;
twdt_config->list = NULL;
twdt_config->timeout = timeout;
twdt_config->panic = panic;
//Register Interrupt and ISR
ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &twdt_config->intr_handle));
//Configure hardware timer
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 = 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(&twdt_context, TWDT_INSTANCE, TWDT_PRESCALER, true);
wdt_hal_write_protect_disable(&twdt_context);
//Configure 1st stage timeout and behavior
wdt_hal_config_stage(&twdt_context, WDT_STAGE0, twdt_config->timeout * (1000000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
//Configure 2nd stage timeout and behavior
wdt_hal_config_stage(&twdt_context, WDT_STAGE1, twdt_config->timeout * (2 * 1000000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
//Enable the WDT
wdt_hal_enable(&twdt_context);
wdt_hal_write_protect_enable(&twdt_context);
} else { //twdt_config previously initialized
//Reconfigure task wdt
twdt_config->panic = panic;
twdt_config->timeout = timeout;
//Reconfigure hardware timer
wdt_hal_write_protect_disable(&twdt_context);
wdt_hal_disable(&twdt_context);
wdt_hal_config_stage(&twdt_context, WDT_STAGE0, twdt_config->timeout * (1000 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
wdt_hal_config_stage(&twdt_context, WDT_STAGE1, twdt_config->timeout * (2 * 1000 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
wdt_hal_enable(&twdt_context);
wdt_hal_write_protect_enable(&twdt_context);
wdt_hal_init(&obj->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
// Assign the driver object
p_twdt_obj = obj;
portEXIT_CRITICAL(&spinlock);
}
portEXIT_CRITICAL(&twdt_spinlock);
return ESP_OK;
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, timeout * (1000000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
// Configure 2nd stage timeout and behavior
wdt_hal_config_stage(&p_twdt_obj->hal, WDT_STAGE1, timeout * (2 * 1000000 / 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);
portEXIT_CRITICAL(&spinlock);
ret = ESP_OK;
err:
return ret;
}
esp_err_t esp_task_wdt_deinit(void)
{
portENTER_CRITICAL(&twdt_spinlock);
//TWDT must already be initialized
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
//Task list must be empty
ASSERT_EXIT_CRIT_RETURN((twdt_config->list == NULL), ESP_ERR_INVALID_STATE);
esp_err_t ret;
//Disable hardware timer
wdt_hal_deinit(&twdt_context);
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
ESP_GOTO_ON_FALSE(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "all tasks must be deleted");
// 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);
ESP_ERROR_CHECK(esp_intr_free(twdt_config->intr_handle)); //Unregister interrupt
free(twdt_config); //Free twdt_config
twdt_config = NULL;
portEXIT_CRITICAL(&twdt_spinlock);
// Free driver resources
ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle)); //Deregister interrupt
free(obj); //Free p_twdt_obj
return ESP_OK;
err:
portEXIT_CRITICAL(&spinlock);
return ret;
}
esp_err_t esp_task_wdt_add(TaskHandle_t handle)
{
portENTER_CRITICAL(&twdt_spinlock);
//TWDT must already be initialized
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
twdt_task_t *target_task;
bool all_reset;
if (handle == NULL){ //Get handle of current task if none is provided
esp_err_t ret;
if (handle == NULL) { //Get handle of current task if none is provided
handle = xTaskGetCurrentTaskHandle();
}
//Check if tasks exists in task list, and if all other tasks have reset
target_task = find_task_in_twdt_list(handle, &all_reset);
//task cannot be already subscribed
ASSERT_EXIT_CRIT_RETURN((target_task == NULL), ESP_ERR_INVALID_ARG);
//Add target task to TWDT task list
target_task = calloc(1,sizeof(twdt_task_t));
ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NO_MEM);
target_task->task_handle = handle;
target_task->has_reset = true;
target_task->next = NULL;
if (twdt_config->list == NULL) { //Adding to empty list
twdt_config->list = target_task;
} else { //Adding to tail of list
twdt_task_t *task;
for (task = twdt_config->list; task->next != NULL; task = task->next){
; //point task to current tail of TWDT task list
}
task->next = target_task;
// Allocate entry for task
twdt_entry_t *entry = calloc(1, sizeof(twdt_entry_t));
ESP_GOTO_ON_FALSE((entry != NULL), ESP_ERR_NO_MEM, alloc_err, TAG, "insufficient memory");
entry->task_handle = handle;
entry->has_reset = false;
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, state_err, TAG, "task watchdog was never initialized");
// Check if the task is an entry, and if all entries have been reset
bool all_reset;
twdt_entry_t *found_entry = find_entry_from_handle_and_check_all_reset(handle, &all_reset);
ESP_GOTO_ON_FALSE((found_entry == NULL), ESP_ERR_INVALID_ARG, state_err, TAG, "task is already subscribed");
// Add task to entry list
SLIST_INSERT_HEAD(&p_twdt_obj->entries_slist, entry, slist_entry);
if (all_reset) { //Reset hardware timer if all other tasks in list have reset in
reset_hw_timer();
}
portEXIT_CRITICAL(&spinlock); //Nested critical if Legacy
//If idle task, register the idle hook callback to appropriate core
for(int i = 0; i < portNUM_PROCESSORS; i++){
if(handle == xTaskGetIdleTaskHandleForCPU(i)){
// If the task was the idle task, register the idle hook callback to appropriate core
for (int i = 0; i < portNUM_PROCESSORS; i++) {
if (handle == xTaskGetIdleTaskHandleForCPU(i)) {
ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, i));
break;
}
}
if(all_reset){ //Reset hardware timer if all other tasks in list have reset in
reset_hw_timer();
}
portEXIT_CRITICAL(&twdt_spinlock); //Nested critical if Legacy
return ESP_OK;
state_err:
portEXIT_CRITICAL(&spinlock);
free(entry);
alloc_err:
return ret;
}
esp_err_t esp_task_wdt_reset(void)
{
portENTER_CRITICAL(&twdt_spinlock);
//TWDT must already be initialized
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
esp_err_t ret;
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
twdt_task_t *target_task;
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
// Find entry for task
bool all_reset;
//Check if task exists in task list, and if all other tasks have reset
target_task = find_task_in_twdt_list(handle, &all_reset);
//Return error if trying to reset task that is not on the task list
ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NOT_FOUND);
target_task->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
twdt_entry_t *entry;
entry = find_entry_from_handle_and_check_all_reset(handle, &all_reset);
ESP_GOTO_ON_FALSE((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
// 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();
}
ret = ESP_OK;
err:
portEXIT_CRITICAL(&spinlock);
portEXIT_CRITICAL(&twdt_spinlock);
return ESP_OK;
return ret;
}
esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
{
if(handle == NULL){
esp_err_t ret;
if (handle == NULL) {
handle = xTaskGetCurrentTaskHandle();
}
portENTER_CRITICAL(&twdt_spinlock);
//Return error if twdt has not been initialized
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
twdt_task_t *target_task;
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
// Find entry for task
bool all_reset;
target_task = find_task_in_twdt_list(handle, &all_reset);
//Task doesn't exist on list. Return error
ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_INVALID_ARG);
if(target_task == twdt_config->list){ //target_task is head of list. Delete
twdt_config->list = target_task->next;
free(target_task);
}else{ //target_task not head of list. Delete
twdt_task_t *prev;
for (prev = twdt_config->list; prev->next != target_task; prev = prev->next){
; //point prev to task preceding target_task
}
prev->next = target_task->next;
free(target_task);
twdt_entry_t *entry;
entry = find_entry_from_handle_and_check_all_reset(handle, &all_reset);
ESP_GOTO_ON_FALSE((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
// 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();
}
portEXIT_CRITICAL(&spinlock);
//If idle task, deregister idle hook callback form appropriate core
for(int i = 0; i < portNUM_PROCESSORS; i++){
if(handle == xTaskGetIdleTaskHandleForCPU(i)){
// Free the entry
free(entry);
// If idle task, deregister idle hook callback form appropriate core
for (int i = 0; i < portNUM_PROCESSORS; i++) {
if (handle == xTaskGetIdleTaskHandleForCPU(i)) {
esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, i);
break;
}
}
if(all_reset){ //Reset hardware timer if all remaining tasks have reset
reset_hw_timer();
}
portEXIT_CRITICAL(&twdt_spinlock);
return ESP_OK;
err:
portEXIT_CRITICAL(&spinlock);
return ret;
}
esp_err_t esp_task_wdt_status(TaskHandle_t handle)
{
if(handle == NULL){
esp_err_t ret;
if (handle == NULL) {
handle = xTaskGetCurrentTaskHandle();
}
portENTER_CRITICAL(&twdt_spinlock);
//Return if TWDT is not initialized
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
portENTER_CRITICAL(&spinlock);
// Check TWDT state
ESP_GOTO_ON_FALSE((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
// Find entry for task
bool all_reset;
twdt_entry_t *entry;
entry = find_entry_from_handle_and_check_all_reset(handle, &all_reset);
(void) all_reset; // Unused
ret = (entry != NULL) ? ESP_OK : ESP_ERR_NOT_FOUND;
err:
portEXIT_CRITICAL(&spinlock);
twdt_task_t *task;
for(task = twdt_config->list; task!=NULL; task=task->next){
//Return ESP_OK if task is found
ASSERT_EXIT_CRIT_RETURN((task->task_handle != handle), ESP_OK);
}
//Task could not be found
portEXIT_CRITICAL(&twdt_spinlock);
return ESP_ERR_NOT_FOUND;
return ret;
}

Wyświetl plik

@ -676,7 +676,6 @@ components/esp_system/include/esp_private/startup_internal.h
components/esp_system/include/esp_private/system_internal.h
components/esp_system/include/esp_private/usb_console.h
components/esp_system/include/esp_task.h
components/esp_system/include/esp_task_wdt.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