refactor(esp_system): ind_wdt.c refactor & code clean

1. introduce reconfigure_ticks function for wdt configuration
 2. move esp32eco3 cache livelock workaround code out from int_wdt.c
This commit is contained in:
wuzhenghui
2026-03-17 12:27:28 +08:00
parent 38f37e18af
commit 6f1bea6a5d
6 changed files with 217 additions and 85 deletions
@@ -1,15 +1,23 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define IWDT_INITIAL_TIMEOUT_S (5)
#define IWDT_TICKS_PER_US (500)
#define IWDT_STAGE0_TIMEOUT_US (CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US)
#define IWDT_STAGE1_TIMEOUT_US (2 * IWDT_STAGE0_TIMEOUT_US)
#define IWDT_INITIAL_TIMEOUT_US (IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US)
/**
* @brief Initialize the non-CPU-specific parts of interrupt watchdog.
*
@@ -28,6 +36,18 @@ void esp_int_wdt_init(void);
*/
void esp_int_wdt_cpu_init(void);
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/**
* @brief Reconfigure WDT stage timeouts (ticks).
*
* Low-level API used by the livelock workaround. Prefer esp_int_wdt_livelock_workaround() for sleep flows.
*
* @param stage0_ticks Stage0 timeout ticks
* @param stage1_ticks Stage1 timeout ticks
*/
void esp_int_wdt_reconfigure_ticks(uint32_t stage0_ticks, uint32_t stage1_ticks);
#endif
#ifdef __cplusplus
}
#endif
+46 -83
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -13,6 +13,7 @@
#include "hal/wdt_hal.h"
#include "soc/system_intr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_cpu.h"
#include "esp_check.h"
#include "esp_err.h"
@@ -28,14 +29,16 @@
#include "esp_private/sleep_retention.h"
#endif
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
#include "esp_private/eco3_livelock_workaround.h"
#endif
#if TIMG_LL_GET(INST_NUM) > 1
/* If we have two hardware timer groups, use the second one for interrupt watchdog. */
#define WDT_LEVEL_INTR_SOURCE SYS_TG1_WDT_INTR_SOURCE
#define IWDT_PRESCALER MWDT_LL_DEFAULT_CLK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US 500
#define IWDT_INSTANCE WDT_MWDT1
#define IWDT_INITIAL_TIMEOUT_S 5
#define IWDT_PERIPH PERIPH_TIMG1_MODULE
#define IWDT_TIMER_GROUP 1
@@ -43,9 +46,7 @@
#define WDT_LEVEL_INTR_SOURCE SYS_TG0_WDT_INTR_SOURCE
#define IWDT_PRESCALER MWDT_LL_DEFAULT_CLK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
#define IWDT_TICKS_PER_US 500
#define IWDT_INSTANCE WDT_MWDT0
#define IWDT_INITIAL_TIMEOUT_S 5
#define IWDT_PERIPH PERIPH_TIMG0_MODULE
#define IWDT_TIMER_GROUP 0
@@ -86,14 +87,24 @@ static esp_err_t esp_int_wdt_retention_enable(uint32_t group_id)
#endif
static wdt_hal_context_t iwdt_context;
static portMUX_TYPE s_iwdt_configure_lock = portMUX_INITIALIZER_UNLOCKED;
static void ESP_SYSTEM_IRAM_ATTR reconfigure_ticks(uint32_t stage0_ticks, uint32_t stage1_ticks)
{
portENTER_CRITICAL_SAFE(&s_iwdt_configure_lock);
wdt_hal_write_protect_disable(&iwdt_context);
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, stage0_ticks, WDT_STAGE_ACTION_INT);
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, stage1_ticks, WDT_STAGE_ACTION_RESET_SYSTEM);
wdt_hal_feed(&iwdt_context);
wdt_hal_write_protect_enable(&iwdt_context);
portEXIT_CRITICAL_SAFE(&s_iwdt_configure_lock);
}
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
* This parameter is used to indicate the response time of Interrupt watchdog to
* identify the live lock.
*/
#define IWDT_LIVELOCK_TIMEOUT_MS (20)
extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max;
void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_reconfigure_ticks(uint32_t stage0_ticks, uint32_t stage1_ticks)
{
reconfigure_ticks(stage0_ticks, stage1_ticks);
}
#endif
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
@@ -102,41 +113,25 @@ volatile bool int_wdt_cpu1_ticked = false;
static void ESP_SYSTEM_IRAM_ATTR tick_hook(void)
{
if (esp_cpu_get_core_id() != 0) {
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
if (esp_cpu_get_core_id() != 0) {
int_wdt_cpu1_ticked = true;
} else {
// 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
#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
#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
#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_feed(&iwdt_context);
wdt_hal_write_protect_enable(&iwdt_context);
int_wdt_cpu1_ticked = false;
}
}
#else // CONFIG_ESP_INT_WDT_CHECK_CPU1
if (esp_cpu_get_core_id() != 0) {
return;
} 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
wdt_hal_feed(&iwdt_context);
wdt_hal_write_protect_enable(&iwdt_context);
}
#endif // CONFIG_ESP_INT_WDT_CHECK_CPU1
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
if (int_wdt_cpu1_ticked) {
int_wdt_cpu1_ticked = false;
} else {
return;
}
#endif
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
esp_int_wdt_set_livelock_params(CONFIG_ESP_INT_WDT_TIMEOUT_MS);
reconfigure_ticks(esp_int_wdt_livelock_get_feed_stage0_ticks(), IWDT_STAGE1_TIMEOUT_US);
#else
reconfigure_ticks(IWDT_STAGE0_TIMEOUT_US, IWDT_STAGE1_TIMEOUT_US);
#endif
}
void esp_int_wdt_init(void)
@@ -153,9 +148,9 @@ void esp_int_wdt_init(void)
* Todo: Fix this
*/
wdt_hal_init(&iwdt_context, IWDT_INSTANCE, IWDT_PRESCALER, true);
reconfigure_ticks(IWDT_INITIAL_TIMEOUT_US, IWDT_INITIAL_TIMEOUT_US);
wdt_hal_write_protect_disable(&iwdt_context);
wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT);
wdt_hal_config_stage(&iwdt_context, WDT_STAGE1, IWDT_INITIAL_TIMEOUT_S * 1000000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_RESET_SYSTEM);
wdt_hal_enable(&iwdt_context);
wdt_hal_write_protect_enable(&iwdt_context);
@@ -163,34 +158,14 @@ void esp_int_wdt_init(void)
esp_int_wdt_retention_enable(IWDT_TIMER_GROUP);
#endif
#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)
uint32_t eriadrs, scratch = 0, immediate = 0;
if (soc_has_cache_lock_bug()) {
if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) {
__asm__ __volatile__(
/* Enable Xtensa Debug Module Integration Mode */
"movi %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n"
"rer %[REG], %[ERI]\n"
"movi %[IMM], 1\n"
"or %[REG], %[IMM], %[REG]\n"
"wer %[REG], %[ERI]\n"
/* Enable Xtensa Debug Module Break_In signal */
"movi %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n"
"rer %[REG], %[ERI]\n"
"movi %[IMM], 0x10000\n"
"or %[REG], %[IMM], %[REG]\n"
"wer %[REG], %[ERI]\n"
: [ERI] "=r"(eriadrs), [REG] "+r"(scratch), [IMM] "+r"(immediate)
);
}
}
#endif // (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
* This is a workaround for issue WDT-3.15 in "ESP32 ECO and workarounds for
* Bugs" document.
*/
esp_int_wdt_init_for_livelock_fix();
esp_int_wdt_set_livelock_params(CONFIG_ESP_INT_WDT_TIMEOUT_MS);
#endif
}
void esp_int_wdt_cpu_init(void)
@@ -207,18 +182,6 @@ void esp_int_wdt_cpu_init(void)
#if SOC_CPU_HAS_FLEXIBLE_INTC
esp_cpu_intr_set_type(ETS_INT_WDT_INUM, INTR_TYPE_LEVEL);
esp_cpu_intr_set_priority(ETS_INT_WDT_INUM, SOC_INTERRUPT_LEVEL_MEDIUM);
#endif
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
* This is a workaround for issue WDT-3.15 in "ESP32 ECO and workarounds for
* Bugs" document.
*/
_lx_intr_livelock_counter = 0;
if (soc_has_cache_lock_bug()) {
assert((portTICK_PERIOD_MS << 1) <= IWDT_LIVELOCK_TIMEOUT_MS);
assert(CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (IWDT_LIVELOCK_TIMEOUT_MS * 3));
_lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
}
#endif
esp_intr_enable_source(ETS_INT_WDT_INUM);
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/**
* @brief Enable or disable the livelock workaround and reconfigure the interrupt WDT.
*
* @param enable true to enable livelock workaround, false to disable
*/
void esp_int_wdt_livelock_workaround(bool enable);
/**
* @brief Get stage0 ticks for a feed (from current livelock params).
*
* @return stage0 timeout ticks
*/
uint32_t esp_int_wdt_livelock_get_feed_stage0_ticks(void);
/**
* @brief Initialize interrupt watchdog for livelock fix workaround
*/
void esp_int_wdt_init_for_livelock_fix(void);
/**
* @brief Set livelock workaround params (window count from timeout, reset counter)
*
* Call at CPU init and when (re-)enabling the livelock workaround.
*
* @param timeout_ms Interrupt watchdog timeout in milliseconds
*/
void esp_int_wdt_set_livelock_params(uint32_t timeout_ms);
/**
* @brief Reset livelock params (counter and max to 0) when disabling the livelock workaround
*/
void esp_int_wdt_reset_livelock_params(void);
#endif // CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
#ifdef __cplusplus
}
#endif
@@ -4,6 +4,10 @@ set(srcs "highint_hdl.S"
"system_internal.c"
"cache_err_int.c")
if(CONFIG_ESP32_ECO3_CACHE_LOCK_FIX)
list(APPEND srcs "eco3_livelock_workaround.c")
endif()
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
@@ -0,0 +1,88 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "esp_chip_info.h"
#include "esp_private/esp_system_attr.h"
#include "esp_private/esp_int_wdt.h"
#include "esp_attr.h"
#define IWDT_LIVELOCK_TIMEOUT_MS (20)
extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max;
void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_reset_livelock_params(void)
{
if (soc_has_cache_lock_bug()) {
_lx_intr_livelock_counter = 0;
_lx_intr_livelock_max = 0;
}
}
_Static_assert((portTICK_PERIOD_MS << 1) <= IWDT_LIVELOCK_TIMEOUT_MS, "portTICK_PERIOD_MS must be less than or equal to IWDT_LIVELOCK_TIMEOUT_MS");
_Static_assert(CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (IWDT_LIVELOCK_TIMEOUT_MS * 3), "CONFIG_ESP_INT_WDT_TIMEOUT_MS must be greater than or equal to IWDT_LIVELOCK_TIMEOUT_MS * 3");
ESP_SYSTEM_IRAM_ATTR void esp_int_wdt_set_livelock_params(uint32_t timeout_ms)
{
_lx_intr_livelock_counter = 0;
if (soc_has_cache_lock_bug()) {
_lx_intr_livelock_max = timeout_ms / IWDT_LIVELOCK_TIMEOUT_MS - 1;
}
}
uint32_t ESP_SYSTEM_IRAM_ATTR esp_int_wdt_livelock_get_feed_stage0_ticks(void)
{
return IWDT_STAGE0_TIMEOUT_US / (_lx_intr_livelock_max + 1);
}
void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_livelock_workaround(bool enable)
{
uint32_t stage0_ticks = IWDT_STAGE0_TIMEOUT_US;
if (enable) {
esp_int_wdt_set_livelock_params(CONFIG_ESP_INT_WDT_TIMEOUT_MS);
stage0_ticks = esp_int_wdt_livelock_get_feed_stage0_ticks();
} else {
esp_int_wdt_reset_livelock_params();
}
esp_int_wdt_reconfigure_ticks(stage0_ticks, IWDT_STAGE1_TIMEOUT_US);
}
void esp_int_wdt_init_for_livelock_fix(void)
{
#if 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)
if (soc_has_cache_lock_bug()) {
if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) {
uint32_t eriadrs, scratch = 0, immediate = 0;
__asm__ __volatile__(
/* Enable Xtensa Debug Module Integration Mode */
"movi %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n"
"rer %[REG], %[ERI]\n"
"movi %[IMM], 1\n"
"or %[REG], %[IMM], %[REG]\n"
"wer %[REG], %[ERI]\n"
/* Enable Xtensa Debug Module Break_In signal */
"movi %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n"
"rer %[REG], %[ERI]\n"
"movi %[IMM], 0x10000\n"
"or %[REG], %[IMM], %[REG]\n"
"wer %[REG], %[ERI]\n"
: [ERI] "=r"(eriadrs), [REG] "+r"(scratch), [IMM] "+r"(immediate)
);
}
}
#endif // CONFIG_BTDM_CTRL_HLI
}