kernel::power
Power management: sleep, and reconciling the kernel tick on wake.
The chip-level sleep FSM is reached through the [hal::power::LowPower]
seam, selected with the SoC (board::SelectedSoc) exactly as
[crate::clock] selects its counter; this module is the kernel half — it
drives that primitive and, crucially, keeps the scheduler’s clock honest
across a light sleep, naming no chip to do it.
Why the tick needs reconciling
Section titled “Why the tick needs reconciling”The system tick is driven by the Xtensa CCOUNT/CCOMPARE0 timer, which
counts CPU cycles. Light sleep gates the CPU clock, so CCOUNT — and the
Timer0 interrupt that increments the tick — both freeze for the whole
sleep. On wake the kernel’s notion of time is behind reality by exactly the
time slept. Left alone, every sleep_ms, timeout and timestamp taken after
the sleep would be wrong, and a task that slept “until now + 100 ms” would
wake far too late.
The RTC slow clock keeps running through light sleep, so the SoC primitive
measures the real elapsed time from it and hands it back. This module turns
that into whole tick periods and adds them — [ticks_elapsed] — so the
tick catches up. It only ever adds: the counter cannot go backwards, no
matter what the measurement returns.
Deferred
Section titled “Deferred”- Deep sleep returns nothing to reconcile — it wakes as a reset, so the
tick restarts from zero with the rest of the system.
deep_sleepis here for completeness and is exercised by construction, not by the self-test (it would reset the board mid-run). - Auto light sleep from the idle task (the
waitihook) is a follow-up; this cut only offers an explicit call. - Non-timer wake sources (RTC-GPIO, touch, ULP) are a follow-up in the SoC layer.
Functions
Section titled “Functions”ticks_elapsed
Section titled “ticks_elapsed”fn ticks_elapsed(elapsed_us: u64, period_us: u32) -> u64Whole tick periods in elapsed_us, for reconciling the tick after a sleep.
Rounds down: a residue shorter than one period cannot advance a whole-tick counter, and rounding up would let repeated short sleeps run the clock fast. The lost sub-tick remainder is bounded by one period (1 ms on every current board) and does not accumulate across calls, because each sleep measures its own true elapsed time from the RTC rather than trusting the requested duration.
period_us of zero — the tick timer never initialised — yields zero rather
than dividing by zero, so a reconciliation before boot completes is a no-op
instead of a fault.