Files
betaflight/lib/main/STM32/n6_obl/Unbricking.md
T
2026-08-03 16:37:10 +08:00

7.6 KiB

Unbricking the STM32N6 (TSV + system DFU)

Use this when OBL or BF in XSPI flash is broken and the board no longer boots into a usable state — bl rom from the BF CLI, OBL DFU on a crashed BF, and SWD-load workflows are all unavailable. The boot ROM's built-in system DFU is always reachable, but it can't write the on-board XSPI flash on its own; CubeProgrammer drives it through a TSV that stages two helper binaries into RAM first.

Why the on-chip DFU can't just write XSPI

The N6 boot ROM exposes a system DFU bootloader (0483:DF11 with the ST descriptor "STM32 BOOTLOADER", reached via BOOT0=DEV) but the alts it offers are RAM- and OTP-shaped:

@FSBL        /0x34180400/01*512Kg     -- staged FSBL → AXISRAM2 secure
@RSSE_FW     ...
@RSSE_BLOB   ...
@RSSE_PLUGIN ...
@virtual     ...

There's no @nor0 alt and no XSPI alt — the ROM has no XSPI driver. To write nor0 you have to:

  1. Upload a signed FSBL into RAM via the @FSBL alt. The ROM verifies it, copies it to AXISRAM2, and runs it.
  2. The FSBL you upload is itself a DFU loader (ST's "OpenBootloader") that exposes @nor0 plus an "external memory loader" alt for the chosen flash chip's driver.
  3. Upload the matching XSPI flash driver into RAM via the @External memory loader alt; the OpenBootloader links it in and the rest of the DFU session can read/write nor0.
  4. Now write your application binary to nor0 at the right offset.

The TSV (Target Sequence Vector) file is just a script for STM32_Programmer_CLI -d <FlashLayout.tsv> that orchestrates these four steps in order so it looks like a single flash operation from the host's point of view.

When to use this

  • bl rom from BF CLI / OBL DFU :leave doesn't reboot the chip into a working state.
  • BF crashes early enough that the BF↔OBL IWDG-recovery path doesn't trigger (e.g., a fault before OBL's iwdg_start runs, or OBL itself is corrupt).
  • You want to nuke and re-flash both OBL and BF from a clean slate.

If the chip is reachable via SWD (BOOT1 in the position that bypasses the FSBL on the dev kit, or BF still alive enough that OBL DFU comes up), prefer the SWD-load or OBL-DFU paths — they don't require touching hardware switches.

What you need

  • STM32CubeProgrammer 2.18+ with the N6 patches installed. The Linux build prior to 2.22 has bugs around the N6 external-loader workflow; current ST tooling works.
  • OpenBootloader_STM32N6570-DK-trusted.stm32 — ST's signed OpenBootloader image. Ships in CubeN6 at lib/modules/STM32N6/Projects/STM32N6570-DK/Applications/OpenBootloader/Binaries/NOR_Binary/.
  • MX66UW1G45G_STM32N6570-DK.bin — XSPI flash driver matching the on-board MX66UW1G45G. Same directory in CubeN6.
  • The binaries you want in nor0:
    • obl_mx66uw1g45g_signed.stm32 — our OBL, output of make -C lib/main/STM32/n6_obl signed.
    • Optionally betaflight_<version>_STM32N657_<config>.bin — your BF build, if you're flashing OBL + BF together. Skip when you want only the OBL written and the BF slot zeroed (next section).

Recovery image variants

Variant What lands in nor0 When to use
OBL + BF combined OBL at 0x0..0x87A0, zeros to 0x100000, BF at 0x100000+ Normal flash after both binaries are ready.
OBL only, BF erased OBL at 0x0..0x87A0, zeros from end-of-OBL through 0x200000 After an OBL change that may make BF crash on first boot (e.g. when OBL now transfers to BF in NS state). The empty BF slot fails OBL's vector-table validity check, so the next boot lands straight in OBL DFU recovery — no IWDG round-trip needed.

Combined image construction (OBL + BF)

cp lib/main/STM32/n6_obl/prebuilt/obl_mx66uw1g45g_signed.stm32 /tmp/obl_bf_combined.bin
truncate -s 1048576 /tmp/obl_bf_combined.bin
cat obj/betaflight_*_OPENN657V1.bin >> /tmp/obl_bf_combined.bin

