mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/update_intr_wdt_for_freertos_smp' into 'master'
esp_system: Fix SMP FreeRTOS INT WDT and tidy up Closes IDF-4749 See merge request espressif/esp-idf!18000
This commit is contained in:
commit
a4c4d8c59b
@ -1,67 +0,0 @@
|
||||
// Copyright 2015-2018 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.
|
||||
|
||||
#ifndef __ESP_INT_WDT_H
|
||||
#define __ESP_INT_WDT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @addtogroup Watchdog_APIs
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
This routine enables a watchdog to catch instances of processes disabling
|
||||
interrupts for too long, or code within interrupt handlers taking too long.
|
||||
It does this by setting up a watchdog which gets fed from the FreeRTOS
|
||||
task switch interrupt. When this watchdog times out, initially it will call
|
||||
a high-level interrupt routine that will panic FreeRTOS in order to allow
|
||||
for forensic examination of the state of the both CPUs. When this interrupt
|
||||
handler is not called and the watchdog times out a second time, it will
|
||||
reset the SoC.
|
||||
|
||||
This uses the TIMERG1 WDT.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the non-CPU-specific parts of interrupt watchdog.
|
||||
* This is called in the init code if the interrupt watchdog
|
||||
* is enabled in menuconfig.
|
||||
*
|
||||
*/
|
||||
void esp_int_wdt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Enable the interrupt watchdog on the current CPU. This is called
|
||||
* in the init code by both CPUs if the interrupt watchdog is enabled
|
||||
* in menuconfig.
|
||||
*
|
||||
*/
|
||||
void esp_int_wdt_cpu_init(void);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
33
components/esp_system/include/esp_private/esp_int_wdt.h
Normal file
33
components/esp_system/include/esp_private/esp_int_wdt.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the non-CPU-specific parts of interrupt watchdog.
|
||||
*
|
||||
* This function is automatically called during application startup if the
|
||||
* interrupt watchdog is enabled in menuconfig.
|
||||
*/
|
||||
void esp_int_wdt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Enable the interrupt watchdog on the current CPU.
|
||||
*
|
||||
* This function is automatically called during application startup for each CPU
|
||||
* that has enabled the interrupt watchdog in menuconfig.
|
||||
*
|
||||
* @note esp_int_wdt_init() must be called first before calling this function
|
||||
*/
|
||||
void esp_int_wdt_cpu_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -6,35 +6,31 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.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_log.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "soc/timer_periph.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_private/system_internal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/cpu_hal.h"
|
||||
#include "hal/timer_types.h"
|
||||
#include "hal/wdt_hal.h"
|
||||
#include "hal/interrupt_controller_hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include "esp_private/system_internal.h"
|
||||
|
||||
#if CONFIG_ESP_INT_WDT
|
||||
|
||||
#define WDT_INT_NUM ETS_T1_WDT_INUM
|
||||
|
||||
#define IWDT_INSTANCE WDT_MWDT1
|
||||
#define IWDT_PRESCALER MWDT1_TICK_PRESCALER //Tick period of 500us if WDT source clock is 80MHz
|
||||
#define IWDT_PRESCALER MWDT1_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
|
||||
#define IWDT_TICKS_PER_US MWDT1_TICKS_PER_US
|
||||
#define IWDT_INITIAL_TIMEOUT_S 5
|
||||
|
||||
static wdt_hal_context_t iwdt_context;
|
||||
|
||||
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
@ -43,83 +39,74 @@ static wdt_hal_context_t iwdt_context;
|
||||
* identify the live lock.
|
||||
*/
|
||||
#define IWDT_LIVELOCK_TIMEOUT_MS (20)
|
||||
|
||||
extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max;
|
||||
#endif
|
||||
|
||||
//Take care: the tick hook can also be called before esp_int_wdt_init() is called.
|
||||
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
//Not static; the ISR assembly checks this.
|
||||
bool int_wdt_app_cpu_ticked = false;
|
||||
volatile bool int_wdt_cpu1_ticked = false;
|
||||
#endif
|
||||
|
||||
static void IRAM_ATTR tick_hook(void)
|
||||
{
|
||||
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
if (cpu_hal_get_core_id() != 0) {
|
||||
int_wdt_app_cpu_ticked = true;
|
||||
int_wdt_cpu1_ticked = true;
|
||||
} else {
|
||||
//Only feed wdt if app cpu also ticked.
|
||||
if (int_wdt_app_cpu_ticked) {
|
||||
//Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
|
||||
// Only feed wdt if app cpu also ticked.
|
||||
if (int_wdt_cpu1_ticked) {
|
||||
// Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
|
||||
wdt_hal_write_protect_disable(&iwdt_context);
|
||||
//Reconfigure stage timeouts
|
||||
// Reconfigure stage timeouts
|
||||
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
_lx_intr_livelock_counter = 0;
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0,
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT); //Set timeout before interrupt
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT); // Set timeout before interrupt
|
||||
#else
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); //Set timeout before interrupt
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt
|
||||
#endif
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); //Set timeout before reset
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset
|
||||
wdt_hal_feed(&iwdt_context);
|
||||
wdt_hal_write_protect_enable(&iwdt_context);
|
||||
int_wdt_app_cpu_ticked = false;
|
||||
int_wdt_cpu1_ticked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void IRAM_ATTR tick_hook(void)
|
||||
{
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#else // CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
if (cpu_hal_get_core_id() != 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
//Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
|
||||
} else {
|
||||
// Todo: Check if there's a way to avoid reconfiguring the stages on each feed.
|
||||
wdt_hal_write_protect_disable(&iwdt_context);
|
||||
//Reconfigure stage timeouts
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); //Set timeout before interrupt
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); //Set timeout before reset
|
||||
// Reconfigure stage timeouts
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT); // Set timeout before interrupt
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, 2 * CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM); // Set timeout before reset
|
||||
wdt_hal_feed(&iwdt_context);
|
||||
wdt_hal_write_protect_enable(&iwdt_context);
|
||||
}
|
||||
#endif // CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void esp_int_wdt_init(void)
|
||||
{
|
||||
periph_module_enable(PERIPH_TIMG1_MODULE);
|
||||
//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.
|
||||
/*
|
||||
* Initialize the WDT timeout stages. Note that the initial timeout is set to 5 seconds as variable startup times of
|
||||
* each CPU can lead to a timeout. The tick hooks will set the WDT timers to the actual timeout.
|
||||
* Todo: Fix this
|
||||
*/
|
||||
wdt_hal_init(&iwdt_context, IWDT_INSTANCE, IWDT_PRESCALER, true);
|
||||
wdt_hal_write_protect_disable(&iwdt_context);
|
||||
//1st stage timeout: interrupt
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT);
|
||||
//2nd stage timeout: reset system
|
||||
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM);
|
||||
//Enable WDT
|
||||
wdt_hal_enable(&iwdt_context);
|
||||
wdt_hal_write_protect_enable(&iwdt_context);
|
||||
|
||||
|
||||
#if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
|
||||
#define APB_DCRSET (0x200c)
|
||||
#define APB_ITCTRL (0x3f00)
|
||||
#define ERI_ADDR(APB) (0x100000 + (APB))
|
||||
#define _SYM2STR(x) # x
|
||||
#define SYM2STR(x) _SYM2STR(x)
|
||||
|
||||
#define APB_DCRSET (0x200c)
|
||||
#define APB_ITCTRL (0x3f00)
|
||||
|
||||
#define ERI_ADDR(APB) (0x100000 + (APB))
|
||||
|
||||
#define _SYM2STR(x) # x
|
||||
#define SYM2STR(x) _SYM2STR(x)
|
||||
uint32_t eriadrs, scratch = 0, immediate = 0;
|
||||
if (soc_has_cache_lock_bug()) {
|
||||
if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) {
|
||||
@ -140,25 +127,25 @@ void esp_int_wdt_init(void)
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
|
||||
}
|
||||
|
||||
void esp_int_wdt_cpu_init(void)
|
||||
{
|
||||
assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to meet twice the RTOS tick period!");
|
||||
esp_register_freertos_tick_hook_for_cpu(tick_hook, cpu_hal_get_core_id());
|
||||
ESP_INTR_DISABLE(WDT_INT_NUM);
|
||||
|
||||
#if SOC_TIMER_GROUPS > 1
|
||||
assert((CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (portTICK_PERIOD_MS << 1)) && "Interrupt watchdog timeout needs to be at least twice the RTOS tick period!");
|
||||
// Register tick hook for current CPU to feed the INT WDT
|
||||
esp_register_freertos_tick_hook_for_cpu(tick_hook, cpu_hal_get_core_id());
|
||||
/*
|
||||
* Register INT WDT interrupt for current CPU. We do this manually as the timeout interrupt should call an assembly
|
||||
* panic handler (see riscv/vector.S and xtensa_vectors.S).
|
||||
*/
|
||||
esp_intr_disable_source(WDT_INT_NUM);
|
||||
esp_rom_route_intr_matrix(cpu_hal_get_core_id(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
|
||||
#endif
|
||||
|
||||
/* Set the type and priority to watch dog interrupts */
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
interrupt_controller_hal_set_int_type(WDT_INT_NUM, INTR_TYPE_LEVEL);
|
||||
interrupt_controller_hal_set_int_level(WDT_INT_NUM, SOC_INTERRUPT_LEVEL_MEDIUM);
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
/*
|
||||
* This is a workaround for issue 3.15 in "ESP32 ECO and workarounds for
|
||||
@ -171,11 +158,11 @@ void esp_int_wdt_cpu_init(void)
|
||||
_lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We do not register a handler for the watchdog interrupt because:
|
||||
// 1. Interrupt level 4 on Xtensa architecture is not servicable from C
|
||||
// 2. Instead, we set the entry of watchdog interrupt to the panic handler, see riscv/vector.S and xtensa_vectors.S
|
||||
ESP_INTR_ENABLE(WDT_INT_NUM);
|
||||
esp_intr_enable_source(WDT_INT_NUM);
|
||||
#else // SOC_TIMER_GROUPS > 1
|
||||
// TODO: Clean up code for ESP32-C2, IDF-4114
|
||||
ESP_EARLY_LOGW("INT_WDT", "ESP32-C2 only has one timer group");
|
||||
#endif // SOC_TIMER_GROUPS > 1
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // CONFIG_ESP_INT_WDT
|
||||
|
@ -259,7 +259,7 @@ xt_highintx:
|
||||
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
|
||||
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
/* Check if the cause is the app cpu failing to tick.*/
|
||||
movi a0, int_wdt_app_cpu_ticked
|
||||
movi a0, int_wdt_cpu1_ticked
|
||||
l32i a0, a0, 0
|
||||
bnez a0, 2f
|
||||
/* It is. Modify cause. */
|
||||
|
@ -1,16 +1,8 @@
|
||||
// Copyright 2015-2020 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
|
||||
*/
|
||||
|
||||
|
||||
#include <xtensa/coreasm.h>
|
||||
@ -92,7 +84,7 @@ xt_highint4:
|
||||
1:
|
||||
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
|
||||
/* Check if the cause is the app cpu failing to tick.*/
|
||||
movi a0, int_wdt_app_cpu_ticked
|
||||
movi a0, int_wdt_cpu1_ticked
|
||||
l32i a0, a0, 0
|
||||
bnez a0, 2f
|
||||
/* It is. Modify cause. */
|
||||
|
@ -198,7 +198,11 @@ This file get's pulled into assembly sources. Therefore, some includes need to b
|
||||
#else
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
#endif
|
||||
#if CONFIG_FREERTOS_USE_TICK_HOOK
|
||||
#define configUSE_TICK_HOOK 1
|
||||
#else
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#endif
|
||||
#if CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE
|
||||
#define configCHECK_FOR_STACK_OVERFLOW 0
|
||||
#elif CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL
|
||||
|
@ -17,17 +17,17 @@
|
||||
#include "xtensa/config/core.h"
|
||||
#include "xtensa/config/core-isa.h"
|
||||
#include "xtensa/xtruntime.h"
|
||||
#include "esp_private/startup_internal.h" /* Required by g_spiram_ok. [refactor-todo] for g_spiram_ok */
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_heap_caps_init.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "esp_private/startup_internal.h" /* Required by g_spiram_ok. [refactor-todo] for g_spiram_ok */
|
||||
#include "esp32/spiram.h" /* Required by esp_spiram_reserve_dma_pool() */
|
||||
#ifdef CONFIG_APPTRACE_ENABLE
|
||||
#include "esp_app_trace.h"
|
||||
@ -620,15 +620,14 @@ BaseType_t xPortSysTickHandler(void)
|
||||
portbenchmarkIntLatency();
|
||||
traceISR_ENTER(SYSTICK_INTR_ID);
|
||||
BaseType_t ret;
|
||||
esp_vApplicationTickHook();
|
||||
if (portGET_CORE_ID() == 0) {
|
||||
//Only Core 0 calls xTaskIncrementTick();
|
||||
// FreeRTOS SMP requires that only core 0 calls xTaskIncrementTick()
|
||||
ret = xTaskIncrementTick();
|
||||
} else {
|
||||
//Manually call the IDF tick hooks
|
||||
esp_vApplicationTickHook();
|
||||
ret = pdFALSE;
|
||||
}
|
||||
if(ret != pdFALSE) {
|
||||
if (ret != pdFALSE) {
|
||||
portYIELD_FROM_ISR();
|
||||
} else {
|
||||
traceISR_EXIT();
|
||||
@ -657,13 +656,6 @@ void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, c
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ( configUSE_TICK_HOOK > 0 )
|
||||
void vApplicationTickHook( void )
|
||||
{
|
||||
esp_vApplicationTickHook();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK
|
||||
/*
|
||||
By default, the port uses vApplicationMinimalIdleHook() to run IDF style idle
|
||||
|
@ -8,9 +8,9 @@
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "portmacro.h"
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_heap_caps_init.h"
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_task.h"
|
||||
#include "esp_private/crosscore_int.h"
|
||||
|
@ -65,9 +65,9 @@
|
||||
#include <xtensa/xtensa_context.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_private/crosscore_int.h"
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_int_wdt.h"
|
||||
#ifdef CONFIG_APPTRACE_ENABLE
|
||||
#include "esp_app_trace.h" /* Required for esp_apptrace_init. [refactor-todo] */
|
||||
#endif
|
||||
|
@ -126,7 +126,6 @@ INPUT = \
|
||||
$(PROJECT_PATH)/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_expression_with_stack.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_freertos_hooks.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_int_wdt.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_system.h \
|
||||
$(PROJECT_PATH)/components/esp_system/include/esp_task_wdt.h \
|
||||
$(PROJECT_PATH)/components/esp_timer/include/esp_timer.h \
|
||||
|
@ -6,24 +6,44 @@ Overview
|
||||
|
||||
The ESP-IDF has support for multiple types of watchdogs, with the two main ones being: The Interrupt Watchdog Timer and the Task Watchdog Timer (TWDT). The Interrupt Watchdog Timer and the TWDT can both be enabled using :ref:`project-configuration-menu`, however the TWDT can also be enabled during runtime. The Interrupt Watchdog is responsible for detecting instances where FreeRTOS task switching is blocked for a prolonged period of time. The TWDT is responsible for detecting instances of tasks running without yielding for a prolonged period.
|
||||
|
||||
Interrupt watchdog
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
ESP-IDF has support for the following types of watchdog timers:
|
||||
|
||||
The interrupt watchdog makes sure the FreeRTOS task switching interrupt isn't blocked for a long time. This is bad because no other tasks, including potentially important ones like the WiFi task and the idle task, can't get any CPU runtime. A blocked task switching interrupt can happen because a program runs into an infinite loop with interrupts disabled or hangs in an interrupt.
|
||||
.. list::
|
||||
|
||||
The default action of the interrupt watchdog is to invoke the panic handler causing a register dump and an opportunity for the programmer to find out, using either OpenOCD or gdbstub, what bit of code is stuck with interrupts disabled. Depending on the configuration of the panic handler, it can also blindly reset the CPU, which may be preferred in a production environment.
|
||||
- Interrupt Watchdog Timer (IWDT)
|
||||
- Task Watchdog Timer (TWDT)
|
||||
:SOC_XT_WDT_SUPPORTED: - Crystal 32K Watchdog Timer (XTWDT)
|
||||
|
||||
The interrupt watchdog is built around the hardware watchdog in timer group 1. If this watchdog for some reason cannot execute the NMI handler that invokes the panic handler (e.g. because IRAM is overwritten by garbage), it will hard-reset the SOC. If the panic handler executes, it will display the panic reason as "Interrupt wdt timeout on CPU0" or "Interrupt wdt timeout on CPU1" (as applicable).
|
||||
The various watchdog timers can be enabled using the :ref:`project-configuration-menu`. However, the TWDT can also be enabled during runtime.
|
||||
|
||||
Interrupt Watchdog Timer (IWDT)
|
||||
-------------------------------
|
||||
|
||||
The purpose of the IWDT is to ensure that interrupt service routines (ISRs) are not blocked from running for a prolonged period of time (i.e., the IWDT timeout period). Blocking ISRs from running in a timely manner is undesirable as it can increases ISR latency, and also prevents task switching (as task switching is executed form an ISR). The things that can block ISRs from running include:
|
||||
|
||||
- Disabling interrupts
|
||||
- Critical Sections (also disables interrupts)
|
||||
- Other same/higher priority ISRs (will block same/lower priority ISRs from running it completes execution)
|
||||
|
||||
The IWDT utilizes the watchdog timer in Timer Group 1 as its underlying hardware timer and leverages the FreeRTOS tick interrupt on each CPU to feed the watchdog timer. If the tick interrupt on a particular CPU is not run at within the IWDT timeout period, it is indicative that something is blocking ISRs from being run on that CPU (see the list of reasons above).
|
||||
|
||||
When the IWDT times out, the default action is to invoke the panic handler and display the panic reason as ``Interrupt wdt timeout on CPU0`` or ``Interrupt wdt timeout on CPU1`` (as applicable). Depending on the panic handler's configured behavior (see :ref:`CONFIG_ESP_SYSTEM_PANIC`), users can then debug the source of the IWDT timeout (via the backtrace, OpenOCD, gdbstub etc) or simply reset the chip (which may be preferred in a production environment).
|
||||
|
||||
If for whatever reason the panic handler is unable to run after an IWDT timeout, the IWDT has a secondary timeout that will hard-reset the chip (i.e., a system reset).
|
||||
|
||||
Configuration
|
||||
"""""""""""""
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The interrupt watchdog is enabled by default via the :ref:`CONFIG_ESP_INT_WDT` configuration flag. The timeout is configured by setting :ref:`CONFIG_ESP_INT_WDT_TIMEOUT_MS`. The default timeout is higher if PSRAM support is enabled, as a critical section or interrupt routine that accesses a large amount of PSRAM will take longer to complete in some circumstances. The INT WDT timeout should always be longer than the period between FreeRTOS ticks (see :ref:`CONFIG_FREERTOS_HZ`).
|
||||
- The IWDT is enabled by default via the :ref:`CONFIG_ESP_INT_WDT` option.
|
||||
- The IWDT's timeout is configured by setting the :ref:`CONFIG_ESP_INT_WDT_TIMEOUT_MS` option.
|
||||
|
||||
- Note that the default timeout is higher if PSRAM support is enabled, as a critical section or interrupt routine that accesses a large amount of PSRAM will take longer to complete in some circumstances.
|
||||
- The timeout should always at least twice longer than the period between FreeRTOS ticks (see :ref:`CONFIG_FREERTOS_HZ`).
|
||||
|
||||
Tuning
|
||||
""""""
|
||||
^^^^^^
|
||||
|
||||
If you find the Interrupt watchdog timeout is triggered because an interrupt or critical section is running longer than the timeout period, consider rewriting the code:
|
||||
If you find the IWDT timeout is triggered because an interrupt or critical section is running longer than the timeout period, consider rewriting the code:
|
||||
|
||||
- Critical sections should be made as short as possible. Any non-critical code/computation should be placed outside the critical section.
|
||||
- Interrupt handlers should also perform the minimum possible amount of computation. Users can consider deferring any computation to a task by having the ISR push data to a task using queues.
|
||||
@ -32,8 +52,8 @@ Neither critical sections or interrupt handlers should ever block waiting for an
|
||||
|
||||
.. _task-watchdog-timer:
|
||||
|
||||
Task Watchdog Timer
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Task Watchdog Timer (TWDT)
|
||||
--------------------------
|
||||
|
||||
{IDF_TARGET_IDLE_TASKS:default="Idle task", esp32="Idle Tasks of each CPU"}
|
||||
|
||||
@ -42,7 +62,7 @@ The Task Watchdog Timer (TWDT) is used to monitor particular tasks, ensuring tha
|
||||
The TWDT is built around the Hardware Watchdog Timer in Timer Group 0. When a timeout occurs, an interrupt is triggered. Users can redefine the function `esp_task_wdt_isr_user_handler` in the user code, in order to receive the timeout event and handle it differently.
|
||||
|
||||
Usage
|
||||
"""""
|
||||
^^^^^
|
||||
|
||||
The following functions can be used to watch tasks using the TWDT:
|
||||
|
||||
@ -60,7 +80,7 @@ In the case where applications need to watch at a more granular level (i.e., ens
|
||||
- :cpp:func:`esp_task_wdt_delete_user` unsubscribes an arbitrary user of the TWDT.
|
||||
|
||||
Configuration
|
||||
"""""""""""""
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The default timeout period for the TWDT is set using config item :ref:`CONFIG_ESP_TASK_WDT_TIMEOUT_S`. This should be set to at least as long as you expect any single task will need to monopolize the CPU (for example, if you expect the app will do a long intensive calculation and should not yield to other tasks). It is also possible to change this timeout at runtime by calling :cpp:func:`esp_task_wdt_init`.
|
||||
|
||||
@ -83,47 +103,36 @@ The following config options control TWDT configuration at startup. They are all
|
||||
- :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0` - {IDF_TARGET_IDLE_TASK} is subscribed to the TWDT during startup. If this option is disabled, it is still possible to subscribe the idle task by calling :cpp:func:`esp_task_wdt_init` again.
|
||||
:not CONFIG_FREERTOS_UNICORE: - :ref:`CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1` - CPU1 Idle task is subscribed to the TWDT during startup.
|
||||
|
||||
|
||||
JTAG and watchdogs
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
While debugging using OpenOCD, the CPUs will be halted every time a breakpoint is reached. However if the watchdog timers continue to run when a breakpoint is encountered, they will eventually trigger a reset making it very difficult to debug code. Therefore OpenOCD will disable the hardware timers of both the interrupt and task watchdogs at every breakpoint. Moreover, OpenOCD will not reenable them upon leaving the breakpoint. This means that interrupt watchdog and task watchdog functionality will essentially be disabled. No warnings or panics from either watchdogs will be generated when the {IDF_TARGET_NAME} is connected to OpenOCD via JTAG.
|
||||
|
||||
|
||||
.. only:: SOC_XT_WDT_SUPPORTED
|
||||
|
||||
XTAL32K Watchdog Timer (XTWDT)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
------------------------------
|
||||
|
||||
The XTAL32K watchdog makes sure the (optional) external 32 KHz crystal or oscillator is functioning correctly.
|
||||
One of the optional clock inputs to the {IDF_TARGET_NAME} is an external 32 KHz crystal or oscillator (XTAL32K) that is used as a clock source (``XTAL32K_CLK``) to various subsystems (such as the RTC).
|
||||
|
||||
When `XTAL32K_CLK` works as the clock source of `RTC_SLOW_CLK` and stops oscillating, the XTAL32K watchdog timer will detect this and generate an interrupt. It also provides functionality for automatically switching over to the internal, but less accurate oscillator as the `RTC_SLOW_CLK` source.
|
||||
The XTWDT is a dedicated watchdog timer used to ensure that the XTAL32K is functioning correctly. When ``XTAL32K_CLK`` works as the clock source of ``RTC_SLOW_CLK`` and stops oscillating, the XTWDT will detect this and generate an interrupt. It also provides functionality for automatically switching over to the internal, but less accurate oscillator as the `RTC_SLOW_CLK` source.
|
||||
|
||||
Since the switch to the backup clock is done in hardware it can also happen during deep sleep. This means that even if `XTAL32K_CLK` stops functioning while the chip in deep sleep, waiting for a timer to expire, it will still be able to wake-up as planned.
|
||||
Since the switch to the backup clock is done in hardware it can also happen during deep sleep. This means that even if ``XTAL32K_CLK`` stops functioning while the chip in deep sleep, waiting for a timer to expire, it will still be able to wake-up as planned.
|
||||
|
||||
If the `XTAL32K_CLK` starts functioning normally again, you can call `esp_xt_wdt_restore_clk` to switch back to this clock source and re-enable the watchdog timer.
|
||||
If the ``XTAL32K_CLK`` starts functioning normally again, you can call ``esp_xt_wdt_restore_clk`` to switch back to this clock source and re-enable the watchdog timer.
|
||||
|
||||
Configuration
|
||||
"""""""""""""
|
||||
|
||||
When the external 32KHz crystal or oscillator is selected (:ref:`CONFIG_RTC_CLK_SRC`) the XTAL32K watchdog can be enabled via the :ref:`CONFIG_ESP_XT_WDT` configuration flag. The timeout is configured by setting :ref:`CONFIG_ESP_XT_WDT_TIMEOUT`. The automatic backup clock functionality is enabled via the ref:`CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE` configuration.
|
||||
- When the external 32KHz crystal or oscillator is selected (:ref:`CONFIG_RTC_CLK_SRC`) the XTWDT can be enabled via the :ref:`CONFIG_ESP_XT_WDT` configuration option.
|
||||
- The timeout is configured by setting the :ref:`CONFIG_ESP_XT_WDT_TIMEOUT` option.
|
||||
- The automatic backup clock functionality is enabled via the ref:`CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE` configuration option.
|
||||
|
||||
Interrupt Watchdog API Reference
|
||||
--------------------------------
|
||||
JTAG & Watchdogs
|
||||
----------------
|
||||
|
||||
Header File
|
||||
^^^^^^^^^^^
|
||||
While debugging using OpenOCD, the CPUs will be halted every time a breakpoint is reached. However if the watchdog timers continue to run when a breakpoint is encountered, they will eventually trigger a reset making it very difficult to debug code. Therefore OpenOCD will disable the hardware timers of both the interrupt and task watchdogs at every breakpoint. Moreover, OpenOCD will not reenable them upon leaving the breakpoint. This means that interrupt watchdog and task watchdog functionality will essentially be disabled. No warnings or panics from either watchdogs will be generated when the {IDF_TARGET_NAME} is connected to OpenOCD via JTAG.
|
||||
|
||||
* :component_file:`esp_system/include/esp_int_wdt.h`
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. doxygenfunction:: esp_int_wdt_init
|
||||
|
||||
Task Watchdog API Reference
|
||||
----------------------------
|
||||
Task Watchdog
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
A full example using the Task Watchdog is available in esp-idf: :example:`system/task_watchdog`
|
||||
|
||||
|
@ -60,6 +60,7 @@ ESP System
|
||||
----------
|
||||
- The header files ``esp_random.h``, ``esp_mac.h`` and ``esp_chip_info.h``, which were all previously indirectly included via the header file ``esp_system.h``, must now be included directly. These headers are removed from ``esp_system.h``.
|
||||
- The header file ``eh_frame_parser.h`` must now be included with a ``esp_private`` prefix like ``#include "esp_private/eh_frame_parser.h"``.
|
||||
- The header file ``esp_int_wdt.h`` must now be included with a ``esp_private`` prefix like ``#include "esp_private/esp_int_wdt.h"``.
|
||||
|
||||
SOC dependency
|
||||
--------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user