mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-04 20:05:25 +02:00
WDT: Add LL and HAL for watchdog timers
This commit updates the watchdog timers (MWDT and RWDT) in the following ways: - Add seprate LL for MWDT and RWDT. - Add a combined WDT HAL for all Watchdog Timers - Update int_wdt.c and task_wdt.c to use WDT HAL - Remove most dependencies on LL or direct register access in other components. They will now use the WDT HAL - Update use of watchdogs (including RTC WDT) in bootloader and startup code to use the HAL layer.
This commit is contained in:
@@ -17,15 +17,13 @@ else()
|
||||
"crosscore_int.c"
|
||||
"dport_access.c"
|
||||
"hw_random.c"
|
||||
"int_wdt.c"
|
||||
"intr_alloc.c"
|
||||
"pm_esp32s2.c"
|
||||
"pm_trace.c"
|
||||
"sleep_modes.c"
|
||||
"spiram.c"
|
||||
"spiram_psram.c"
|
||||
"system_api_esp32s2.c"
|
||||
"task_wdt.c")
|
||||
"system_api_esp32s2.c")
|
||||
|
||||
set(include_dirs "include")
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include "soc/dport_access.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_wdt.h"
|
||||
#include "soc/rtc_periph.h"
|
||||
#include "soc/i2s_reg.h"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
#include "bootloader_clock.h"
|
||||
@@ -85,10 +85,13 @@ void esp_clk_init(void)
|
||||
// Therefore, for the time of frequency change, set a new lower timeout value (1.6 sec).
|
||||
// This prevents excessive delay before resetting in case the supply voltage is drawdown.
|
||||
// (If frequency is changed from 90kHz to 32kHz then WDT timeout will increased to 1.6sec * 90/32 = 4.5 sec).
|
||||
rtc_wdt_protect_off();
|
||||
rtc_wdt_feed();
|
||||
rtc_wdt_set_time(RTC_WDT_STAGE0, 1600);
|
||||
rtc_wdt_protect_on();
|
||||
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
|
||||
uint32_t stage_timeout_ticks = (uint32_t)(1600ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_feed(&rtc_wdt_ctx);
|
||||
//Bootloader has enabled RTC WDT until now. We're only modifying timeout, so keep the stage and timeout action the same
|
||||
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS)
|
||||
@@ -103,10 +106,11 @@ void esp_clk_init(void)
|
||||
|
||||
#ifdef CONFIG_BOOTLOADER_WDT_ENABLE
|
||||
// After changing a frequency WDT timeout needs to be set for new frequency.
|
||||
rtc_wdt_protect_off();
|
||||
rtc_wdt_feed();
|
||||
rtc_wdt_set_time(RTC_WDT_STAGE0, CONFIG_BOOTLOADER_WDT_TIME_MS);
|
||||
rtc_wdt_protect_on();
|
||||
stage_timeout_ticks = (uint32_t)((uint64_t)CONFIG_BOOTLOADER_WDT_TIME_MS * rtc_clk_slow_freq_get_hz() / 1000ULL);
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_feed(&rtc_wdt_ctx);
|
||||
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
#endif
|
||||
|
||||
rtc_cpu_freq_config_t old_config, new_config;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/rtc_wdt.h"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "driver/rtc_io.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -122,7 +122,10 @@ void IRAM_ATTR call_start_cpu0(void)
|
||||
// from panic handler we can be reset by RWDT or TG0WDT
|
||||
if (rst_reas == RTCWDT_SYS_RESET || rst_reas == TG0WDT_SYS_RESET) {
|
||||
#ifndef CONFIG_BOOTLOADER_WDT_ENABLE
|
||||
rtc_wdt_disable();
|
||||
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -397,7 +400,10 @@ static void main_task(void *args)
|
||||
|
||||
// Now that the application is about to start, disable boot watchdog
|
||||
#ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE
|
||||
rtc_wdt_disable();
|
||||
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BOOTLOADER_EFUSE_SECURE_VERSION_EMULATE
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "driver/timer.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "esp_int_wdt.h"
|
||||
|
||||
#if CONFIG_ESP_INT_WDT
|
||||
|
||||
|
||||
#define WDT_INT_NUM 24
|
||||
|
||||
|
||||
//Take care: the tick hook can also be called before esp_int_wdt_init() is called.
|
||||
|
||||
static void IRAM_ATTR tick_hook(void)
|
||||
{
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config2=CONFIG_ESP_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_ESP_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
|
||||
void esp_int_wdt_init(void)
|
||||
{
|
||||
periph_module_enable(PERIPH_TIMG1_MODULE);
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.level_int_en=1;
|
||||
TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
|
||||
TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
|
||||
TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
//The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets
|
||||
//it to their actual value.
|
||||
TIMERG1.wdt_config2=10000;
|
||||
TIMERG1.wdt_config3=10000;
|
||||
TIMERG1.wdt_config0.en=1;
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
TIMERG1.int_clr.wdt=1;
|
||||
timer_group_intr_enable(TIMER_GROUP_1, TIMG_WDT_INT_ENA_M);
|
||||
}
|
||||
|
||||
void esp_int_wdt_cpu_init(void)
|
||||
{
|
||||
esp_register_freertos_tick_hook_for_cpu(tick_hook, 0);
|
||||
ESP_INTR_DISABLE(WDT_INT_NUM);
|
||||
intr_matrix_set(0, ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
|
||||
//We do not register a handler for the interrupt because it is interrupt level 4 which
|
||||
//is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
|
||||
//this interrupt.
|
||||
ESP_INTR_ENABLE(WDT_INT_NUM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // CONFIG_ESP_INT_WDT
|
||||
@@ -31,9 +31,9 @@
|
||||
#include "soc/spi_periph.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/extmem_reg.h"
|
||||
#include "soc/rtc_wdt.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#include "soc/uart_caps.h"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -301,16 +301,15 @@ esp_err_t esp_light_sleep_start(void)
|
||||
rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config();
|
||||
|
||||
// Safety net: enable WDT in case exit from light sleep fails
|
||||
bool wdt_was_enabled = rtc_wdt_is_on(); // If WDT was enabled in the user code, then do not change it here.
|
||||
wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
|
||||
bool wdt_was_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx); // If WDT was enabled in the user code, then do not change it here.
|
||||
if (!wdt_was_enabled) {
|
||||
rtc_wdt_protect_off();
|
||||
rtc_wdt_disable();
|
||||
rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us);
|
||||
rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us);
|
||||
rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_RTC);
|
||||
rtc_wdt_set_time(RTC_WDT_STAGE0, 1000);
|
||||
rtc_wdt_enable();
|
||||
rtc_wdt_protect_on();
|
||||
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
|
||||
uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
|
||||
wdt_hal_enable(&rtc_wdt_ctx);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
}
|
||||
|
||||
// Enter sleep, then wait for flash to be ready on wakeup
|
||||
@@ -340,7 +339,9 @@ esp_err_t esp_light_sleep_start(void)
|
||||
esp_timer_private_unlock();
|
||||
DPORT_STALL_OTHER_CPU_END();
|
||||
if (!wdt_was_enabled) {
|
||||
rtc_wdt_disable();
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
}
|
||||
portEXIT_CRITICAL(&light_sleep_lock);
|
||||
return err;
|
||||
|
||||
@@ -25,11 +25,10 @@
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_wdt.h"
|
||||
#include "soc/syscon_reg.h"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
|
||||
/* "inner" restart function for after RTOS, interrupts & anything else on this
|
||||
@@ -42,27 +41,33 @@ void IRAM_ATTR esp_restart_noos(void)
|
||||
xt_ints_off(0xFFFFFFFF);
|
||||
|
||||
// Enable RTC watchdog for 1 second
|
||||
rtc_wdt_protect_off();
|
||||
rtc_wdt_disable();
|
||||
rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_RTC);
|
||||
rtc_wdt_set_stage(RTC_WDT_STAGE1, RTC_WDT_STAGE_ACTION_RESET_SYSTEM);
|
||||
rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_200ns);
|
||||
rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_200ns);
|
||||
rtc_wdt_set_time(RTC_WDT_STAGE0, 1000);
|
||||
rtc_wdt_flashboot_mode_enable();
|
||||
wdt_hal_context_t rtc_wdt_ctx;
|
||||
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
|
||||
uint32_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
|
||||
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
|
||||
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_SYSTEM);
|
||||
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE1, stage_timeout_ticks, WDT_STAGE_ACTION_RESET_RTC);
|
||||
//Enable flash boot mode so that flash booting after restart is protected by the RTC WDT.
|
||||
wdt_hal_set_flashboot_en(&rtc_wdt_ctx, true);
|
||||
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
|
||||
|
||||
// Reset and stall the other CPU.
|
||||
// CPU must be reset before stalling, in case it was running a s32c1i
|
||||
// instruction. This would cause memory pool to be locked by arbiter
|
||||
// to the stalled CPU, preventing current CPU from accessing this pool.
|
||||
const uint32_t core_id = xPortGetCoreID();
|
||||
|
||||
//Todo: Refactor to use Interrupt or Task Watchdog API, and a system level WDT context
|
||||
// Disable TG0/TG1 watchdogs
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
|
||||
wdt_hal_write_protect_disable(&wdt0_context);
|
||||
wdt_hal_disable(&wdt0_context);
|
||||
wdt_hal_write_protect_enable(&wdt0_context);
|
||||
|
||||
wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
|
||||
wdt_hal_write_protect_disable(&wdt1_context);
|
||||
wdt_hal_disable(&wdt1_context);
|
||||
wdt_hal_write_protect_enable(&wdt1_context);
|
||||
|
||||
// Flush any data left in UART FIFOs
|
||||
uart_tx_wait_idle(0);
|
||||
|
||||
@@ -1,428 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "soc/timer_periph.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/timer.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_private/system_internal.h"
|
||||
|
||||
static const char *TAG = "task_wdt";
|
||||
|
||||
//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
|
||||
|
||||
//Structure used for each subscribed task
|
||||
typedef struct twdt_task_t twdt_task_t;
|
||||
struct twdt_task_t {
|
||||
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
|
||||
intr_handle_t intr_handle;
|
||||
};
|
||||
|
||||
static twdt_config_t *twdt_config = NULL;
|
||||
static portMUX_TYPE twdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
/*
|
||||
* 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 bool idle_hook_cb(void)
|
||||
{
|
||||
esp_task_wdt_reset();
|
||||
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.
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
//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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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)
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
//Acknowledge interrupt
|
||||
TIMERG0.int_clr.wdt=1;
|
||||
//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.
|
||||
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");
|
||||
ESP_EARLY_LOGE(TAG, " - %s (%s)", pcTaskGetTaskName(twdttask->task_handle), cpu);
|
||||
}
|
||||
}
|
||||
ESP_EARLY_LOGE(TAG, "%s", DRAM_STR("Tasks currently running:"));
|
||||
for (int x=0; x<portNUM_PROCESSORS; x++) {
|
||||
ESP_EARLY_LOGE(TAG, "CPU %d: %s", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
|
||||
}
|
||||
|
||||
esp_task_wdt_isr_user_handler();
|
||||
if (twdt_config->panic){ //Trigger Panic if configured to do so
|
||||
ESP_EARLY_LOGE(TAG, "Aborting.");
|
||||
portEXIT_CRITICAL_ISR(&twdt_spinlock);
|
||||
esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
|
||||
abort();
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
|
||||
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
|
||||
periph_module_enable(PERIPH_TIMG0_MODULE);
|
||||
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=twdt_config->timeout*2000; //Set timeout before interrupt
|
||||
TIMERG0.wdt_config3=twdt_config->timeout*4000; //Set timeout before reset
|
||||
TIMERG0.wdt_config0.en=1;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0; //Enable write protection
|
||||
|
||||
}else{ //twdt_config previously initialized
|
||||
//Reconfigure task wdt
|
||||
twdt_config->panic = panic;
|
||||
twdt_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=twdt_config->timeout*2000; //Set timeout before interrupt
|
||||
TIMERG0.wdt_config3=twdt_config->timeout*4000; //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(&twdt_spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
//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_ERROR_CHECK(esp_intr_free(twdt_config->intr_handle)); //Unregister interrupt
|
||||
free(twdt_config); //Free twdt_config
|
||||
twdt_config = NULL;
|
||||
portEXIT_CRITICAL(&twdt_spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
//If 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
||||
twdt_task_t *target_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
|
||||
reset_hw_timer();
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&twdt_spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
esp_err_t esp_task_wdt_status(TaskHandle_t handle)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void esp_task_wdt_feed(void)
|
||||
{
|
||||
portENTER_CRITICAL(&twdt_spinlock);
|
||||
//Return immediately if TWDT has not been initialized
|
||||
ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), VOID_RETURN);
|
||||
|
||||
//Check if task is on list
|
||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
||||
bool all_reset;
|
||||
twdt_task_t *target_task = find_task_in_twdt_list(handle, &all_reset);
|
||||
|
||||
//reset the task if it's on the list, then return
|
||||
if(target_task != NULL){
|
||||
target_task->has_reset = true;
|
||||
if(all_reset){
|
||||
reset_hw_timer(); //Reset hardware timer if all other tasks have reset
|
||||
}
|
||||
portEXIT_CRITICAL(&twdt_spinlock);
|
||||
return;
|
||||
}
|
||||
|
||||
//Add task if it's has not on list
|
||||
target_task = calloc(1, sizeof(twdt_task_t));
|
||||
ASSERT_EXIT_CRIT_RETURN((target_task != NULL), VOID_RETURN); //If calloc failed
|
||||
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 wdt task list
|
||||
}
|
||||
task->next = target_task;
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL(&twdt_spinlock);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user