mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/support_configurable_sleep_tick_overflow_behaviour' into 'master'
feat(pm): add light sleep tick overflow protection configuration See merge request espressif/esp-idf!45413
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
@@ -833,10 +834,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);
|
||||
|
||||
@@ -885,7 +897,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
|
||||
|
||||
@@ -92,6 +92,26 @@ Light-sleep duration is chosen to wake up the chip before the nearest event (tas
|
||||
|
||||
To skip unnecessary wake-up, you can consider initializing an ``esp_timer`` with the ``skip_unhandled_events`` option as ``true``. Timers with this flag will not wake up the system and it helps to reduce consumption.
|
||||
|
||||
Automatic Light-sleep Time Compensation Mechanism
|
||||
-------------------------------------------------
|
||||
|
||||
ESP-IDF uses a predictive time compensation mechanism for automatic Light-sleep. The system measures the actual wakeup overhead after each Light-sleep cycle and uses this measurement to predict the wakeup overhead for the next sleep cycle.
|
||||
|
||||
The system calculates sleep duration based on the next scheduled event and subtracts the predicted wakeup overhead (from the previous cycle) to set the wakeup timer. After wakeup, since FreeRTOS systick interrupts are suspended during sleep, the system needs to call :cpp:func:`vTaskStepTick()` to compensate for the ticks elapsed during sleep, maintaining the accuracy of FreeRTOS tick count. Meanwhile, it measures the actual overhead and stores it for the next prediction, creating a feedback loop that adapts to system behavior.
|
||||
|
||||
However, actual overhead can vary due to cache misses, CPU frequency changes, flash latency variations, or hardware state restoration time. When actual overhead exceeds prediction, the actual sleep time may exceed the expected value, causing :cpp:func:`vTaskStepTick()` to receive an excessive tick compensation value, triggering assertion failure.
|
||||
|
||||
The :ref:`CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION` option provides a safety mechanism to prevent assertion failures when wakeup overhead exceeds prediction. When enabled, the system limits the tick compensation value to prevent overflow. This option can be enabled in menuconfig via ``Component config`` > ``Power Management`` > ``Enable light sleep tick overflow protection``.
|
||||
|
||||
When enabled, this option handles oversleep as follows:
|
||||
- If oversleep is within tolerance (configurable via :ref:`CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_TOLERANCE`, default: 2 ticks), the system silently limits ``slept_ticks`` to ``xExpectedIdleTime``, preventing assertion failure
|
||||
- If oversleep exceeds tolerance (may indicate a bug), the system does not limit ticks, logs an error message, and assertion failure will occur
|
||||
- In rare edge cases, it may lose ticks, causing FreeRTOS tick count (``xTickCount``) to lag behind real time (``esp_timer``). Tasks using :cpp:func:`vTaskDelay()` may experience slightly longer delays than expected, and FreeRTOS software timers may have reduced accuracy.
|
||||
|
||||
When disabled (default), the system provides accurate tick compensation with better precision for time-critical applications. In edge cases, if wakeup overhead estimation is insufficient causing Light-sleep oversleep, assertion failure and system crash may occur.
|
||||
|
||||
It is recommended to keep this option disabled by default to maintain tick accuracy. Enable it only when you experience assertion failures related to :cpp:func:`vTaskStepTick()` and can accept slight inaccuracy of RTOS tick time compared to real time.
|
||||
|
||||
Debugging and Profiling
|
||||
-----------------------
|
||||
|
||||
|
||||
@@ -92,6 +92,26 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
|
||||
为了跳过不必要的唤醒,可以将 ``skip_unhandled_events`` 选项设置为 ``true`` 来初始化 ``esp_timer``。带有此标志的定时器不会唤醒系统,有助于减少功耗。
|
||||
|
||||
自动 Light-sleep 时间补偿机制
|
||||
---------------------------------------
|
||||
|
||||
ESP-IDF 使用预测性时间补偿机制来实现自动 Light-sleep。系统会在每次 Light-sleep 周期后测量实际的唤醒开销,并使用该测量值来预测下一次睡眠周期的唤醒开销。
|
||||
|
||||
系统根据下一个计划事件计算睡眠持续时间,并减去预测的唤醒开销(来自上一周期)来设置唤醒定时器。唤醒后,由于睡眠期间 FreeRTOS systick 中断被暂停,系统需要调用 :cpp:func:`vTaskStepTick()` 来补偿睡眠期间经过的 tick 数,以保持 FreeRTOS tick 计数的准确性。同时,系统测量实际开销并记录,用于下次预测,形成自适应系统行为的反馈循环。
|
||||
|
||||
但实际开销可能因缓存未命中、CPU 频率变化、Flash 延迟变化或硬件状态恢复时间而有所不同。当实际开销超过预测值时,实际睡眠时间可能超过预期,导致 :cpp:func:`vTaskStepTick()` 接收到的 tick 补偿值过大,触发断言失败。
|
||||
|
||||
:ref:`CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_PROTECTION` 选项提供了一个安全机制,用于在唤醒开销超过预测时防止断言失败。启用后,系统会限制 tick 补偿值以防止溢出。在 menuconfig 中可通过 ``Component config`` > ``Power Management`` > ``Enable light sleep tick overflow protection`` 启用此选项。
|
||||
|
||||
启用该选项时,系统对睡过超时情况的处理如下:
|
||||
- 如果睡过超时在容忍范围内(可通过 :ref:`CONFIG_PM_LIGHTSLEEP_TICK_OVERFLOW_TOLERANCE` 配置,默认:2 个 tick),系统会静默地将 ``slept_ticks`` 限制为 ``xExpectedIdleTime``,防止断言失败
|
||||
- 如果睡过超时超过容忍范围(可能存在 bug),系统不会限制 tick,会抛出错误日志,并触发断言失败
|
||||
- 在极少数边缘场景下可能会丢失 tick,导致 FreeRTOS tick 计数(``xTickCount``)落后于真实时间(``esp_timer``),使用 :cpp:func:`vTaskDelay()` 的任务可能比预期延迟稍长,FreeRTOS 软件定时器精度可能降低。
|
||||
|
||||
禁用该选项时(默认),可以获得准确的 tick 补偿,对时间关键应用具有更好的精度。在边缘情况下, 如果唤醒开销估算不足导致 Light-sleep 睡过超时时,可能会触发断言失败导致系统崩溃。
|
||||
|
||||
建议默认保持禁用状态以维持 tick 精度。仅在遇到与 :cpp:func:`vTaskStepTick()` 相关的断言失败,且可以接受 RTOS tick 时间相较于真实时间轻微不准时启用。
|
||||
|
||||
调试和性能分析
|
||||
-----------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user