Skip to content

Commit 0435c78

Browse files
doc: SWD flashing RAM-clear contract
After st-flash write, the bootloader and main firmware see whatever was in shared RAM (0x20000000+) and backup SRAM (0x40024000) at the moment SWD started talking to the MCU. That stale state can: - trigger a false firmware-update on next boot (shared RAM holds the FwAutoUpdate flag; ~3% chance of a random byte matching one of the "do an update" enum values → bootloader stuck at 50% with no BBF to flash); - trigger a false power-panic recovery (backup SRAM holds the power_panic magic + CRC; in practice protected by the CRC check but still wastes boot time). A power-cycle drops both regions because they only persist via V_BAT, but SWD-only flashing has no chance to invoke that. The openocd-side clear sequence used in flash_coreone.sh is documented here. This is documentation only — no code change. The expectation is that anyone debugging via SWD now has a single reference for "why does the printer hang at the bootloader bar after my flash" and how to fix it in their openocd script.
1 parent e96ce2b commit 0435c78

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

doc/swd_flashing.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# SWD flashing — RAM state contract
2+
3+
When flashing Buddy firmware via SWD (e.g. with `st-flash` + ST-Link), the
4+
default flow is **incomplete on its own** and can leave the printer in a
5+
confused or stuck state on the next boot. This page documents the missing
6+
clear sequence and explains why.
7+
8+
## TL;DR
9+
10+
After `st-flash write`, run **before** the MCU reset:
11+
12+
```tcl
13+
# OpenOCD script (interface/stlink.cfg + target/stm32f4x.cfg)
14+
15+
# 1. Reset boot-firmware data exchange in shared RAM
16+
# (DataExchange struct in src/common/data_exchange.cpp,
17+
# mapped to section .boot_fw_data_exchange at 0x20000000)
18+
mwb 0x20000000 0x00 ; fw_update_flag = FwAutoUpdate::off
19+
mwb 0x20000001 0x01 ; appendix_status (set to "present")
20+
mwb 0x20000002 0x00 ; fw_signature
21+
mwb 0x20000003 0x01 ; bootloader_valid
22+
23+
# 2. Enable backup-SRAM clock and PWR access
24+
mmw 0x40023830 0x00040000 0 ; RCC_AHB1ENR |= BKPSRAMEN (bit 18)
25+
mmw 0x40007000 0x00000100 0 ; PWR_CR |= DBP (bit 8)
26+
27+
# 3. Clear the first 16 bytes of backup SRAM
28+
# (power-panic storage in src/common/power_panic_storage_bkpsram.cpp)
29+
mww 0x40024000 0x00000000
30+
mww 0x40024004 0x00000000
31+
mww 0x40024008 0x00000000
32+
mww 0x4002400C 0x00000000
33+
34+
# 4. NVIC system reset via AIRCR (writes the VECTKEY + SYSRESETREQ)
35+
mww 0xE000ED0C 0x05FA0004
36+
shutdown
37+
```
38+
39+
Skip step 1 and the bootloader may interpret stale RAM as a request to
40+
re-flash from USB, freezing at "50%" with no BBF to flash. Skip step 3
41+
and the firmware may interpret stale backup SRAM as a recoverable
42+
power-panic from a prior session.
43+
44+
## Why st-flash alone isn't enough
45+
46+
The xBuddy STM32F427 has three RAM regions that survive an SWD flash:
47+
48+
| Region | Address | Used by |
49+
| ----------------- | ------------- | ---------------------------------------- |
50+
| Shared SRAM | `0x20000000` | DataExchange between bootloader & FW |
51+
| Main SRAM | `0x20000020+` | Normal heap/stack |
52+
| Backup SRAM | `0x40024000` | Power-panic state (`MAGIC_VALID_VALUE`) |
53+
54+
`st-flash write` only touches the application area in internal flash
55+
(`0x08020000` onwards). It does NOT clear any of the SRAM regions
56+
above, and it does NOT issue the same kind of cold-boot reset the
57+
printer performs via its power switch.
58+
59+
The result is that on the next boot, both the bootloader and the main
60+
firmware see whatever values were in RAM when SWD started talking to
61+
the MCU — which is usually leftover state from the firmware that was
62+
running just before the flash. Two specific failure modes:
63+
64+
### 1. False firmware-update trigger (shared SRAM)
65+
66+
The DataExchange struct at `0x20000000` starts with:
67+
68+
```cpp
69+
struct __attribute__((packed)) DataExchange {
70+
FwAutoUpdate fw_update_flag; // 1 byte, enum
71+
uint8_t appendix_status;
72+
uint8_t fw_signature;
73+
uint8_t bootloader_valid;
74+
...
75+
};
76+
```
77+
78+
`FwAutoUpdate` is an `enum class : uint8_t` with values like `on=0xAA`,
79+
`off=0x00`, `older=0x55`, `specified=0xBB`. The bootloader reads this
80+
byte during boot to decide whether to re-flash from `firmware.bbf` on
81+
USB. If the leftover byte happens to match one of the "do an update"
82+
values (~3% chance per random byte across the enum range), the
83+
bootloader hangs trying to flash a file that isn't there — the
84+
infamous "stuck at 50%" symptom.
85+
86+
Clearing the first four bytes to `00 01 00 01` puts the bootloader
87+
back in the normal "no update requested, appendix present, signed"
88+
state.
89+
90+
### 2. False power-panic recovery (backup SRAM)
91+
92+
`backup_sram_data_t` at `0x40024000` ends with a 32-bit `magic_valid`
93+
field set to `0xFA0746DC` whenever there's recoverable state. The
94+
firmware also checks CRCs over the data, so a truly random garbage
95+
match is extraordinarily unlikely (~1/2³² × CRC odds). In practice
96+
this is much less of a problem than the shared-RAM case — but the
97+
power-panic path takes long enough to evaluate that even a *correct*
98+
"no panic recorded" determination delays boot. Clearing the magic
99+
upfront skips the check entirely.
100+
101+
If you only want to fix one thing, fix shared SRAM.
102+
103+
## Reference openocd script
104+
105+
A working end-to-end SWD flash including the steps above is in the
106+
project's `flash_coreone.sh` (downstream — not in the upstream tree).
107+
The relevant openocd invocation is:
108+
109+
```sh
110+
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
111+
-c "init; halt" \
112+
-c "mwb 0x20000000 0x00; mwb 0x20000001 0x01; mwb 0x20000002 0x00; mwb 0x20000003 0x01" \
113+
-c "mmw 0x40023830 0x00040000 0; mmw 0x40007000 0x00000100 0" \
114+
-c "mww 0x40024000 0x00000000; mww 0x40024004 0x00000000; mww 0x40024008 0x00000000; mww 0x4002400C 0x00000000" \
115+
-c "mww 0xE000ED0C 0x05FA0004" \
116+
-c "shutdown"
117+
```
118+
119+
## Notes
120+
121+
- A hard power-cycle (turn the printer off at the wall, wait ~5 s, on
122+
again) accomplishes the same thing as the openocd sequence, because
123+
all SRAM regions lose state without standby power on the V_BAT pin.
124+
The openocd path exists so the SWD flow can be fully scripted
125+
without physical access to the power switch.
126+
- These details apply specifically to the xBuddy STM32F427-based
127+
printers (Core One/+, MK4, MK3.9, MK3.5, …). Other Buddy variants
128+
(Mini, XL) may have different RAM layouts.

0 commit comments

Comments
 (0)