mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'fix/fix_esp32_int_wdt_workaround_break_sleep_process_v6.0' into 'release/v6.0'
fix(esp_system): disable esp32 cache livelock int_wdt workaround in ipc_isr_stall (v6.0) See merge request espressif/esp-idf!46741
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
||||
@@ -135,6 +135,10 @@
|
||||
#include "hal/clk_gate_ll.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
#include "esp_private/eco3_livelock_workaround.h"
|
||||
#endif
|
||||
|
||||
#if SOC_MSPI_HAS_INDEPENT_IOMUX
|
||||
#include "hal/mspi_ll.h"
|
||||
#endif
|
||||
@@ -1175,6 +1179,11 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
|
||||
esp_os_enter_critical(&spinlock_rtc_deep_sleep);
|
||||
esp_ipc_isr_stall_other_cpu();
|
||||
esp_ipc_isr_stall_pause();
|
||||
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
// The other core will be stalled by high-priority interrupt and spins on variables in internal RAM,
|
||||
// which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed.
|
||||
esp_int_wdt_livelock_workaround(false);
|
||||
#endif
|
||||
|
||||
// record current RTC time
|
||||
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
|
||||
@@ -1241,6 +1250,10 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
|
||||
ESP_INFINITE_LOOP();
|
||||
}
|
||||
// Never returns here, except that the sleep is rejected.
|
||||
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
// Configure WDT to use livelock workaround timeout after releasing other CPU
|
||||
esp_int_wdt_livelock_workaround(true);
|
||||
#endif
|
||||
esp_ipc_isr_stall_resume();
|
||||
esp_ipc_isr_release_other_cpu();
|
||||
esp_os_exit_critical(&spinlock_rtc_deep_sleep);
|
||||
@@ -1381,6 +1394,11 @@ esp_err_t esp_light_sleep_start(void)
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
|
||||
sleep_smp_cpu_sleep_prepare();
|
||||
#else
|
||||
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
// The other core will be stalled by high-priority interrupt and spins on variables in internal RAM,
|
||||
// which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed.
|
||||
esp_int_wdt_livelock_workaround(false);
|
||||
#endif
|
||||
esp_ipc_isr_stall_other_cpu();
|
||||
#endif
|
||||
esp_ipc_isr_stall_pause();
|
||||
@@ -1569,6 +1587,10 @@ esp_err_t esp_light_sleep_start(void)
|
||||
sleep_smp_cpu_wakeup_prepare();
|
||||
#else
|
||||
esp_ipc_isr_release_other_cpu();
|
||||
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
||||
// Configure WDT to use livelock workaround timeout after releasing other CPU
|
||||
esp_int_wdt_livelock_workaround(true);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -462,9 +462,45 @@ It is also possible to enter sleep modes with no wakeup sources configured. In t
|
||||
UART Output Handling
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Before entering sleep mode, :cpp:func:`esp_deep_sleep_start` will flush the contents of UART FIFOs.
|
||||
Before entering sleep, the sleep flow prepares the **console UART** (the UART used for debug output, selected by :ref:`CONFIG_ESP_CONSOLE_UART_NUM`) so that APB clock changes or power-down do not cause garbled output or undefined behavior. The strategy applied is configurable and affects data integrity, sleep entry time, and power consumption.
|
||||
|
||||
When entering Light-sleep mode using :cpp:func:`esp_light_sleep_start`, UART FIFOs will not be flushed. Instead, UART output will be suspended, and remaining characters in the FIFO will be sent out after wakeup from Light-sleep.
|
||||
**Default behavior (auto mode)**
|
||||
|
||||
If you do not call :cpp:func:`esp_sleep_set_console_uart_handling_mode`, the following default strategy is used:
|
||||
|
||||
- **Deep-sleep**: Always wait until all data in the console UART FIFO has been transmitted before entering sleep, so that all debug output is sent and no data is lost.
|
||||
- **Light-sleep**: Behavior depends on whether the UART power domain is powered down:
|
||||
- If the UART remains powered (e.g. HP peripheral domain not powered down): UART output is **suspended** after the current frame completes; after wakeup it is resumed and any remaining data in the UART TX FIFO before sleep continues to be sent.
|
||||
- If the UART power domain is powered down: The sleep flow waits until all data in the console UART TX FIFO has been transmitted before entering sleep; data in other UARTs is discarded to enter sleep faster.
|
||||
|
||||
**Configuring console UART handling**
|
||||
|
||||
You can override the default by calling :cpp:func:`esp_sleep_set_console_uart_handling_mode` and choosing one of the following modes (see :cpp:enum:`esp_sleep_uart_handling_mode_t`):
|
||||
|
||||
- :cpp:enumerator:`ESP_SLEEP_AUTO_FLUSH_SUSPEND_UART` (default): Automatically choose flush or suspend based on sleep type and power domain, as described above.
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_FLUSH_UART` : Always wait until all data in the console UART TX FIFO has been transmitted before entering sleep. Use when you must guarantee that all debug output is visible; sleep entry will take longer and the chip will stay in Active state longer, increasing power consumption.
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_SUSPEND_UART` : Wait for the current UART frame to complete, then suspend the UART. If the UART stays powered during Light-sleep, transmission continues after wake. If the UART power domain is powered down, unsent data will be lost.
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_DISCARD_UART` : Discard all unsent data in the console UART FIFO and enter sleep immediately. Use for the fastest sleep entry and lowest power when debug output can be discarded.
|
||||
- :cpp:enumerator:`ESP_SLEEP_NO_HANDLING` : Do not perform any handling on the console UART before sleep. Use only when the UART state is known to be safe (e.g. no pending output or the console UART is disabled).
|
||||
|
||||
.. note::
|
||||
|
||||
The sleep flow runs in a critical section. When using a mode that flushes the console UART (e.g. :cpp:enumerator:`ESP_SLEEP_ALWAYS_FLUSH_UART` , or the default behavior for Light-sleep/Deep-sleep when the HP peripheral domain is powered down), set :ref:`CONFIG_ESP_INT_WDT_TIMEOUT_MS` to be **greater than** ``SOC_UART_FIFO_LEN`` × (time to send one character at the current baud rate). Otherwise, if too much data is queued in the TX FIFO, the flush may take longer than the interrupt watchdog timeout and trigger a watchdog reset during sleep entry.
|
||||
|
||||
Example: ensure all debug output is sent before every sleep::
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
fflush(stdout);
|
||||
esp_sleep_set_console_uart_handling_mode(ESP_SLEEP_ALWAYS_FLUSH_UART);
|
||||
esp_light_sleep_start();
|
||||
|
||||
Example: minimize sleep entry time and allow discarding console output::
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
esp_sleep_set_console_uart_handling_mode(ESP_SLEEP_ALWAYS_DISCARD_UART);
|
||||
esp_deep_sleep_start();
|
||||
|
||||
.. _wakeup_cause:
|
||||
|
||||
|
||||
@@ -462,9 +462,45 @@ flash 断电
|
||||
UART 输出处理
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
在进入睡眠模式之前,调用函数 :cpp:func:`esp_deep_sleep_start` 会冲刷掉 UART FIFO 缓存。
|
||||
进入睡眠前,睡眠流程会对**控制台 UART**(用于调试输出的 UART,由 :ref:`CONFIG_ESP_CONSOLE_UART_NUM` 选定)进行准备,以避免 APB 时钟变化或掉电导致输出乱码或未定义行为。所采用的策略可配置,会影响数据完整性、进入睡眠的时间以及功耗。
|
||||
|
||||
当使用函数 :cpp:func:`esp_light_sleep_start` 进入 Light-sleep 模式时,UART FIFO 将不会被冲刷。与之相反,UART 输出将被暂停,FIFO 中的剩余字符将在 Light-sleep 唤醒后被发送。
|
||||
**默认行为(自动模式)**
|
||||
|
||||
若不调用 :cpp:func:`esp_sleep_set_console_uart_handling_mode`,则采用以下默认策略:
|
||||
|
||||
- **Deep-sleep**:始终等到控制台 UART FIFO 中的数据全部发完后再进入睡眠,确保所有调试输出被发送且不丢数据。
|
||||
- **Light-sleep**:行为取决于 UART 所在电源域是否掉电:
|
||||
- 若 UART 保持供电(例如 HP 外设域未掉电):在当前帧发完后挂起 UART 输出;唤醒后恢复发送,睡眠前 UART TX FIFO 中剩余数据会继续发出。
|
||||
- 若 UART 电源域掉电:睡眠流程会等到控制台 UART TX FIFO 中的数据全部发完后再进入睡眠;其他 UART 中的数据会被丢弃以更快进入睡眠。
|
||||
|
||||
**配置控制台 UART 处理方式**
|
||||
|
||||
可通过调用 :cpp:func:`esp_sleep_set_console_uart_handling_mode` 覆盖默认行为,并选择下列模式之一(参见 :cpp:enum:`esp_sleep_uart_handling_mode_t`):
|
||||
|
||||
- :cpp:enumerator:`ESP_SLEEP_AUTO_FLUSH_SUSPEND_UART` (默认):根据睡眠类型和电源域自动选择冲刷或挂起,如上所述。
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_FLUSH_UART` :进入睡眠前始终等待控制台 UART TX FIFO 中的数据全部发送完毕。适用于必须保证所有调试输出可见的场景;进入睡眠时间会更长,芯片处于 Active 状态的时间变长进而增加功耗。
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_SUSPEND_UART` :等待当前 UART 帧发完后挂起 UART。若 Light-sleep 期间 UART 保持供电,唤醒后会继续发送;若 UART 电源域掉电,未发送的数据将丢失。
|
||||
- :cpp:enumerator:`ESP_SLEEP_ALWAYS_DISCARD_UART` :丢弃控制台 UART FIFO 中所有未发送数据并立即进入睡眠。适用于追求最快进入睡眠和最低功耗、且可接受丢弃调试输出的场景。
|
||||
- :cpp:enumerator:`ESP_SLEEP_NO_HANDLING` :进入睡眠前不对控制台 UART 做任何处理。仅在确认 UART 状态安全时使用(例如无待发数据或已禁用控制台 UART)。
|
||||
|
||||
.. note::
|
||||
|
||||
睡眠流程在临界区中执行,当使用会冲刷控制台 UART 的模式(如 :cpp:enumerator:`ESP_SLEEP_ALWAYS_FLUSH_UART` ,或 HP 外设域掉电时的 Light-sleep/Deep-sleep 默认行为)时,请将 :ref:`CONFIG_ESP_INT_WDT_TIMEOUT_MS` 配置为**大于** ``SOC_UART_FIFO_LEN`` ×(当前波特率下发送一个字符所需时间)。否则若 TX FIFO 中积压数据过多,冲刷时间可能超过中断看门狗超时,会在进入睡眠过程中触发看门狗复位。
|
||||
|
||||
示例:在每次睡眠前确保所有调试输出已发出::
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
fflush(stdout);
|
||||
esp_sleep_set_console_uart_handling_mode(ESP_SLEEP_ALWAYS_FLUSH_UART);
|
||||
esp_light_sleep_start();
|
||||
|
||||
示例:尽量缩短进入睡眠时间并允许丢弃控制台输出::
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_sleep_set_console_uart_handling_mode(ESP_SLEEP_ALWAYS_DISCARD_UART);
|
||||
esp_deep_sleep_start();
|
||||
|
||||
.. _wakeup_cause:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user