Skip to content

[EXPERIMENTAL — do not merge] Deep sleep: drop debug-UART RX block#54

Draft
Coral-coder wants to merge 5 commits into
mainfrom
power/experimental-deep-sleep
Draft

[EXPERIMENTAL — do not merge] Deep sleep: drop debug-UART RX block#54
Coral-coder wants to merge 5 commits into
mainfrom
power/experimental-deep-sleep

Conversation

@Coral-coder

Copy link
Copy Markdown
Owner

⚠️ EXPERIMENTAL — power investigation, not for merge

Hypothesis being tested

On debug builds the watch never reaches true DEEPSLEEP — it's capped at DEEP-WFI. Root cause found in the trace: the debug console arms the UART RX receiver at boot (main.c:189dbgserial_input.c:30), and enabling RX takes a permanent DEEPSLEEP sleep-block (uart/sf32lb.c:137). It's a pure software refcount held the whole uptime — independent of whether anything is connected to the port. Because the block is at the DEEPSLEEP level, deep-WFI stays allowed, which is exactly the observed 0 deepsleep, deep-wfi is the floor.

What this build does

Adds CONFIG_EXPERIMENTAL_DEEP_SLEEP (enabled for obelix here) that drops the UART RX deep-sleep block (symmetrically — no refcount imbalance). Log output over TX is unaffected; console input is lost only while the chip is actually asleep. This lets the SoC reach true deep sleep when idle, to prove on-device that the console block is the ceiling.

Also splits the Idle States HUD into a 4-way breakdown so deep sleep is visible separately:

dsleep N     <- true deep sleep (was folded into deep before; should jump 0 -> high)
dwfi   N     <- deep WFI
wfi    N     <- light WFI
run    N     <- executing

How to run the experiment

  1. Flash this build, enable Settings → Debugging → Idle States HUD.
  2. Use a static watchface (tictoc / minute-tick) — an animated face keeps a 33 ms frame timer alive that independently blocks deep sleep.
  3. Screen off, wait, wake and read:
    • dsleep jumps from 0 to a high number → hypothesis confirmed: the console UART block was the ceiling. Next step is a proper gate (only block while a byte is mid-reception).
    • dsleep stays 0 → the UART block was not the cause; look elsewhere (I was wrong).

Build note: compiles + links for obelix@pvt. (A clean local rebuild trips an unrelated pre-existing -Werror=maybe-uninitialized in the SiFli SDK bt_rf_fulcal.c; CI builds that same tree green, so it's a local-toolchain artifact, not from this change.)

Close without merging once the result is captured.


Generated by Claude Code

Coral-coder and others added 5 commits July 7, 2026 01:45
Add monotonic (never-reset) counters for time spent in WFI, deep WFI and
deep sleep, alongside the existing analytics counters (which reset each
collection). A per-minute sampler, piggybacked on the existing minute
regular-timer so it adds no wake-ups, records deep-sleep and wall-clock
deltas into a 60-entry ring covering the last hour.

Expose soc_sf32lb_deep_sleep_residency_permille(minutes) for the last
1/10/60 minutes and soc_sf32lb_cpu_time_get() for the since-boot totals,
to back an on-watch Deep Sleep Stats screen. Started from
services_normal once the regular-timer service is up.

Signed-off-by: Coral Coder <dsthunder@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Coral Coder <dsthunder@gmail.com>
Add a "Deep Sleep Stats" row (sf32lb52 only) under Settings ->
Debugging that opens a live screen showing the fraction of time the
chip spent in deep sleep over the last 1 minute, 10 minutes and 1 hour,
plus the current battery drain rate and estimated time to empty. The
values refresh once a second while the screen is open.

Signed-off-by: Coral Coder <dsthunder@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Coral Coder <dsthunder@gmail.com>
Swap the static Deep Sleep Stats sub-screen for a toggleable overlay that
floats over the watchface and reports how many milliseconds per second the
chip spent in, and out of, deep sleep, averaged over the trailing minute.

Replace the per-minute residency ring with a lazily-sampled rolling window
that yields deep-sleep ms per wall-clock second. It samples only when read,
so it adds no wake-ups and costs nothing while the overlay is off.

The HUD is a discreet, transparent, unfocused modal, so it redraws with the
watchface on each wake (via the AppAndModal compositor path) rather than
running a timer that would keep the watch awake.

The toggle lives under Settings -> Debugging (sf32lb52 only) and persists
across reboots via a new shell pref.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Coral Coder <dsthunder@gmail.com>
True deep sleep is unreachable in normal always-on use: it needs a >=50 ms
uninterrupted idle window the workload rarely leaves, so the chip's real
resting floor is deep WFI. The old HUD read the deep-sleep counter, which
sits at ~0, and lumped deep WFI into "awake" -- misleading.

Point the metric at the states the chip actually reaches: report a
per-minute idle breakdown -- deep (deep WFI or deeper), light WFI, and
running -- each in average ms per wall-clock second, summing to ~1000.
Rename the debug row to "Idle States HUD".

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Coral Coder <dsthunder@gmail.com>
The debug console arms the UART RX receiver for the whole uptime, which
holds a permanent DEEPSLEEP sleep-block and caps the chip at deep WFI --
so true deep sleep never happens on debug builds, regardless of whether
anything is connected to the port. Add CONFIG_EXPERIMENTAL_DEEP_SLEEP
(enabled for obelix here) that drops that block so the SoC can reach deep
sleep when idle, to prove on hardware that the console block is the ceiling.

Also split the Idle States HUD into a 4-way breakdown -- dsleep / dwfi /
wfi / run -- so true deep sleep shows separately from deep WFI when it
engages.

For power investigation only; not for merge.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Coral Coder <dsthunder@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant