mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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
This commit is contained in:
parent
4bd5c4ef53
commit
4877a9fcba
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user