diff --git a/components/esp_hw_support/include/esp_private/esp_pmu.h b/components/esp_hw_support/include/esp_private/esp_pmu.h index fe3021a830..03a85ea5a5 100644 --- a/components/esp_hw_support/include/esp_private/esp_pmu.h +++ b/components/esp_hw_support/include/esp_private/esp_pmu.h @@ -234,6 +234,12 @@ void pmu_sleep_init(const pmu_sleep_config_t *config, bool dslp); */ uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp); +/** + * @brief Finish sleep process settings and get sleep reject status + * @return return sleep reject status + */ +bool pmu_sleep_finish(void); + /** * @brief Initialize PMU related power/clock/digital parameters and functions */ diff --git a/components/esp_hw_support/port/esp32c6/pmu_sleep.c b/components/esp_hw_support/port/esp32c6/pmu_sleep.c index f990464c80..a2f28c4c52 100644 --- a/components/esp_hw_support/port/esp32c6/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32c6/pmu_sleep.c @@ -278,10 +278,16 @@ uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp /* Start entry into sleep mode */ pmu_ll_hp_set_sleep_enable(PMU_instance()->hal->dev); + /* In pd_cpu lightsleep and deepsleep mode, we never get here */ while (!pmu_ll_hp_is_sleep_wakeup(PMU_instance()->hal->dev) && !pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev)) { ; } - return ESP_OK; + return pmu_sleep_finish(); +} + +bool pmu_sleep_finish(void) +{ + return pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev); } diff --git a/components/esp_hw_support/sleep_cpu.c b/components/esp_hw_support/sleep_cpu.c index 41ed0b9b94..1bc2037d6d 100644 --- a/components/esp_hw_support/sleep_cpu.c +++ b/components/esp_hw_support/sleep_cpu.c @@ -22,7 +22,9 @@ #include "esp_private/sleep_cpu.h" #include "sdkconfig.h" -#if !SOC_PMU_SUPPORTED +#if SOC_PMU_SUPPORTED +#include "esp_private/esp_pmu.h" +#else #include "hal/rtc_hal.h" #endif @@ -683,7 +685,7 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep, } #endif - return ESP_OK; + return pmu_sleep_finish(); } esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool), diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 2e784e7ca6..4a71b4135d 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -124,6 +124,9 @@ #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) #endif +// Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks +#define SLEEP_TIMER_ALARM_TO_SLEEP_TICKS (16) + #define LIGHT_SLEEP_TIME_OVERHEAD_US DEFAULT_HARDWARE_OUT_OVERHEAD_US #ifdef CONFIG_ESP_SYSTEM_RTC_EXT_XTAL #define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) @@ -224,7 +227,7 @@ static void ext0_wakeup_prepare(void); #if SOC_PM_SUPPORT_EXT1_WAKEUP static void ext1_wakeup_prepare(void); #endif -static void timer_wakeup_prepare(void); +static esp_err_t timer_wakeup_prepare(void); #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 static void touch_wakeup_prepare(void); #endif @@ -476,6 +479,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo // For light sleep, suspend UART output — it will resume after wakeup. // For deep sleep, wait for the contents of UART FIFO to be sent. bool deep_sleep = (mode == ESP_SLEEP_MODE_DEEP_SLEEP); + bool should_skip_sleep = false; if (deep_sleep) { flush_uarts(); @@ -585,6 +589,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo } // Enter sleep + esp_err_t result; #if SOC_PMU_SUPPORTED pmu_sleep_config_t config; pmu_sleep_init(pmu_sleep_config_default(&config, pd_flags, s_config.sleep_time_adjustment, @@ -603,7 +608,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo // Configure timer wakeup if (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) { - timer_wakeup_prepare(); + if (timer_wakeup_prepare() != ESP_OK) { + result = ESP_ERR_SLEEP_REJECT; + should_skip_sleep = true; + } } #if CONFIG_ESP_SLEEP_SYSTIMER_STALL_WORKAROUND @@ -612,66 +620,67 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t mo } #endif - uint32_t result; - if (deep_sleep) { + if (!should_skip_sleep) { + if (deep_sleep) { #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP - esp_sleep_isolate_digital_gpio(); + esp_sleep_isolate_digital_gpio(); #endif #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY - esp_set_deep_sleep_wake_stub_default_entry(); - // Enter Deep Sleep + esp_set_deep_sleep_wake_stub_default_entry(); + // Enter Deep Sleep #if SOC_PMU_SUPPORTED - result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); + result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); #else - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #endif #else #if !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP - /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ + /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ #if SOC_RTC_FAST_MEM_SUPPORTED - set_rtc_memory_crc(); + set_rtc_memory_crc(); #endif - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #else - /* Otherwise, need to call the dedicated soc function for this */ - result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); + /* Otherwise, need to call the dedicated soc function for this */ + result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); #endif #endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY - } else { + } else { -/* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. - In order to avoid the leakage of the SPI cs pin, hold it here */ + /* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. + In order to avoid the leakage of the SPI cs pin, hold it here */ #if (CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND) - if(!(pd_flags & PMU_SLEEP_PD_VDDSDIO)) { - rtcio_ll_force_hold_enable(SPI_CS0_GPIO_NUM); - } + if(!(pd_flags & PMU_SLEEP_PD_VDDSDIO)) { + rtcio_ll_force_hold_enable(SPI_CS0_GPIO_NUM); + } #endif #if SOC_PM_CPU_RETENTION_BY_SW - if (pd_flags & PMU_SLEEP_PD_CPU) { - result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); - } else { - result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); - } + if (pd_flags & PMU_SLEEP_PD_CPU) { + result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); + } else { + result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); + } #else - result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); + result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #endif -/* Unhold the SPI CS pin */ + /* Unhold the SPI CS pin */ #if (CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND) - if(!(pd_flags & PMU_SLEEP_PD_VDDSDIO)) { - rtcio_ll_force_hold_disable(SPI_CS0_GPIO_NUM); - } + if(!(pd_flags & PMU_SLEEP_PD_VDDSDIO)) { + rtcio_ll_force_hold_disable(SPI_CS0_GPIO_NUM); + } #endif - } + } #if CONFIG_ESP_SLEEP_SYSTIMER_STALL_WORKAROUND - if (!(pd_flags & RTC_SLEEP_PD_XTAL)) { - rtc_sleep_systimer_enable(true); - } + if (!(pd_flags & RTC_SLEEP_PD_XTAL)) { + rtc_sleep_systimer_enable(true); + } #endif + } // Restore CPU frequency #if SOC_PM_SUPPORT_PMU_MODEM_STATE @@ -1122,7 +1131,7 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) return ESP_OK; } -static void timer_wakeup_prepare(void) +static esp_err_t timer_wakeup_prepare(void) { int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment; if (sleep_duration < 0) { @@ -1130,12 +1139,23 @@ static void timer_wakeup_prepare(void) } int64_t ticks = rtc_time_us_to_slowclk(sleep_duration, s_config.rtc_clk_cal_period); + int64_t target_wakeup_tick = s_config.rtc_ticks_at_sleep_start + ticks; #if SOC_LP_TIMER_SUPPORTED - lp_timer_hal_set_alarm_target(0, s_config.rtc_ticks_at_sleep_start + ticks); -#else - rtc_hal_set_wakeup_timer(s_config.rtc_ticks_at_sleep_start + ticks); +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + // When pd_top is supported, light_sleep will flush uart, and the sleep overhead time will become an unpredictable value, + // here is the last timer wake-up validity check + if ((sleep_duration == 0) || \ + (target_wakeup_tick < lp_timer_hal_get_cycle_count() + SLEEP_TIMER_ALARM_TO_SLEEP_TICKS)) { + // Treat too short sleep duration setting as timer reject + return ESP_ERR_SLEEP_REJECT; + } #endif + lp_timer_hal_set_alarm_target(0, target_wakeup_tick); +#else + rtc_hal_set_wakeup_timer(target_wakeup_tick); +#endif + return ESP_OK; } #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index 13343f48f4..9b7f6e608a 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -156,6 +156,7 @@ static const char* s_mode_names[] = { "APB_MAX", "CPU_MAX" }; +static uint32_t s_light_sleep_counts, s_light_sleep_reject_counts; #endif // WITH_PROFILING #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT @@ -612,7 +613,13 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) /* Enter sleep */ ESP_PM_TRACE_ENTER(SLEEP, core_id); int64_t sleep_start = esp_timer_get_time(); - esp_light_sleep_start(); + if (esp_light_sleep_start() != ESP_OK){ +#ifdef WITH_PROFILING + s_light_sleep_reject_counts++; + } else { + s_light_sleep_counts++; +#endif + } int64_t slept_us = esp_timer_get_time() - sleep_start; ESP_PM_TRACE_EXIT(SLEEP, core_id); @@ -652,6 +659,9 @@ void esp_pm_impl_dump_stats(FILE* out) pm_time_t last_mode_change_time = s_last_mode_change_time; pm_mode_t cur_mode = s_mode; pm_time_t now = pm_get_time(); + bool light_sleep_en = s_light_sleep_en; + uint32_t light_sleep_counts = s_light_sleep_counts; + uint32_t light_sleep_reject_counts = s_light_sleep_reject_counts; portEXIT_CRITICAL_ISR(&s_switch_lock); time_in_mode[cur_mode] += now - last_mode_change_time; @@ -659,7 +669,7 @@ void esp_pm_impl_dump_stats(FILE* out) fprintf(out, "\nMode stats:\n"); fprintf(out, "%-8s %-10s %-10s %-10s\n", "Mode", "CPU_freq", "Time(us)", "Time(%)"); for (int i = 0; i < PM_MODE_COUNT; ++i) { - if (i == PM_MODE_LIGHT_SLEEP && !s_light_sleep_en) { + if (i == PM_MODE_LIGHT_SLEEP && !light_sleep_en) { /* don't display light sleep mode if it's not enabled */ continue; } @@ -670,6 +680,10 @@ void esp_pm_impl_dump_stats(FILE* out) time_in_mode[i], (int) (time_in_mode[i] * 100 / now)); } + if (light_sleep_en){ + fprintf(out, "\nSleep stats:\n"); + fprintf(out, "light_sleep_counts:%ld light_sleep_reject_counts:%ld\n", light_sleep_counts, light_sleep_reject_counts); + } } #endif // WITH_PROFILING