diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index b9ecfa3af55..7f6c3623ae9 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -241,6 +241,44 @@ menu "Power Management" NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in callback and hence it is highly recommended to keep them as short as possible + config PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION + bool "Enable light sleep tick overflow protection" + depends on FREERTOS_USE_TICKLESS_IDLE + default n + help + Limits tick compensation after light sleep wakeup to prevent vTaskStepTick() assertion + failure when actual wakeup overhead exceeds estimation (due to cache misses, CPU + frequency changes, or flash latency variations). + + When enabled: + - Silently limits slept_ticks to xExpectedIdleTime when oversleep is within tolerance + (configured by PM_LIGHTSLEEP_TICK_OVERFLOW_TOLERANCE), preventing assertion failure + - Does not limit when oversleep exceeds tolerance (may indicate a bug), assertion failure may occur + - May lose ticks in rare cases, causing xTickCount to lag behind esp_timer + + When disabled (default): + - Accurate tick compensation, better precision + - May trigger assertion failure and system crash if overslept + + Keep disabled by default to maintain RTOS tick accuracy. Enable only when you experience + assertion failures related to vTaskStepTick() and can accept slight inaccuracy of RTOS + tick time compared to real time. + + config PM_LIGHTSLEEP_TICK_OVERFLOW_TOLERANCE + int "Light sleep tick overflow tolerance" + depends on PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION + default 2 + range 1 10 + help + Maximum number of ticks that can be overslept before triggering. + When oversleep is within this tolerance, the system silently limits slept_ticks to + prevent assertion failure. When oversleep exceeds this tolerance, assertion failure + may occur and system may crash. + + Higher values provide more tolerance for estimation variance but may hide real issues + and cause more tick loss, leading to greater RTOS tick time inaccuracy compared to real time. + Lower values provide better RTOS tick accuracy but may cause false positives. + config PM_WORKAROUND_FREQ_LIMIT_ENABLED bool default y if SPI_FLASH_FREQ_LIMIT_C5_240MHZ diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index bab406ca89f..1d4b482b2f0 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -834,10 +835,21 @@ static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id) } // Adjust RTOS tick count based on the amount of time spent in sleep. -FORCE_INLINE_ATTR void pm_step_tick(int64_t slept_us) +FORCE_INLINE_ATTR void pm_step_tick(int64_t slept_us, TickType_t xExpectedIdleTime) { uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL); if (slept_ticks) { +#if CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION + /* Limit slept_ticks when oversleep is within tolerance to prevent assertion failure */ + if ((slept_ticks > xExpectedIdleTime) && + (slept_ticks <= (xExpectedIdleTime + CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_TOLERANCE))) { + slept_ticks = xExpectedIdleTime; + } +#endif // CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION + if (slept_ticks > xExpectedIdleTime) { + ESP_EARLY_LOGE(TAG, "Light sleep overslept: expect %"PRIu32" idle ticks but slept %"PRIu32" ticks.", + (uint32_t)xExpectedIdleTime, slept_ticks); + } /* Adjust RTOS tick count based on the amount of time spent in sleep */ vTaskStepTick(slept_ticks); @@ -886,7 +898,7 @@ void vApplicationSleep( TickType_t xExpectedIdleTime ) // In this case, there is no need to call vTaskStepTick, because the OS tick count will // automatically catch up in the next systick interrupt handler. if (err == ESP_OK) { - pm_step_tick(slept_us); + pm_step_tick(slept_us, xExpectedIdleTime); } other_core_should_skip_light_sleep(core_id); #ifdef WITH_PROFILING