Skip to content

0x81 bursts on L4 tags: DBG_SLEEP clear during a monitor session (2026-10)

Closed: fixed in both tagcore libraries (commit 0ba7dc22), which set DBGMCU_CR.DBG_SLEEP right after MONITORSTART on an STM32L4 tag.

The cause

On an STM32L4 with DBGMCU_CR.DBG_SLEEP clear, Sleep mode stops HCLK (FCLK keeps running). A tag's RTOS idles in __WFI() with SCB_SCR.SLEEPDEEP clear, which is Sleep mode, whenever every thread waits: on an I2C transfer, a test delay, a flash poll. While the core sleeps, every debug access through the AHB-AP answers WAIT, for the whole wait. The base retries a WAIT MAX_SWD_RETRY (25) times with no delay, under 2 ms on tag-breakout-base-l432-v1, then reports the access as failed, which the host sees as STLINK_DEBUG_ERR_FAULT (0x81). Its error recovery then writes into the same stalled bus, so one wait becomes a burst. Nothing in the tree set DBG_SLEEP; the tag's Stop and terminal-sleep paths only clear DBGMCU_CR. ST's own tools set it when they connect, which is why the bursts never showed with them.

Evidence

All on 2026-10-27, with both bases built to log every SWD fault (with the DP CTRL/STAT sticky bits), every WAIT that outlasts the retries, and the longest WAIT run per session on a USB virtual serial port (branch base-0x81).

Setup Result
bittag-base-v7, BitTag LE 2039354D30315012005E0068, firmware ceca92e6 and 62273e39, 3.22 V No 0x81 in about 2,800 status reads across Synchronize Clock and Self-Test, polled every 50-400 ms, with current and pre-fix host code. Longest WAIT run 3 of 25: this tag does not wait long in those commands.
tag-breakout-base-l432-v1, UIUCTag 2036354B3032500800520028, firmware 7c508f46, 3.28 V Bursts on every Synchronize Clock and Self-Test. The base logged swd wait x25 on data-register reads (CTRL/STAT F0000F00: no sticky errors) and on its recovery writes (F0000F80: WDATAERR).
Same, base built with a 100000-retry budget Every stall completed. Lengths: 9-10 ms during Synchronize Clock (I2C to the RV3028), 150, 300 and 512+ ms during Self-Test, matching AT25XE_SECTOR_ERASE_POLL_INTERVAL (150 ms) and other waits.
Registers read while attached DBGMCU_CR = 0, SCB_SCR = 0 (Sleep, not Stop), PWR_CR1 = 0x500, FLASH_ACR = 0x700, SRAM1 and flash Sleep-mode clocks enabled.
Same tag, DBG_SLEEP set after attach (provoke_0x81 --dbg-sleep) 0 of 48 status reads failed; no base faults; longest WAIT run 3.
Same tag, host fix only 0 of 72 status reads failed over three rounds; three C++ tag-test runs passed with no 0x81; longest WAIT run 2-4.
Resting current after attach and detach, 2.496 V, Joulescope 0.1618 uA after the fixed host, 0.1617 uA after the old host (two 10 s windows each); 0.167 uA from cold. The tag clears DBGMCU_CR itself after the session.

The same cause explained a second fault: on every detach, SWD_Close()'s first write went unacknowledged (trans fail 7), because the tag was already asleep with HCLK stopped. With DBG_SLEEP set it is acknowledged.

Ruled out on the way

  • The host's idle-wait fix (125fb28c, 71ab1df4): the pre-fix host showed no bursts on the BitTag LE either.
  • The base's retry budget alone: it is short, but the tag held the bus off for up to half a second; no count of immediate retries rides that out.
  • Stop mode: stopMilliseconds() uses a plain RTOS sleep during a monitor session, and SCB_SCR was 0.

The UIUCTag's self-test also erased and rewrote an external flash sector (left from bring-up debugging); it now uses the shared identity-only test (9c7259a0). That shortened nothing that mattered: the other test delays held the bus as long.