0007. STM32U375 run-mode sleep is Stop 2, not Stop 1
Date: 2026-09-07 (commit e08e3fab)
The text below is cut verbatim from Open Issues; "the Standby entry below" refers to the Standby layout investigation.
Context
RESOLVED by Stop 2: run-mode Stop 1 current is layout-dependent too
The same fault family as the Standby entry below, at the idle thread's sleep during RUNNING, found on 2026-09-07 when run current rose from a remembered 548/815 uA at 100/400 Hz to 869/984.
It is not a failure to sleep. A probe in the retained SRAM2 page counted
what the idle hook actually did over a 37 s run at 100 Hz: the Stop path was
taken 1086 times, LPMS=1, STOPF=1, and RTC_SSR -- which runs in every
low-power mode -- measured 36.4 s of the 37 s asleep in Stop, mean dwell
33 ms. The stIsAlarmActive() fallback to plain SLEEP fired 8460 times but
for 0.09 ms each, 0.75 s in total. The peripheral state latched at the instant
of Stop entry (SPI1, I2C1, all the RCC ENR/STPENR words, GPIO ODRs, ADC, LPTIM1,
PWR_CR2, RCC_CR, CCIPR1) was identical between builds.
What varies is the current drawn while in Stop 1. At 1 ms resolution the whole distribution shifts, first percentile included (656 uA against 558), so it is a raised sleeping floor and not more time awake.
| build (differs only in layout) | 100 Hz | 400 Hz |
|---|---|---|
| without the page-skip commit | 671 | 811 |
| with it (HEAD) | 866 | 980 |
HEAD + 8 or 16 inert nops |
866 | -- |
| with an idle-branch probe compiled in | 670 | -- |
| with probe + register snapshot | 867 | -- |
Reproducible to 0.3 uA across three run cycles per build, and the probe builds
flipped both directions, which is what identifies it as layout rather than as
anything the page-skip code does -- that code cannot execute in a healthy run,
and noinline on it changed nothing.
Evidence
Stop 2 is not affected. Same three layouts, 100 Hz: 605.0, 604.8, 604.9 uA -- a 0.2 uA spread against 195. It is also lower at every rate, and collection, timing and download pass at all of them:
| mode | 100 Hz | 400 Hz | 800 Hz | 1600 Hz |
|---|---|---|---|---|
| Stop 1, best layout | 671 | 811 | -- | -- |
| Stop 1, bad layout | 866 | 980 | -- | -- |
| Stop 2 | 605 | 746 | 959 | 1133 |
Decision
The fix is IMUTAG_RUN_SLEEP_MODE, a per-target selector in the IMUTag
state_run.c defaulting to the historical USE_STOP1 choice, with
IMUTagNandBmp581 setting STOP2. USE_STOP1 stays 1 there because
stopMilliseconds() and the LPTIM1 trigger gate still read it. Other targets
are untouched.
Consequences
One caveat, recorded rather than dismissed. The Stop 2 release check saw a
single SetRtc failed: RTC sync failed while writing tag clock in its 30 clock
cycles, where every Stop 1 gate that day was clean over 200. Stop 2 powers down
more than Stop 1, so this was chased: two dedicated 30-cycle runs on Stop 2
were clean (89/90 in total) and a Stop 1 control under the same rig conditions
was 30/30. That points at the known RV-3028 write intermittent rather than at
Stop 2, but the gate runs 30 clock cycles each time and will show a real rate
difference if there is one.
The lesson for the release gate: four consecutive tag_release_check.py
runs reported this run current and passed, because the gate bounds idle current
and only reports the run. Run current is what sets battery life during a
deployment, and it is now the second place a low-power mode has proved
layout-dependent. Bound it.