parent
a3731902f5
commit
b6a2bd1184
12 changed files with 192 additions and 643 deletions
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
// This module implements runtime file I/O API for GCOV.
|
// This module implements runtime file I/O API for GCOV.
|
||||||
|
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_task_wdt.h"
|
|
||||||
#include "soc/cpu.h"
|
#include "soc/cpu.h"
|
||||||
#include "soc/timer_group_struct.h"
|
#include "soc/timer_group_struct.h"
|
||||||
#include "soc/timer_group_reg.h"
|
#include "soc/timer_group_reg.h"
|
||||||
|
|
|
@ -527,11 +527,10 @@ config INT_WDT_CHECK_CPU1
|
||||||
Also detect if interrupts on CPU 1 are disabled for too long.
|
Also detect if interrupts on CPU 1 are disabled for too long.
|
||||||
|
|
||||||
config TASK_WDT
|
config TASK_WDT
|
||||||
bool "Initialize Task Watchdog on startup"
|
bool "Task watchdog"
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This watchdog timer can be used to make sure individual tasks are still running.
|
This watchdog timer can be used to make sure individual tasks are still running.
|
||||||
The Task Watchdog timer can be initialized at run time as well
|
|
||||||
|
|
||||||
config TASK_WDT_PANIC
|
config TASK_WDT_PANIC
|
||||||
bool "Invoke panic handler when Task Watchdog is triggered"
|
bool "Invoke panic handler when Task Watchdog is triggered"
|
||||||
|
@ -540,46 +539,33 @@ config TASK_WDT_PANIC
|
||||||
help
|
help
|
||||||
Normally, the Task Watchdog will only print out a warning if it detects it has not
|
Normally, the Task Watchdog will only print out a warning if it detects it has not
|
||||||
been fed. If this is enabled, it will invoke the panic handler instead, which
|
been fed. If this is enabled, it will invoke the panic handler instead, which
|
||||||
can then halt or reboot the chip. This can also be configured at run time
|
can then halt or reboot the chip.
|
||||||
by reinitializing the task watchdog.
|
|
||||||
|
|
||||||
config TASK_WDT_TIMEOUT_S
|
config TASK_WDT_TIMEOUT_S
|
||||||
int "Task watchdog timeout (ms)"
|
int "Task watchdog timeout (seconds)"
|
||||||
depends on TASK_WDT
|
depends on TASK_WDT
|
||||||
range 1 60000
|
range 1 60
|
||||||
default 5000
|
default 5
|
||||||
help
|
help
|
||||||
Timeout for the task WDT, in ms.
|
Timeout for the task WDT, in seconds.
|
||||||
|
|
||||||
config TASK_WDT_CHECK_IDLE_TASK_CPU0
|
config TASK_WDT_CHECK_IDLE_TASK
|
||||||
bool "Add CPU0 idle task to task watchdog on startup"
|
bool "Task watchdog watches CPU0 idle task"
|
||||||
depends on TASK_WDT
|
depends on TASK_WDT
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
With this turned on, the CPU0 idle task will be added to the task watchdog
|
With this turned on, the task WDT can detect if the idle task is not called within the task
|
||||||
on startup. Adding the idle task to the task watchdog allows for the detection
|
watchdog timeout period. The idle task not being called usually is a symptom of another
|
||||||
of CPU starvation. The idle task not being called is usually a symptom of another
|
|
||||||
task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
|
task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
|
||||||
idle task getting some runtime every now and then.
|
idle task getting some runtime every now and then. Take Care: With this disabled, this
|
||||||
|
watchdog will trigger if no tasks register themselves within the timeout value.
|
||||||
|
|
||||||
config TASK_WDT_CHECK_IDLE_TASK_CPU1
|
config TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||||
bool "Add CPU0 idle task to task watchdog on startup"
|
bool "Task watchdog also watches CPU1 idle task"
|
||||||
depends on TASK_WDT && !FREERTOS_UNICORE
|
depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
With this turned on, the CPU1 idle task will also be added to the task watchdog
|
Also check the idle task that runs on CPU1.
|
||||||
on startup.
|
|
||||||
|
|
||||||
config TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
bool "Use legacy behavior for task watchdog"
|
|
||||||
depends on TASK_WDT
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Task wdt legacy behavior will add a task to the wdt list on its first
|
|
||||||
call to esp_task_wdt_feed(). Furthermore, tasks can only remove
|
|
||||||
themselves from the wdt task list when calling esp_task_wdt_delete().
|
|
||||||
Therefore esp_task_wdt_delete() should be called with no parameters
|
|
||||||
when legacy behavior is enabled.
|
|
||||||
|
|
||||||
#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32
|
#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32
|
||||||
#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU.
|
#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU.
|
||||||
|
|
|
@ -323,6 +323,9 @@ void start_cpu0_default(void)
|
||||||
do_global_ctors();
|
do_global_ctors();
|
||||||
#if CONFIG_INT_WDT
|
#if CONFIG_INT_WDT
|
||||||
esp_int_wdt_init();
|
esp_int_wdt_init();
|
||||||
|
#endif
|
||||||
|
#if CONFIG_TASK_WDT
|
||||||
|
esp_task_wdt_init();
|
||||||
#endif
|
#endif
|
||||||
esp_cache_err_int_init();
|
esp_cache_err_int_init();
|
||||||
esp_crosscore_int_init();
|
esp_crosscore_int_init();
|
||||||
|
@ -397,30 +400,6 @@ static void main_task(void* args)
|
||||||
#endif
|
#endif
|
||||||
//Enable allocation in region where the startup stacks were located.
|
//Enable allocation in region where the startup stacks were located.
|
||||||
heap_caps_enable_nonos_stack_heaps();
|
heap_caps_enable_nonos_stack_heaps();
|
||||||
|
|
||||||
//Initialize task wdt
|
|
||||||
#ifdef CONFIG_TASK_WDT
|
|
||||||
#ifdef CONFIG_TASK_WDT_PANIC
|
|
||||||
esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, true);
|
|
||||||
#else
|
|
||||||
esp_task_wdt_init(CONFIG_TASK_WDT_TIMEOUT_S, false);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
//Add IDLE 0 to task wdt
|
|
||||||
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
|
|
||||||
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
|
|
||||||
if(idle_0 != NULL){
|
|
||||||
esp_task_wdt_add(idle_0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
//Add IDLE 1 to task wdt
|
|
||||||
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
|
||||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
|
||||||
if(idle_1 != NULL){
|
|
||||||
esp_task_wdt_add(idle_1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
app_main();
|
app_main();
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,136 +15,60 @@
|
||||||
#ifndef __ESP_TASK_WDT_H
|
#ifndef __ESP_TASK_WDT_H
|
||||||
#define __ESP_TASK_WDT_H
|
#define __ESP_TASK_WDT_H
|
||||||
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "esp_err.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
This routine enables a more general-purpose task watchdog which uses the TIMERG0
|
|
||||||
WDT: All tasks subscribed to the task wdt must feed the wdt at least once
|
|
||||||
before the wdt times out or else the wdt will bark. The Idle tasks can also be
|
|
||||||
subscribed to the wdt. This allows for the detection of CPU starvation.
|
|
||||||
|
|
||||||
To enable/disable legacy behavior, toggle the CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
/** \defgroup Watchdog_APIs Watchdog APIs
|
||||||
option in menuconfig. Under legacy behavior, a task will subscribe to the
|
* @brief Watchdog APIs
|
||||||
wdt on its first call of esp_task_wdt_feed(). esp_task_wdt_add() SHOULD NOT BE
|
*/
|
||||||
CALLED in an application if legacy behavior is enabled. esp_task_wdt_delete()
|
|
||||||
will only allow tasks to unsubscribe themselves from the task wdt under legacy
|
/** @addtogroup Watchdog_APIs
|
||||||
behavior. Enabling legacy behavior will also force the task wdt to be enabled on
|
* @{
|
||||||
startup.
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This routine enables a more general-purpose task watchdog: tasks can individually
|
||||||
|
feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
|
||||||
|
watchdog within the specified time. Optionally, the idle tasks can also configured
|
||||||
|
to feed the watchdog in a similar fashion, to detect CPU starvation.
|
||||||
|
|
||||||
|
This uses the TIMERG0 WDT.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the task watchdog timer
|
* @brief Initialize the task watchdog. This is called in the init code, if the
|
||||||
|
* task watchdog is enabled in menuconfig.
|
||||||
*
|
*
|
||||||
* This function will initialize the task watchdog timer. If the wdt
|
|
||||||
* has already been initialized, calling this function again will change the
|
|
||||||
* timeout and panic configuration, then reset the timer count. Once the watchdog
|
|
||||||
* timer has been initialized, tasks can subscribe to the watchdog timer.
|
|
||||||
*
|
|
||||||
* @param[in] timeout Timeout(ms) period of watchdog timer
|
|
||||||
* @param[in] panic Flag that if set, will cause the wdt to trigger the
|
|
||||||
* panic handler when it times out
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: Initialization was successful
|
|
||||||
* - ESP_ERR_NO_MEM: Initialization was unsuccessful due to lack of
|
|
||||||
* memory
|
|
||||||
*
|
|
||||||
* @note esp_task_wdt_init() must only be called after the scheduler
|
|
||||||
* started
|
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic);
|
void esp_task_wdt_init();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deinitialize the task watchdog timer
|
* @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling
|
||||||
*
|
* task to keep feeding the watchdog until task_wdt_delete() is called.
|
||||||
* This function will deinitialize the task watchdog timer. This function should
|
|
||||||
* be called when all tasks have unsubscribed form the watchdog timer.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: Watchdog timer successfully deinitialized
|
|
||||||
* - ESP_ERR_INVALID_STATE: Error due to wdt not being initialized, or
|
|
||||||
* if tasks are still subscribed to the wdt
|
|
||||||
*/
|
|
||||||
esp_err_t esp_task_wdt_deinit();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Subscribes a tasks to the task watchdog timer
|
|
||||||
*
|
*
|
||||||
* This function subscribes a task to the task watchdog timer. Note that once
|
|
||||||
* subscribed, a task must periodically feed the watchdog timer. If the task
|
|
||||||
* is an IDLE task, this function will enable feeding from the idle hook
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* @param[in] handle Handle of the task to be added. Input NULL
|
|
||||||
* to add the current running task
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: Successfully subscribed the task to the wdt
|
|
||||||
* - ESP_ERR_INVALID_ARG: Failed as task is already subscribed
|
|
||||||
* - ESP_ERR_INVALID_STATE: Failed as wdt has not been initialized
|
|
||||||
* - ESP_ERR_NO_MEM: Failed due to lack of memory
|
|
||||||
*
|
|
||||||
* @note This function should not be called from application code if
|
|
||||||
* CONFIG_TASK_WDT_LEGACY_BEHAVIOR has been enabled. Legacy behavior
|
|
||||||
* uses esp_task_wdt_feed() to subscribe tasks to the wdt.
|
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_task_wdt_add(TaskHandle_t handle);
|
|
||||||
|
void esp_task_wdt_feed();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Feed the current running task
|
* @brief Delete the watchdog for the current task.
|
||||||
*
|
*
|
||||||
* This function will feed the current running task if it has subscribed to the
|
|
||||||
* task watchdog timer. All non-idle tasks subscribed to the wdt must call this
|
|
||||||
* function at least once per watchdog timeout period. Idle tasks will feed
|
|
||||||
* the task watchdog automatically from their idle hooks.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: Successfully fed the current running task
|
|
||||||
* - ESP_ERR_INVALID_STATE: Failed to feed current running task as the
|
|
||||||
* wdt has not been initialized, or the
|
|
||||||
* current running task has not subscribed
|
|
||||||
*
|
|
||||||
* @note If CONFIG_TASK_WDT_LEGACY_BEHAVIOR is enabled, tasks will subscribe
|
|
||||||
* to the watchdog on their first call of esp_task_wdt_feed().
|
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_task_wdt_feed();
|
void esp_task_wdt_delete();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Unsubscribes a task from the task watchdog timer
|
* @}
|
||||||
*
|
|
||||||
* This function unsubscribes a task from the task watchdog. After unsubscribing,
|
|
||||||
* the task should no longer feed the wdt. If the unsubscribing task is an idle
|
|
||||||
* task, this function will disable feeding from the idle hook automatically.
|
|
||||||
*
|
|
||||||
* @param[in] handle Handle of the task to be deleted. Input NULL
|
|
||||||
* if deleting the current running task.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: Successfully unsubscribed task form the wdt
|
|
||||||
* - ESP_ERR_INVALID_ARG: Failed to unsubscribe task as it was not
|
|
||||||
* subscribed to begin with
|
|
||||||
* - ESP_ERR_INVALID_STATE: Failed to unsubscribe task as wdt has not
|
|
||||||
* been initialized yet
|
|
||||||
*
|
|
||||||
* @note Legacy behavior only allows tasks to unsubscribe from the wdt on their
|
|
||||||
* own behalf. Therefore, if CONFIG_TASK_WDT_LEGACY_BEHAVIOR is
|
|
||||||
* enabled, this esp_task_wdt_delete() will accept no parameters and
|
|
||||||
* unsubscribe the calling task from the wdt.
|
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
esp_err_t esp_task_wdt_delete();
|
|
||||||
#else
|
|
||||||
esp_err_t esp_task_wdt_delete(TaskHandle_t handle);
|
|
||||||
#endif //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif //__ESP_TASK_WDT_H
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,7 +20,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "freertos/FreeRTOSConfig.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_intr.h"
|
#include "esp_intr.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "esp_ipc.h"
|
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "esp_freertos_hooks.h"
|
#include "esp_freertos_hooks.h"
|
||||||
#include "soc/timer_group_struct.h"
|
#include "soc/timer_group_struct.h"
|
||||||
|
@ -39,80 +37,25 @@
|
||||||
|
|
||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
|
|
||||||
//Assertion macro that exits from critical section and returns 'ret'
|
#if CONFIG_TASK_WDT
|
||||||
#define ASSERT_EXIT_CRIT_RET_ERR(cond, ret) ({ \
|
|
||||||
if(!(cond)){ \
|
static const char* TAG = "task_wdt";
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock); \
|
|
||||||
return ret; \
|
|
||||||
} \
|
|
||||||
})
|
|
||||||
|
|
||||||
typedef struct wdt_task_t wdt_task_t;
|
typedef struct wdt_task_t wdt_task_t;
|
||||||
struct wdt_task_t {
|
struct wdt_task_t {
|
||||||
TaskHandle_t task_handle;
|
TaskHandle_t task_handle;
|
||||||
bool fed;
|
bool fed_watchdog;
|
||||||
wdt_task_t *next;
|
wdt_task_t *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct task_wdt_config_t task_wdt_config_t;
|
static wdt_task_t *wdt_task_list=NULL;
|
||||||
struct task_wdt_config_t {
|
|
||||||
wdt_task_t *list;
|
|
||||||
uint32_t timeout;
|
|
||||||
bool panic;
|
|
||||||
bool idle_enable[portNUM_PROCESSORS];
|
|
||||||
TaskHandle_t idle_handles[portNUM_PROCESSORS];
|
|
||||||
intr_handle_t intr_handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
static task_wdt_config_t *wdt_config = NULL;
|
|
||||||
static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal function that checks the list for target task. Returns the list item
|
|
||||||
* if found and returns null if not found. Also checks if the all the other
|
|
||||||
* tasks have checked in. Called within critical.
|
|
||||||
*/
|
|
||||||
static wdt_task_t *check_list(TaskHandle_t handle, bool *checked)
|
|
||||||
{
|
|
||||||
wdt_task_t *target = NULL;
|
|
||||||
*checked = true;
|
|
||||||
for(wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
|
|
||||||
if(task->task_handle == handle){
|
|
||||||
target = task; //Get pointer to target task list member
|
|
||||||
}else{
|
|
||||||
if(task->fed == false){ //If a task has yet to check in
|
|
||||||
*checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
static void task_wdt_isr(void *arg) {
|
||||||
* Resets the wdt and fed flags of each task on the list. Called within critical
|
|
||||||
*/
|
|
||||||
static void feed_wdt()
|
|
||||||
{
|
|
||||||
//All tasks have checked in; time to feed the hw watchdog.
|
|
||||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
|
||||||
TIMERG0.wdt_feed=1;
|
|
||||||
TIMERG0.wdt_wprotect=0;
|
|
||||||
//Reset fed flags on all tasks in list
|
|
||||||
for (wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
|
|
||||||
task->fed=false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ISR for when wdt expires. Checks for which tasks have not fed. Trigger panic
|
|
||||||
* if configured to do so in initialization
|
|
||||||
*/
|
|
||||||
static void task_wdt_isr(void *arg)
|
|
||||||
{
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock);
|
|
||||||
wdt_task_t *wdttask;
|
wdt_task_t *wdttask;
|
||||||
const char *cpu;
|
const char *cpu;
|
||||||
//Feed the watchdog so we don't reset
|
//Feed the watchdog so we do not reset
|
||||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||||
TIMERG0.wdt_feed=1;
|
TIMERG0.wdt_feed=1;
|
||||||
TIMERG0.wdt_wprotect=0;
|
TIMERG0.wdt_wprotect=0;
|
||||||
|
@ -122,14 +65,16 @@ static void task_wdt_isr(void *arg)
|
||||||
//bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case,
|
//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
|
//something bad already happened and reporting this is considered more important
|
||||||
//than the badness caused by a spinlock here.
|
//than the badness caused by a spinlock here.
|
||||||
if (!wdt_config->list) { //No tasks on list
|
portENTER_CRITICAL(&taskwdt_spinlock);
|
||||||
|
if (!wdt_task_list) {
|
||||||
|
//No task on list. Maybe none registered yet.
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Watchdog got triggered because at least one task did not report in.
|
//Watchdog got triggered because at least one task did not report in.
|
||||||
ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
|
ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
|
||||||
for (wdttask=wdt_config->list; wdttask!=NULL; wdttask=wdttask->next) {
|
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
|
||||||
if (!wdttask->fed) {
|
if (!wdttask->fed_watchdog) {
|
||||||
cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
|
cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
|
||||||
if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
|
if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
|
||||||
ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
|
ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
|
||||||
|
@ -140,243 +85,125 @@ static void task_wdt_isr(void *arg)
|
||||||
ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
|
ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wdt_config->panic){
|
#if CONFIG_TASK_WDT_PANIC
|
||||||
ets_printf("Aborting.\n");
|
ets_printf("Aborting.\n");
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
abort();
|
||||||
abort();
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Idle hook for Idle Task to feed the wdt. Will only call feed if the idle task
|
void esp_task_wdt_feed() {
|
||||||
* of that core has been added to wdt task list.
|
wdt_task_t *wdttask=wdt_task_list;
|
||||||
*/
|
bool found_task=false, do_feed_wdt=true;
|
||||||
static bool idle_hook(void)
|
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||||
{
|
portENTER_CRITICAL(&taskwdt_spinlock);
|
||||||
uint32_t core_id = xPortGetCoreID();
|
|
||||||
if (wdt_config->idle_enable[core_id]){
|
//Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
|
||||||
esp_task_wdt_feed();
|
//the real watchdog timer.
|
||||||
|
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
|
||||||
|
//See if we are at the current task.
|
||||||
|
if (wdttask->task_handle == handle) {
|
||||||
|
wdttask->fed_watchdog=true;
|
||||||
|
found_task=true;
|
||||||
|
}
|
||||||
|
//If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
|
||||||
|
if (!wdttask->fed_watchdog) do_feed_wdt=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found_task) {
|
||||||
|
//This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
|
||||||
|
//the linked list.
|
||||||
|
wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
|
||||||
|
memset(newtask, 0, sizeof(wdt_task_t));
|
||||||
|
newtask->task_handle=handle;
|
||||||
|
newtask->fed_watchdog=true;
|
||||||
|
if (wdt_task_list == NULL) {
|
||||||
|
wdt_task_list=newtask;
|
||||||
|
} else {
|
||||||
|
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ;
|
||||||
|
wdttask->next=newtask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (do_feed_wdt) {
|
||||||
|
//All tasks have checked in; time to feed the hw watchdog.
|
||||||
|
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||||
|
TIMERG0.wdt_feed=1;
|
||||||
|
TIMERG0.wdt_wprotect=0;
|
||||||
|
//Reset fed_watchdog status
|
||||||
|
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_task_wdt_delete() {
|
||||||
|
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||||
|
wdt_task_t *wdttask=wdt_task_list;
|
||||||
|
portENTER_CRITICAL(&taskwdt_spinlock);
|
||||||
|
|
||||||
|
//Wdt task list can't be empty
|
||||||
|
if (!wdt_task_list) {
|
||||||
|
ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
|
||||||
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (handle==wdt_task_list) {
|
||||||
|
//Current task is first on list.
|
||||||
|
wdt_task_list=wdt_task_list->next;
|
||||||
|
free(wdttask);
|
||||||
|
} else {
|
||||||
|
//Find current task in list
|
||||||
|
if (wdt_task_list->task_handle==handle) {
|
||||||
|
//Task is the very first one.
|
||||||
|
wdt_task_t *freeme=wdt_task_list;
|
||||||
|
wdt_task_list=wdt_task_list->next;
|
||||||
|
free(freeme);
|
||||||
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
|
||||||
|
if (!wdttask->next) {
|
||||||
|
ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
|
||||||
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wdt_task_t *freeme=wdttask->next;
|
||||||
|
wdttask->next=wdttask->next->next;
|
||||||
|
free(freeme);
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&taskwdt_spinlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
|
||||||
|
static bool idle_hook(void) {
|
||||||
|
#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||||
|
if (xPortGetCoreID()!=0) return true;
|
||||||
|
#endif
|
||||||
|
esp_task_wdt_feed();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Initializes the task watchdog timer 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.
|
|
||||||
*/
|
|
||||||
esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
|
|
||||||
{
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock);
|
|
||||||
if(wdt_config == NULL){ //wdt not initialized yet
|
|
||||||
//Allocate memory for wdt_config
|
|
||||||
wdt_config = calloc(1, sizeof(task_wdt_config_t));
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_NO_MEM);
|
|
||||||
|
|
||||||
wdt_config->list = NULL;
|
|
||||||
wdt_config->timeout = timeout;
|
|
||||||
wdt_config->panic = panic;
|
|
||||||
|
|
||||||
//Get idle task handles
|
|
||||||
for(int i = 0; i < portNUM_PROCESSORS; i++){
|
|
||||||
wdt_config->idle_handles[i] = xTaskGetIdleTaskHandleForCPU( i );
|
|
||||||
wdt_config->idle_enable[i] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Register Idle Hook and Interrupts
|
|
||||||
esp_register_freertos_idle_hook(idle_hook);
|
|
||||||
ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &wdt_config->intr_handle))
|
|
||||||
|
|
||||||
//Configure hardware timer
|
|
||||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
|
|
||||||
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
|
||||||
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
|
||||||
TIMERG0.wdt_config0.level_int_en=1;
|
|
||||||
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
|
|
||||||
TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
|
|
||||||
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
|
||||||
TIMERG0.wdt_config2=wdt_config->timeout*2; //Set timeout before interrupt
|
|
||||||
TIMERG0.wdt_config3=wdt_config->timeout*4; //Set timeout before reset
|
|
||||||
TIMERG0.wdt_config0.en=1;
|
|
||||||
TIMERG0.wdt_feed=1;
|
|
||||||
TIMERG0.wdt_wprotect=0; //Enable write protection
|
|
||||||
|
|
||||||
}else{ //wdt_config previously initialized
|
|
||||||
//Reconfigure task wdt
|
|
||||||
wdt_config->panic = panic;
|
|
||||||
wdt_config->timeout = timeout;
|
|
||||||
|
|
||||||
//Reconfigure hardware timer
|
|
||||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
|
|
||||||
TIMERG0.wdt_config0.en=0; //Disable timer
|
|
||||||
TIMERG0.wdt_config2=wdt_config->timeout*2; //Set timeout before interrupt
|
|
||||||
TIMERG0.wdt_config3=wdt_config->timeout*4; //Set timeout before reset
|
|
||||||
TIMERG0.wdt_config0.en=1; //Renable timer
|
|
||||||
TIMERG0.wdt_feed=1; //Reset timer
|
|
||||||
TIMERG0.wdt_wprotect=0; //Enable write protection
|
|
||||||
}
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_task_wdt_deinit()
|
|
||||||
{
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock);
|
|
||||||
//wdt must already be initialized
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
|
|
||||||
//Task list must be empty
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config->list == NULL), ESP_ERR_INVALID_STATE);
|
|
||||||
|
|
||||||
//Disable hardware timer
|
|
||||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
|
|
||||||
TIMERG0.wdt_config0.en=0; //Disable timer
|
|
||||||
TIMERG0.wdt_wprotect=0; //Enable write protection
|
|
||||||
|
|
||||||
esp_deregister_freertos_idle_hook(idle_hook); //deregister idle hook
|
|
||||||
ESP_ERROR_CHECK(esp_intr_free(wdt_config->intr_handle)) //Unregister interrupt
|
|
||||||
free(wdt_config); //Free wdt_config
|
|
||||||
wdt_config = NULL;
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_task_wdt_add(TaskHandle_t handle)
|
|
||||||
{
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock); //Nested critical in Legacy (called from feed)
|
|
||||||
#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
wdt_task_t *target_task;
|
|
||||||
#else
|
|
||||||
//Return error if wdt has not been initialized
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
|
|
||||||
|
|
||||||
wdt_task_t *target_task;
|
|
||||||
bool all_fed;
|
|
||||||
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 checked in
|
|
||||||
target_task = check_list(handle, &all_fed);
|
|
||||||
//Return error if task has already been added
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((target_task == NULL), ESP_ERR_INVALID_ARG);
|
|
||||||
#endif //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
//Add task to wdt list
|
|
||||||
target_task = calloc(1,sizeof(wdt_task_t));
|
|
||||||
//Return error if calloc failed
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_NO_MEM);
|
|
||||||
|
|
||||||
target_task->task_handle = handle;
|
|
||||||
target_task->fed = true;
|
|
||||||
target_task->next = NULL;
|
|
||||||
if (wdt_config->list == NULL) { //Adding to empty list
|
|
||||||
wdt_config->list = target_task;
|
|
||||||
} else { //Adding to tail of list
|
|
||||||
wdt_task_t *task;
|
|
||||||
for (task = wdt_config->list; task->next != NULL; task = task->next){
|
|
||||||
; //point task to current tail of wdt task list
|
|
||||||
}
|
|
||||||
task->next = target_task;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If idle task, set idle_enable flag
|
|
||||||
for(int i = 0; i < portNUM_PROCESSORS; i++){
|
|
||||||
if(handle == wdt_config->idle_handles[i]){
|
|
||||||
wdt_config->idle_enable[i] = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
if(all_fed){ //Reset if all other tasks in list have checked in
|
|
||||||
feed_wdt();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock); //Nested critical if Legacy
|
|
||||||
return ESP_OK;
|
|
||||||
|
void esp_task_wdt_init() {
|
||||||
|
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||||
|
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
||||||
|
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||||
|
TIMERG0.wdt_config0.level_int_en=1;
|
||||||
|
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
|
||||||
|
TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
|
||||||
|
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||||
|
TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt
|
||||||
|
TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset
|
||||||
|
TIMERG0.wdt_config0.en=1;
|
||||||
|
TIMERG0.wdt_feed=1;
|
||||||
|
TIMERG0.wdt_wprotect=0;
|
||||||
|
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
|
||||||
|
esp_register_freertos_idle_hook(idle_hook);
|
||||||
|
#endif
|
||||||
|
ESP_ERROR_CHECK( esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, NULL) );
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_task_wdt_feed()
|
|
||||||
{
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock);
|
|
||||||
//Return error if wdt has not been initialized
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
|
|
||||||
|
|
||||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
|
||||||
wdt_task_t *target_task;
|
|
||||||
bool all_fed;
|
|
||||||
|
|
||||||
//Check if tasks exists in task list, and if all other tasks have checked in
|
|
||||||
target_task = check_list(handle, &all_fed);
|
|
||||||
|
|
||||||
if(target_task == NULL){
|
|
||||||
#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
//Add task to wdt task list if it doesn't exist, return error if failed to add
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((esp_task_wdt_add(handle) == ESP_OK), ESP_ERR_NO_MEM);
|
|
||||||
#else
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
|
||||||
return ESP_ERR_INVALID_STATE; //Return error if task does not exist
|
|
||||||
#endif //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
}else{
|
|
||||||
target_task->fed = true; //Feed the task
|
|
||||||
}
|
|
||||||
|
|
||||||
if(all_fed){ //Reset if all other tasks in list have checked in
|
|
||||||
feed_wdt();
|
|
||||||
}
|
|
||||||
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
esp_err_t esp_task_wdt_delete()
|
|
||||||
{
|
|
||||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
|
||||||
#else
|
|
||||||
esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
|
|
||||||
{
|
|
||||||
if(handle == NULL){
|
|
||||||
handle = xTaskGetCurrentTaskHandle();
|
|
||||||
}
|
|
||||||
#endif //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
portENTER_CRITICAL(&taskwdt_spinlock);
|
|
||||||
//Return error if wdt has not been initialized
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
|
|
||||||
wdt_task_t *target_task;
|
|
||||||
bool all_fed;
|
|
||||||
|
|
||||||
target_task = check_list(handle, &all_fed);
|
|
||||||
|
|
||||||
//Task doesn't exist on list. Return error
|
|
||||||
ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_INVALID_ARG);
|
|
||||||
|
|
||||||
if(target_task == wdt_config->list){ //target_task is head of list. Delete
|
|
||||||
wdt_config->list = target_task->next;
|
|
||||||
free(target_task);
|
|
||||||
}else{ //target_task not head of list. Delete
|
|
||||||
wdt_task_t *prev;
|
|
||||||
for (prev = wdt_config->list; prev->next != target_task; prev = prev->next){
|
|
||||||
; //point prev to task preceding target_task
|
|
||||||
}
|
|
||||||
prev->next = target_task->next;
|
|
||||||
free(target_task);
|
|
||||||
}
|
|
||||||
|
|
||||||
//If idle task, disable idle_enable flag
|
|
||||||
for(int i = 0; i < portNUM_PROCESSORS; i++){
|
|
||||||
if(handle == wdt_config->idle_handles[i]){
|
|
||||||
wdt_config->idle_enable[i] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(all_fed){ //Reset timer if all remaining tasks have checked in
|
|
||||||
feed_wdt();
|
|
||||||
}
|
|
||||||
|
|
||||||
portEXIT_CRITICAL(&taskwdt_spinlock);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1382,15 +1382,6 @@ BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter )
|
||||||
*/
|
*/
|
||||||
TaskHandle_t xTaskGetIdleTaskHandle( void );
|
TaskHandle_t xTaskGetIdleTaskHandle( void );
|
||||||
|
|
||||||
/**
|
|
||||||
* xTaskGetIdleTaskHandleForCPU() is only available if
|
|
||||||
* INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h.
|
|
||||||
*
|
|
||||||
* Simply returns the idle task handle of a given cpu. It is not valid to call
|
|
||||||
* xTaskGetIdleTaskHandleForCPU() before the scheduler has been started.
|
|
||||||
*/
|
|
||||||
TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for
|
* configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for
|
||||||
* uxTaskGetSystemState() to be available.
|
* uxTaskGetSystemState() to be available.
|
||||||
|
|
|
@ -2371,18 +2371,6 @@ UBaseType_t uxTaskGetNumberOfTasks( void )
|
||||||
return xIdleTaskHandle[ xPortGetCoreID() ];
|
return xIdleTaskHandle[ xPortGetCoreID() ];
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskHandle_t xTaskGetIdleTaskHandleForCPU( UBaseType_t cpuid )
|
|
||||||
{
|
|
||||||
TaskHandle_t xReturn = NULL;
|
|
||||||
/* If xTaskGetIdleTaskHandleForCPU() is called before the scheduler has been
|
|
||||||
started, then xIdleTaskHandle will be NULL. */
|
|
||||||
if (cpuid < portNUM_PROCESSORS) {
|
|
||||||
configASSERT( ( xIdleTaskHandle[ cpuid ] != NULL ) );
|
|
||||||
xReturn = xIdleTaskHandle[ cpuid ];
|
|
||||||
}
|
|
||||||
return xReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
|
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
|
||||||
/*----------------------------------------------------------*/
|
/*----------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
|
@ -28,40 +28,23 @@ Task watchdog
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
Any tasks can elect to be watched by the task watchdog. If such a task does not feed the watchdog within the time
|
Any tasks can elect to be watched by the task watchdog. If such a task does not feed the watchdog within the time
|
||||||
specified by the task watchdog timeout, the watchdog will print out a warning with information about which processes
|
specified by the task watchdog timeout (which is configurable using ``make menuconfig``), the watchdog will
|
||||||
are running on the ESP32 CPUs and which processes failed to feed the watchdog.
|
print out a warning with information about which processes are running on the ESP32 CPUs and which processes
|
||||||
|
failed to feed the watchdog.
|
||||||
|
|
||||||
Ideally, the task watchdog should watch the idle tasks. The usual cause of the idle tasks not feeding the watchdog
|
By default, the task watchdog watches the idle tasks. The usual cause of idle tasks not feeding the watchdog
|
||||||
is a higher-priority process looping without yielding to the lower-priority processes, and can be an indicator
|
is a higher-priority process looping without yielding to the lower-priority processes, and can be an indicator
|
||||||
of badly-written code that spinloops on a peripheral or a task that is stuck in an infinite loop.
|
of badly-written code that spinloops on a peripheral or a task that is stuck in an infinite loop.
|
||||||
|
|
||||||
Other task can elect to be watched by the task watchdog. If the watchdog watches multiple tasks, all the tasks
|
Other task can elect to be watched by the task watchdog by calling ``esp_task_wdt_feed()``. Calling this routine
|
||||||
must feed before the watchdog times out. If one or more tasks fails to feed, the watchdog timeout will trigger.
|
for the first time will register the task to the task watchdog; calling it subsequent times will feed
|
||||||
The watchdog timer can be initialized using :cpp:func:`esp_task_wdt_init` which will configure the hardware timer and
|
the watchdog. If a task does not want to be watched anymore (e.g. because it is finished and will call
|
||||||
establish the hardware timer interrupts. In order for a task to be watched by the task watchdog, a task must subscribe to
|
``vTaskDelete()`` on itself), it needs to call ``esp_task_wdt_delete()``.
|
||||||
the task watchdog using :cpp:func:`esp_task_wdt_add`. Once added, each subscribed task must call :cpp:func:`esp_task_wdt_feed`
|
|
||||||
periodically to prevent the watchdog from timing out. If all tasks being watched have fed, the watchdog timer counter immediately resets
|
|
||||||
and starts recounting to the timeout value from zero. To unsubscribe a task from the task watchdog, call :cpp:func:`esp_task_wdt_delete()`.
|
|
||||||
Once all tasks have been unsubscribed form the task watchdog, :cpp:func:`esp_task_wdt_deinit()` can be called to deinitialize
|
|
||||||
the task watchdog. Deinitialization will stop the hardware timer, deregister the hardware timer interrupts, and remove the idle hooks
|
|
||||||
if idle tasks were being watched.
|
|
||||||
|
|
||||||
By setting the task watchdog options in ``make menuconfig``, the task watchdog can be automatically initialized
|
The task watchdog is built around the hardware watchdog in timer group 0. If this watchdog for some reason
|
||||||
during startup by enabling the :ref:`CONFIG_TASK_WDT`. Moreover the two Idle tasks can also be subscibed to the
|
|
||||||
task watchdog on startup as well by enabling :ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0` and
|
|
||||||
:ref:`CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1`.
|
|
||||||
|
|
||||||
The task watchdog is built around the hardware watchdog in timer group 0. If the watchdog for some reason
|
|
||||||
cannot execute the interrupt handler that prints the task data (e.g. because IRAM is overwritten by garbage
|
cannot execute the interrupt handler that prints the task data (e.g. because IRAM is overwritten by garbage
|
||||||
or interrupts are disabled entirely) it will hard-reset the SOC.
|
or interrupts are disabled entirely) it will hard-reset the SOC.
|
||||||
|
|
||||||
Note: ``make menuconfig`` provides a :ref:`CONFIG_TASK_WDT_LEGACY_BEHAVIOR` option which will change the behavior of the
|
|
||||||
task watchdog to make it compatible with the legacy code. Note that the legacy behavior causes tasks to subscribe to the
|
|
||||||
task watchdog on their first call to :cpp:func:`esp_task_wdt_feed`. Moreover, legacy behavior only allows
|
|
||||||
tasks to unsubscribe on their own behalf when calling :cpp:func:`esp_task_wdt_delete()`. It is strongly recommended that
|
|
||||||
non-legacy behavior is used as additions/deletions/feeds are explicit.
|
|
||||||
|
|
||||||
|
|
||||||
JTAG and watchdogs
|
JTAG and watchdogs
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -70,27 +53,20 @@ CPU. This makes it very hard to debug code; that is why the OpenOCD config will
|
||||||
This does mean that you will not get any warnings or panics from either the task or interrupt watchdog when the ESP32
|
This does mean that you will not get any warnings or panics from either the task or interrupt watchdog when the ESP32
|
||||||
is connected to OpenOCD via JTAG.
|
is connected to OpenOCD via JTAG.
|
||||||
|
|
||||||
Interrupt Watchdog API Reference
|
API Reference
|
||||||
--------------------------------
|
-------------
|
||||||
|
|
||||||
Header File
|
Header Files
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
* :component_file:`esp32/include/esp_int_wdt.h`
|
* :component_file:`esp32/include/esp_int_wdt.h`
|
||||||
|
* :component_file:`esp32/include/esp_task_wdt.h`
|
||||||
|
|
||||||
|
|
||||||
Functions
|
Functions
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. doxygenfunction:: esp_int_wdt_init
|
.. doxygenfunction:: esp_int_wdt_init
|
||||||
|
.. doxygenfunction:: esp_task_wdt_init
|
||||||
Task Watchdog API Reference
|
.. doxygenfunction:: esp_task_wdt_feed
|
||||||
----------------------------
|
.. doxygenfunction:: esp_task_wdt_delete
|
||||||
|
|
||||||
NOTE: Ensure that the :ref:`CONFIG_TASK_WDT_LEGACY_BEHAVIOR` configuraiton is disabled using ``make menuconfig``
|
|
||||||
to prevent legacy behavior of the task watchdog.
|
|
||||||
|
|
||||||
A full example using the Task Watchdog is available in esp-idf: :example:`system/task_watchdog`
|
|
||||||
|
|
||||||
|
|
||||||
.. include:: /_build/inc/esp_task_wdt.inc
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#
|
|
||||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
|
||||||
# project subdirectory.
|
|
||||||
#
|
|
||||||
|
|
||||||
PROJECT_NAME := task_watchdog
|
|
||||||
|
|
||||||
include $(IDF_PATH)/make/project.mk
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# Example: task_watchdog
|
|
||||||
|
|
||||||
This test code shows how to initialize the task watchdog, add tasks to the
|
|
||||||
watchdog task list, feeding the tasks, deleting tasks from the watchdog task
|
|
||||||
list, and deinitializing the task watchdog.
|
|
||||||
|
|
||||||
|
|
||||||
### Test:
|
|
||||||
|
|
||||||
Program should run without error. Comment out "esp_task_wdt_feed()" to observe
|
|
||||||
a watchdog timeout.
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#
|
|
||||||
# Main Makefile. This is basically the same as a component makefile.
|
|
||||||
#
|
|
|
@ -1,98 +0,0 @@
|
||||||
/* Task_Watchdog Example
|
|
||||||
|
|
||||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, this
|
|
||||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
||||||
CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/queue.h"
|
|
||||||
#include "esp_task_wdt.h"
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
|
|
||||||
|
|
||||||
#define TIMEOUT 4000
|
|
||||||
#define TASK_PERIOD 2000
|
|
||||||
|
|
||||||
#define ASSERT_PRINT_ERROR(cond, str) ({ \
|
|
||||||
if(!(cond)){ \
|
|
||||||
printf("%s\n",str); \
|
|
||||||
} \
|
|
||||||
})
|
|
||||||
|
|
||||||
void feeding_task(void *arg){
|
|
||||||
int core = xPortGetCoreID();
|
|
||||||
//Simple task that periodically feeds watchdog
|
|
||||||
while(1){
|
|
||||||
if(esp_task_wdt_feed() == ESP_OK){
|
|
||||||
printf("Feed Task %d\n", core);
|
|
||||||
}
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(TASK_PERIOD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
//Initialize task watchdog
|
|
||||||
if(esp_task_wdt_init(TIMEOUT, false) != ESP_OK){
|
|
||||||
printf("Error\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create tasks
|
|
||||||
TaskHandle_t handle_0;
|
|
||||||
TaskHandle_t handle_1;
|
|
||||||
xTaskCreatePinnedToCore(feeding_task, "Feed Task 0", 2000, NULL, 15, &handle_0, 0);
|
|
||||||
xTaskCreatePinnedToCore(feeding_task, "Feed Task 1", 2000, NULL, 15, &handle_1, 1);
|
|
||||||
|
|
||||||
//Add tasks to wdt
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_add(handle_0) == ESP_OK, "Add failed");
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_add(handle_1) == ESP_OK, "Add failed");
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
|
|
||||||
//Adding Idles. Idle hook will call feed automatically
|
|
||||||
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
|
|
||||||
ASSERT_PRINT_ERROR(idle_0 != NULL, "Get Idle handle failed");
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_add(idle_0) == ESP_OK, "Add failed");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
|
||||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
|
|
||||||
ASSERT_PRINT_ERROR(idle_1 != NULL, "Get Idle handle failed");
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_add(idle_1) == ESP_OK, "Add failed");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//Wait 10 seconds
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
|
||||||
|
|
||||||
//Remove tasks form wdt
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_delete(handle_0) == ESP_OK, "Failed to delete");
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_delete(handle_1) == ESP_OK, "Failed to delete");
|
|
||||||
//Delete tasks
|
|
||||||
vTaskDelete(handle_0);
|
|
||||||
vTaskDelete(handle_1);
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_delete(idle_0) == ESP_OK, "Failed to delete");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_delete(idle_1) == ESP_OK, "Failed to delete");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
|
|
||||||
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
|
||||||
//Deinit task watchdog. Can only do so once all tasks (including idle) are deleted from wdt
|
|
||||||
ASSERT_PRINT_ERROR(esp_task_wdt_deinit() == ESP_OK, "deinit failed");
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
printf("Complete\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in a new issue