change(esp_hw_support): add sleep_rtc_wdt_prepare api for rtc wdt enable or disable

This commit is contained in:
hebinglin
2026-01-20 20:09:22 +08:00
parent c02944f8b8
commit b763ce7974
2 changed files with 72 additions and 49 deletions
+8 -7
View File
@@ -282,10 +282,10 @@ menu "Hardware Settings"
timeout will be set as the sum of the sleep duration, hardware overhead time, and RTC slow clock
compensation time.
NOTE: In the current phase, the RTC slow clock compensation time remains zero (pending implementation).
When using the RC_SLOW_CLK without calibration, the maximum continuous sleep duration is typically
around 5 minutes, and may vary between chips. Therefore, avoid configuring sleep periods longer than
this when enabling this option.
NOTE: This feature only works when a timer wakeup is used. In the current phase, the RTC slow clock
compensation time remains zero (pending implementation). When using the RC_SLOW_CLK without
calibration, the maximum continuous sleep duration is typically around 5 minutes, and may vary
between chips. Therefore, avoid configuring sleep periods longer than this when enabling this option.
config ESP_SLEEP_ENABLE_RTC_WDT_IN_DEEP_SLEEP
@@ -296,9 +296,10 @@ menu "Hardware Settings"
help
If enabled, rtc watchdog will remain active during deep sleep to prevent program runaway.
NOTE: In the current phase, the RTC watchdog will not be disabled or reset after waking from deep
sleep. Even though it will be reconfigured during the bootloader phase, there remains a concern that
a timeout could occur before this reconfiguration is completed.
NOTE: This feature only works when a timer wakeup is used. In the current phase, the RTC watchdog
will not be disabled or reset after waking from deep sleep. Even though it will be reconfigured
during the bootloader phase, there remains a concern that a timeout could occur before this
reconfiguration is completed.
endmenu
menu "RTC Clock Config"
+64 -42
View File
@@ -348,7 +348,8 @@ void esp_sleep_overhead_out_time_refresh(void)
static uint32_t get_power_down_flags(void);
static uint32_t get_sleep_flags(uint32_t pd_flags, bool deepsleep);
static uint32_t get_sleep_clock_icg_flags(void);
static uint32_t get_slow_clk_zero_drift_compensate(uint32_t sleep_duration);
static uint32_t get_sleep_rtc_wdt_timeout(uint64_t sleep_duration);
static uint32_t calc_sleep_slow_clk_required_cycles(uint32_t timeout, uint32_t rtc_slow_clk_cal_period);
#if SOC_PM_SUPPORT_EXT0_WAKEUP
static void ext0_wakeup_prepare(void);
#endif
@@ -766,6 +767,28 @@ static SLEEP_FN_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags)
#endif
}
/**
* RTC WDT prepare for using during sleep
*/
static SLEEP_FN_ATTR void sleep_rtc_wdt_prepare(bool enable)
{
wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
if (enable) {
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
// Use default timeout for sleep monitoring
uint32_t rtc_wdt_timeout = get_sleep_rtc_wdt_timeout(s_config.sleep_duration);
uint32_t rtc_wdt_timeout_required_cycles = calc_sleep_slow_clk_required_cycles(rtc_wdt_timeout, s_config.rtc_clk_cal_period);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, rtc_wdt_timeout_required_cycles, WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_enable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
} else {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
}
}
static SLEEP_FN_ATTR void sleep_low_power_clock_calibration(bool is_dslp)
{
// Calibrate rtc slow clock
@@ -1223,20 +1246,11 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
#if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_DEEP_SLEEP
// Safety net: enable WDT in case exit from deep sleep fails
wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
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) {
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
uint64_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
#if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP
uint64_t sleep_timout_ticks = rtc_time_us_to_slowclk(s_config.sleep_duration, s_config.rtc_clk_cal_period);
#else
uint64_t sleep_timout_ticks = 0;
#endif
uint64_t slow_clk_comp_ticks = get_slow_clk_zero_drift_compensate(sleep_timout_ticks + stage_timeout_ticks);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, (uint32_t)(stage_timeout_ticks + sleep_timout_ticks + slow_clk_comp_ticks), WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_enable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
bool rtc_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 (!rtc_wdt_was_enabled) {
sleep_rtc_wdt_prepare(true);
} else {
ESP_EARLY_LOGW(TAG, "RTC WDT is enabled and will not be reconfigured again!");
}
#endif
@@ -1285,13 +1299,10 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
// can take several CPU cycles for the sleep mode to start.
ESP_INFINITE_LOOP();
}
// Never returns here, except that the sleep is rejected.
#if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_DEEP_SLEEP
if (!wdt_was_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
if (!rtc_wdt_was_enabled) {
sleep_rtc_wdt_prepare(false);
}
#endif
@@ -1566,20 +1577,11 @@ esp_err_t esp_light_sleep_start(void)
// Safety net: enable WDT in case exit from light sleep fails
wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
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) {
wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false);
uint64_t stage_timeout_ticks = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL);
#if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP
uint64_t sleep_timout_ticks = rtc_time_us_to_slowclk(s_config.sleep_duration, s_config.rtc_clk_cal_period);
#else
uint64_t sleep_timout_ticks = 0;
#endif
uint64_t slow_clk_comp_ticks = get_slow_clk_zero_drift_compensate(sleep_timout_ticks + stage_timeout_ticks);
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, (uint32_t)(stage_timeout_ticks + sleep_timout_ticks + slow_clk_comp_ticks), WDT_STAGE_ACTION_RESET_RTC);
wdt_hal_enable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
bool rtc_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 (!rtc_wdt_was_enabled) {
sleep_rtc_wdt_prepare(true);
} else {
ESP_EARLY_LOGW(TAG, "RTC WDT is enabled and will not be reconfigured again!");
}
esp_err_t err = ESP_OK;
@@ -1651,10 +1653,8 @@ esp_err_t esp_light_sleep_start(void)
#endif
#endif
if (!wdt_was_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
if (!rtc_wdt_was_enabled) {
sleep_rtc_wdt_prepare(false);
}
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
@@ -2959,11 +2959,33 @@ static SLEEP_FN_ATTR uint32_t get_sleep_clock_icg_flags(void)
return clk_flags;
}
/* Compensate zero drift of rtc slow clock in long-term working scenarios */
static uint32_t get_slow_clk_zero_drift_compensate(uint32_t sleep_duration)
/* TODO: PM-609 */
/* Calculate drift cycles of rtc slow clock in long-term working scenarios. */
static SLEEP_FN_ATTR uint32_t calc_sleep_slow_clk_required_cycles(uint32_t timeout, uint32_t rtc_slow_clk_cal_period)
{
(void) sleep_duration;
return 0;
uint32_t timeout_cycles = 0;
uint32_t required_timeout_cycles = 0;
timeout_cycles = (uint32_t)rtc_time_us_to_slowclk(timeout, rtc_slow_clk_cal_period);
/* TODO: Add slow clock drift compensation */
required_timeout_cycles = timeout_cycles;
return required_timeout_cycles;
}
/* Get the timeout in microseconds for the RTC WDT sleep monitoring.
* Returns: default_timeout (1 second) + sleep_duration, all in microseconds.
*/
static SLEEP_FN_ATTR uint32_t get_sleep_rtc_wdt_timeout(uint64_t sleep_duration)
{
uint32_t default_timeout = 1000000; // 1 second in microseconds
uint32_t sleep_timeout = 0;
uint32_t rtc_wdt_timeout = 0;
#if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP || CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_DEEP_SLEEP
sleep_timeout = (uint32_t)sleep_duration;
#endif
rtc_wdt_timeout = default_timeout + sleep_timeout;
return rtc_wdt_timeout;
}
#if CONFIG_IDF_TARGET_ESP32