From 6f1bea6a5d789e56fbfdec217e053675c0030e40 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Tue, 17 Mar 2026 12:27:28 +0800 Subject: [PATCH] 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 --- .../esp_hw_support/port/esp32/chip_info.c | 3 +- .../include/esp_private/esp_int_wdt.h | 22 ++- components/esp_system/int_wdt.c | 129 +++++++----------- .../esp_private/eco3_livelock_workaround.h | 56 ++++++++ .../esp_system/port/soc/esp32/CMakeLists.txt | 4 + .../port/soc/esp32/eco3_livelock_workaround.c | 88 ++++++++++++ 6 files changed, 217 insertions(+), 85 deletions(-) create mode 100644 components/esp_system/port/include/private/esp_private/eco3_livelock_workaround.h create mode 100644 components/esp_system/port/soc/esp32/eco3_livelock_workaround.c diff --git a/components/esp_hw_support/port/esp32/chip_info.c b/components/esp_hw_support/port/esp32/chip_info.c index 5f9cb505fc..951fb50503 100644 --- a/components/esp_hw_support/port/esp32/chip_info.c +++ b/components/esp_hw_support/port/esp32/chip_info.c @@ -6,6 +6,7 @@ #include #include "esp_chip_info.h" +#include "esp_attr.h" #include "soc/soc.h" #include "soc/chip_revision.h" #include "soc/efuse_reg.h" @@ -42,7 +43,7 @@ void esp_chip_info(esp_chip_info_t* out_info) } #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX -inline bool soc_has_cache_lock_bug(void) +IRAM_ATTR inline bool soc_has_cache_lock_bug(void) { unsigned rev = efuse_hal_chip_revision(); return ESP_CHIP_REV_ABOVE(rev, 300); diff --git a/components/esp_system/include/esp_private/esp_int_wdt.h b/components/esp_system/include/esp_private/esp_int_wdt.h index 38cd30bb7b..8c2305142f 100644 --- a/components/esp_system/include/esp_private/esp_int_wdt.h +++ b/components/esp_system/include/esp_private/esp_int_wdt.h @@ -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 diff --git a/components/esp_system/int_wdt.c b/components/esp_system/int_wdt.c index 3a43caf203..36053f28de 100644 --- a/components/esp_system/int_wdt.c +++ b/components/esp_system/int_wdt.c @@ -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); } diff --git a/components/esp_system/port/include/private/esp_private/eco3_livelock_workaround.h b/components/esp_system/port/include/private/esp_private/eco3_livelock_workaround.h new file mode 100644 index 0000000000..3c92cdb903 --- /dev/null +++ b/components/esp_system/port/include/private/esp_private/eco3_livelock_workaround.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#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 diff --git a/components/esp_system/port/soc/esp32/CMakeLists.txt b/components/esp_system/port/soc/esp32/CMakeLists.txt index 1b6758a33a..f6e8c89029 100644 --- a/components/esp_system/port/soc/esp32/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32/CMakeLists.txt @@ -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}) diff --git a/components/esp_system/port/soc/esp32/eco3_livelock_workaround.c b/components/esp_system/port/soc/esp32/eco3_livelock_workaround.c new file mode 100644 index 0000000000..8305df7ff6 --- /dev/null +++ b/components/esp_system/port/soc/esp32/eco3_livelock_workaround.c @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include +#include +#include +#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 +}