Layout to use: lib/main/STM32/n6_fsbl/FlashLayout_OBL.tsv.

OBL-only image construction (BF slot erased)

install -m 0644 lib/main/STM32/n6_obl/prebuilt/obl_mx66uw1g45g_signed.stm32 \
    lib/main/STM32/n6_obl/prebuilt/obl_only_combined.bin
truncate -s 2097152 lib/main/STM32/n6_obl/prebuilt/obl_only_combined.bin

The 2 MiB size covers both the 1 MiB OBL slot and the 1 MiB BF slot with a single contiguous write at nor0 offset 0; everything past the OBL payload is zero from truncate, so the BF slot lands erased.

Layout to use: lib/main/STM32/n6_fsbl/FlashLayout_OBL_only.tsv.

TSV layout

Save as Unbricking.tsv next to OpenBootloader_STM32N6570-DK-trusted.stm32 and MX66UW1G45G_STM32N6570-DK.bin, then edit the two <...your-path...> lines to point at your OBL and BF artefacts.

#Opt	Id	Name	Type	IP	Offset	Binary
P	0x1	fsbl-openbl	Binary	none	0x0	OpenBootloader_STM32N6570-DK-trusted.stm32
P	0x3	fsbl-extfl	Binary	none	0x0	MX66UW1G45G_STM32N6570-DK.bin
P	0x2	obl-app	Binary	nor0	0x0	<path/to>/obl_mx66uw1g45g_signed.stm32
P	0x4	bf-app	Binary	nor0	0x100000	<path/to>/obj/betaflight_<version>_STM32N657_<config>.bin

Columns:

  • Opt: P = program (write); PE = program + erase first.
  • Id: ST's partition ID. The OpenBootloader recognises:
    • 0x1 → @FSBL: stage a signed FSBL image to AXISRAM2.
    • 0x2 → nor0 offset 0 (OBL slot, signed .stm32 artefact).
    • 0x3 → @External memory loader: stage the XSPI driver to RAM.
    • 0x4 → nor0 offset 0x100000 (BF slot).
  • Name: free-text label, shown in CubeProg output.
  • Type: Binary for raw bytes; Force not used to skip a row.
  • IP: target memory — none means "load to RAM" (used by Id 0x1 and 0x3); nor0 is the on-board XSPI flash.
  • Offset: byte offset inside the target memory.
  • Binary: path (relative to the TSV) of the file to write.

Procedure

  1. Power cycle the board.

  2. Slide BOOT0 to DEV (toward the edge of the board on the N6570-DK; the boot ROM samples this pin at reset and enters system DFU when high).

  3. Press the reset button. lsusb should now show:

    Bus xxx Device xxx: ID 0483:df11 STMicroelectronics STM Device in DFU Mode
    
  4. Run CubeProg against the TSV (substitute FlashLayout_OBL_only.tsv if you want OBL written and the BF slot erased):

    ~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI \
        -c port=USB1 -d Unbricking.tsv
    

    You'll see four phases scroll past — fsbl-openbl upload, then a re-enumeration as the OpenBootloader starts, then fsbl-extfl upload, then obl-app and bf-app writes to nor0.

  5. Slide BOOT0 back to USER.

  6. Press reset. The chip boots from XSPI into our OBL → BF.

Verifying

After the reset in step 6:

lsusb | grep 0483
# Expect:
#   Bus xxx Device xxx: ID 0483:3754 STMicroelectronics STLINK-V3
#   Bus xxx Device xxx: ID 0483:5740 STMicroelectronics Virtual COM Port

/dev/ttyACM1 is the BF VCP. Send # + LF to escape MSP into the BF CLI; version should report the build you just flashed.

Footnotes

  • STM32_Programmer_CLI -el .stldr -w with the .stldr form of the XSPI driver SIGSEGVs on Linux at least up to v2.22.0; the TSV path works around that.
  • port=USB1 selects the first USB DFU device; if you have several DFU devices attached, add sn=<serial> to disambiguate. List with STM32_Programmer_CLI --list usb.
  • The TSV's relative-path resolution is done from the TSV's own directory, which is why ST's two helper binaries are kept alongside it. OBL and BF artefact paths can be absolute or relative to that same directory.