0009. PresTag stop delays use a free-running RTC Alarm A, not a re-armed LPTIM1
Date: 2026-09-09
Cut verbatim from sections 1.2b and 1.2c of
the PresTag power test plan;
implemented in 0ac8bc6. Section numbers (§) refer to that plan. The rest of
the diagnosis is in
the campaign investigation.
Context
1.2b Measured: stopMilliseconds() spins ~6.7 ms and never sleeps
Every driver wait on this target goes through stopMilliseconds(), which arms
LPTIM1 for the requested delay and enters Stop 2 with WFE. Instrumented with
retained timestamps (see §11), the call behaves like this for requests of 2 ms
and 5 ms alike:
| Step | Measured |
|---|---|
| bus disable, LPTIM clock/enable | ~0.1 ms |
ARR write → ARROK seen (while (!(ISR & ARROK)) {}) |
6.3–7.1 ms, Run current |
WFE loop until ARRM |
0.0–0.1 ms, exactly 1 iteration |
The cause is the clock. LSE on this board is 1024 Hz, and LPTIM1 runs from
it (RCC_CCIPR.LPTIM1SEL = 11, read live), so TAG_STOP_LPTIM_HZ = 1024 is
right and a 5 ms request correctly programs 6 ticks. But LPTIM register writes
— ARR, and every ICR flag clear — only take effect after kernel-clock edges,
and at 1024 Hz each edge is ~1 ms. The ARROK wait therefore burns ~7 cycles of
Run current, longer than the delay it is arming; and the ARRM clear written
immediately before the WFE has not propagated when the loop reads ISR, so
it sees the previous call's match still set and returns without sleeping.
Net effect: the Stop 2 sleep never happens, the wait lasts ~6.7 ms whatever
was requested, and all of it is at Run current — three per sample (two LPS27
waits, one flash-wake wait), ~20 ms and roughly a third of Q_cycle, the flash
one dearest because the AT25 is awake underneath it.
The AT25 recharge metering is therefore over-satisfied in time (7 ms for a 2 ms request) but paid for at Run current instead of in Stop.
Fix direction: the synchronisation cost has to become small relative to the
delay, and the flag check must not race the clear. Options, in rough order of
preference: (a) clock LPTIM1 from LSI (~32 kHz, available in Stop 2; LPTIM1SEL
is independent of RTCSEL, so the RTC stays on the 1024 Hz LSE) with a
prescaler to taste and TAG_STOP_LPTIM_HZ updated — sync drops to tens of µs;
(b) keep 1024 Hz but never rewrite ARR: free-run, use CMP = CNT + ticks and
wait for CMPOK by sleeping, accepting ~1–2 ms of granularity; (c) the RTC
wakeup timer, which has the same LSE-domain sync cost. Whichever is chosen must
then be shown to sleep — a dip to ~2 µA for the requested duration in a fine
trace — rather than argued.
Decision
The LPTIM arming cost. Re-arming LPTIM1 per delay costs an ARROK
busy-wait of 6.3-7.1 ms at Run current on a 1024 Hz LSE, longer than the delay
it arms. Replaced by a free-running RTC Alarm A tick (§1.2d).
Evidence
| build | Q_cycle |
I_avg at 10 s |
|---|---|---|
| LPTIM, floating pin | 34.06 µC | 3.6948 µA |
| LPTIM + pin fix | 26.82 µC | 2.9742 µA |
| Alarm A + pin fix | 14.49 µC | 1.7376 µA |
Consequences
The mechanism as built is described in §1.2d of the test plan.