Tickless Idle
Tasks and Scheduling established that the tick is a periodic interrupt — SysTick, by default 1000 Hz — that the kernel uses as its only window onto elapsed time. That periodicity is exactly what makes an RTOS system a bad candidate for the deep sleep modes earlier in this folder, for a reason that has nothing to do with how much idle time there is: a system that wakes once a millisecond to increment a counter never gets to stay in a low-power mode for longer than a millisecond, no matter how little else it has to do. Sleep Modes showed that Stop mode's wake latency comes from a regulator switch-back and, at the deepest setting, a flash re-power — real time that has to be paid on every entry and exit. Pay it a thousand times a second and the average current never gets anywhere near the deep-sleep floor in the table on that page; it is dominated by the wake-and-sleep-again overhead, not by the sleep current itself.
The mental model: tickless idle is not a low-power mode. It is a change to how the kernel keeps time, made so that entering a low-power mode is worth doing. Suppressing the tick removes the thing that was forcing a wake every period — but the kernel still needs to know how much time passed while it was not counting, because every blocked task's wake time, every xTaskDelayUntil() period, and every software timer in Software Timers and Delays is expressed in ticks. On wake, xTickCount has to be advanced by however long the core actually slept — not by how long it was told to sleep, because those two numbers can differ — and the accuracy of that correction is what limits how long you are allowed to ask for.
Tasks and Scheduling owns xTaskIncrementTick(), the delayed lists, and xNextTaskUnblockTime — the tick machinery this page suspends and later corrects. Software Timers and Delays owns what a delay means when the tick that quantises it can itself stop. Sleep Modes owns the Stop-mode current and wake-latency figures that make suppressing the tick worth doing in the first place.
Why a periodic tick blocks deep sleep
Every tick is, from the processor's point of view, an ordinary interrupt: it fires, it wakes the core out of whatever low-power mode WFI last entered, xTaskIncrementTick() runs, and unless it decided a context switch is needed, the idle task's WFI puts the core straight back to sleep. Two costs are paid on every one of those cycles even when nothing of substance happened:
- The wake-up cost itself. In the deepest Stop variant from Sleep Modes, that is the low-power-regulator switch-back plus, if flash was powered down, the ART accelerator refilling its buffers — real microseconds, on every single tick.
- A ceiling on how deep the mode can be. SysTick's own clock is derived from
HCLK(PM0214 §4.5), which Stop mode stops. A system that never suppresses the tick therefore cannot use SysTick to time its own reawakening past a tick period in the first place — it has to be woken by something else on the same schedule, which is a second timer doing a job the tick is already conceptually doing, badly, once every millisecond.
configUSE_TICKLESS_IDLE exists to remove both costs at once: when the kernel can prove nothing needs to run for a while, it stops generating ticks entirely for that interval, instead of generating them and discarding almost all of them.
What the kernel actually does
Tickless idle is not a register you set on a peripheral; it is logic that runs in the idle task, replacing the plain __WFI() that Tasks and Scheduling mentions as the un-tickless default. With configUSE_TICKLESS_IDLE set to 1, the idle task's loop becomes, in outline:
- Compute the expected idle time.
xNextTaskUnblockTime - xTickCount— the number of ticks until the earliest thing in the system needs to wake, capped at whatever the low-power timer being used can actually count. If that value is smaller thanconfigEXPECTED_IDLE_TIME_BEFORE_SLEEP(default 2 ticks), tickless idle does not engage at all — the overhead of suspending the scheduler and reprogramming a timer is not worth it for an interval that short, so the kernel just runs a normalWFIand waits for the next tick as usual. - Suspend the scheduler and disable interrupts, then call
eTaskConfirmSleepModeStatus(). This is the race guard, and it exists because step 1 was computed with interrupts briefly re-enabled: something could have made a task ready, or a tick could already be pending, in the gap between that computation and this check. The function returns one of three values —eAbortSleepif a context switch is already pending or a tick interrupt fired but has not yet been processed (do not sleep at all, let the normal path run),eNoTasksWaitingTimeoutif nothing in the system has a wake deadline (sleep can be as long as the low-power timer's maximum, since nothing but an external event needs to end it), oreStandardSleep(sleep for exactly the computed idle time, no longer). - Reprogram a low-power timer for the confirmed idle time and enter
WFI. This is the port-specific part,portSUPPRESS_TICKS_AND_SLEEP(), and it is also where "which mode" gets decided. The reference Cortex-M ports reprogram SysTick itself with an extended reload value and enterWFIwithSLEEPDEEPclear — plain Sleep, not Stop. That is a direct consequence of the clock argument above: SysTick's own clock survives Sleep (only the CPU clock stops), so SysTick can still be the thing measuring the sleep interval. It cannot do that job in Stop, because Stop is exactly the mode that gatesHCLKand therefore SysTick's clock too. Reaching Stop with tickless idle needs a timer wired to a clock domain Stop does not touch — on this part, that is the RTC wakeup timer, which Wake Sources and Event-Driven Design already establishes as clocked from LSE/LSI and unaffected by Stop's clock gating. A tickless implementation that wants Stop-mode current has to replace the SysTick-reload logic with "set the RTC wakeup timer for N ticks' worth of RTC clocks, then enter Stop," and correctxTickCountfrom that timer's counter on the way out instead of SysTick's. - On wake, correct the tick count for what actually happened, not for what was asked for. If the full computed idle time elapsed,
vTaskStepTick(xExpectedIdleTime)simply advancesxTickCountby that many ticks. If the wake was early — some other interrupt fired before the low-power timer expired — the port has to read back how much of the programmed interval actually passed (SysTick'sLOAD/VALdifference, or the RTC wakeup timer's remaining count) and step the tick count by that smaller amount instead. Getting this step wrong in either direction is the whole risk this page is about: stepping too far runsxTickCountahead of real time, and every delayed task in the system wakes early relative to the clock on the wall; stepping too little leaves it behind, and every deadline computed from it fires late.
The accuracy bound
The correction in step 4 can only be as accurate as the timer it is computed from, and that timer's resolution and drift are what set the ceiling on how long a single tickless sleep is safe to request.
| Low-power timekeeping source | Resolution | Drift | Reaches |
|---|---|---|---|
SysTick, HCLK or HCLK/8 | one core-clock period or one eighth of it — sub-microsecond at any Run-mode frequency on this part | negligible over a single sleep interval; it is the same clock the rest of the system already trusts | Sleep only — its clock is gated in Stop |
| RTC wakeup timer, LSE-clocked | RTCCLK/16 down to ck_spre depending on the chosen prescaler — tens of microseconds to whole seconds per step, RM0383 RTC chapter | crystal-grade, low tens of ppm — the same source RTC and Timekeeping already relies on for wall-clock accuracy | Stop and Standby |
| RTC wakeup timer, LSI-clocked | same divider options, but against an internal RC oscillator | RM0383 documents LSI as loosely specified and temperature-dependent; this page did not re-verify the exact percentage this session — read RM0383's LSI characteristics table before relying on it for anything longer than a few seconds | Stop and Standby, with correspondingly looser absolute time |
Two things fall out of that table directly. First, the granularity of the wakeup timer is a floor on the correction, not just a nuisance: if the RTC wakeup timer's configured step is, say, 61 µs and configTICK_RATE_HZ is 1000 (a 1 ms tick), every tickless wake can only correct xTickCount to the nearest 61 µs, and that quantisation accumulates across repeated sleep–wake cycles exactly the way Software Timers and Delays describes pdMS_TO_TICKS() truncation accumulating — except here it is unavoidable rather than a rounding choice, because it is the hardware's own granularity. Second, LSI drift, if that is the clock behind the wakeup timer, bounds how long any single sleep should be requested for, independent of how deep the mode is or how little current it draws: a long sleep timed against a loosely-specified RC oscillator is a long sleep whose waking edge has genuine, compounding uncertainty, and a design with real wall-clock deadlines should prefer LSE for exactly this reason.
The expected-idle-time calculation, and why the cap matters
xExpectedIdleTime is not simply "how long until the next xTaskDelayUntil() fires." It is the minimum of that and every other thing in the system with a deadline — a software timer's next expiry, a queue receive with a timeout, the low-power timer's own maximum countable interval — because tickless idle has to wake in time for the earliest of them, not the average. eTaskConfirmSleepModeStatus()'s eNoTasksWaitingTimeout case is the degenerate end of this: when nothing in the whole system has a timeout at all, the "expected idle time" is unbounded, and the kernel arms the low-power timer for the longest interval it can express and waits for an external event — an EXTI line or an RTC alarm, per Wake Sources and Event-Driven Design — to end the sleep instead of a timeout doing it.
configEXPECTED_IDLE_TIME_BEFORE_SLEEP's default of 2 ticks is a deliberate floor, not an arbitrary one: entering tickless idle costs the scheduler suspension, the confirmation check, and the timer reprogramming, and for an idle interval of one tick that overhead can exceed the tick period it was meant to save. Raising the threshold makes sense on a system whose typical idle intervals are long and whose short ones are rare; lowering it below the default is rarely useful, because a genuinely one-tick idle gap was never going to reach a low-power mode's break-even point in the first place — see Clock and Peripheral Gating for the same break-even argument applied to run-fast-then-sleep instead of tick suppression.
A custom portSUPPRESS_TICKS_AND_SLEEP() — written when porting to a timer the reference implementation does not support, or when moving the Stop-mode-capable version onto the RTC wakeup timer as described above — has exactly one correctness obligation: after waking, measure how much of the programmed interval actually elapsed and step xTickCount by that amount, not by the full interval it was armed for. It is a natural mistake to skip that measurement, because most wakes are on time and the code appears to work through ordinary testing: a UART byte arriving mid-sleep, a GPIO interrupt, or any other asynchronous event ends the WFI early, and a port that unconditionally advances the tick by the full xExpectedIdleTime it originally requested runs the clock ahead of real time by the unslept remainder, every single time this happens. The error is silent and compounding — nothing crashes, nothing asserts — and it surfaces as xTaskDelayUntil() periods that are consistently short, software timers that fire early relative to the wall clock, and a system time that visibly outruns an attached RTC or NTP reference over hours of runtime, even though every individual delay call in the source code is correct. The fix is always the same shape: read the low-power timer's own remaining-count register before clearing it, not after, and pass the elapsed ticks to vTaskStepTick(), never the requested ones.
See also
- Sleep Modes — the Stop-mode current and wake-latency figures that make suppressing the tick worth the added complexity.
- Wake Sources and Event-Driven Design — the RTC wakeup timer and EXTI mechanics a Stop-capable tickless implementation and the
eNoTasksWaitingTimeoutcase both depend on. - Tasks and Scheduling — the tick, the delayed lists, and
xNextTaskUnblockTime, all of which this page'sxExpectedIdleTimecalculation is built from. - Software Timers and Delays — what a delay means once the tick quantising it can itself stop, and the same truncation hazard applied to
pdMS_TO_TICKS(). - RTC and Timekeeping — the LSE/LSI accuracy trade-off behind the drift row in the table above.
References
- Amazon Web Services — FreeRTOS: low power support and
eTaskConfirmSleepModeStatus. Verified against these:configUSE_TICKLESS_IDLE,configEXPECTED_IDLE_TIME_BEFORE_SLEEP,configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING, theeTaskConfirmSleepModeStatus()race check and its three return valueseAbortSleep,eStandardSleep, andeNoTasksWaitingTimeout, and the requirement that the port-supplied sleep function correct the tick count for the time actually slept. (Documentation checked 2026-08-26.) - FreeRTOS-Kernel —
History.txtand the Cortex-Mport.c/portmacro.hsources underportable/. Confirmed from the changelog:portSUPPRESS_TICKS_AND_SLEEP()andvTaskStepTick()introduced together,configEXPECTED_IDLE_TIME_BEFORE_SLEEPandeTaskConfirmSleepModeStatus()added in the Cortex-M3 tickless overhaul, and tickless low-power support subsequently carried into the GCC/IAR/ARM Cortex-M0 and other ports. (Source checked 2026-08-26.) - STMicroelectronics — RM0383, STM32F411xC/E advanced Arm-based 32-bit MCUs reference manual, consulted at Rev 4. The RTC chapter for the wakeup-timer prescaler options (
RTCCLK/16 throughck_spre) that set the resolution row above, and the clock-tree chapter for which clocks Stop mode gates. - STMicroelectronics — PM0214, STM32 Cortex-M4 MCUs and MPUs programming manual, Rev 10. §4.5 for the SysTick timer, its
HCLK/HCLK÷8 clock source options, and why its count is unavailable once that clock is gated.