Skip to content

Flash target lockup / HardFault on XIAO nRF54LM20A after mass-erase (pyocd crashes outright; OpenOCD reports false-positive SUCCESS) #69

Description

@SteveF161

Environment

  • Board: Seeed XIAO nRF54LM20A
  • platform-seeedboards version: installed from https://github.com/Seeed-Studio/platform-seeedboards.git (git-pinned in platformio.ini, not a registry release) — commit dd790080f5847f04680b079b3a7f24db4a56ccfa
  • PlatformIO Core version: 6.1.19
  • OpenOCD version bundled/used: 3.1200.7
  • pyocd version: 0.45.1
  • Host OS: Windows 11 Home, build 26200.9168
  • Debug probe: CMSIS-DAPv2, VID:PID 0x2886:0x0068 (Seeed vendor ID — onboard SWD debugger), FW 2.0.0, serial 14F20522
  • Framework: Zephyr (native, no Arduino option for this board)

Summary

Reproduced with a single, controlled test on one continuous session against the same
chip state (not pieced together from separate attempts):

  1. Confirmed no serial output on COM5 beforehand.
  2. Mass-erased the chip: pyocd erase --target nrf54lm20a --chip (succeeds cleanly).
  3. Immediately attempted pyocd flash on the freshly-erased chip — the target
    crashes mid-program
    (CPU reports IPSR=3 / HardFault); the write never
    completes. COM5 remained silent afterward.
  4. Immediately attempted pio run -t upload (OpenOCD) on that same,
    still-erased/crashed chip — OpenOCD logs clearing lockup after double fault
    and Polling failed, trying to reexamine, then still reports [SUCCESS].
    COM5 remained silent afterward.
  5. Power-cycled the board (full power-on reset, not just a soft reset) to rule out
    "the CPU is just halted after flashing and needs a reset" as an explanation —
    COM5 still showed no output. The board is genuinely non-functional after both
    attempts, not merely halted in a debug state.

So: both tools fail to produce working firmware when flashing a chip that was just
mass-erased. pyocd fails loudly (crash, non-zero-equivalent failure state). OpenOCD
fails silently — it reports [SUCCESS] and exit-codes as if the upload worked, even
though the chip never boots. The [SUCCESS] status is not a reliable indicator that
the flash actually succeeded.

Both tools also log a persistent warning throughout — "not in a secure state"
which is consistent with this being a TrustZone-M secure/non-secure debug-attach
state issue specific to this SoC, rather than a wiring/hardware fault (the same
board/cable/probe flashes correctly when the chip is not freshly erased — see
Additional notes).

Steps to reproduce

  1. Start from a XIAO nRF54LM20A with working firmware already flashed (e.g. a Zephyr
    build produced via pio run -t upload on this same project). Confirm serial
    output on the expected COM port beforehand if you want a clean before/after.
  2. Mass-erase the chip: pyocd erase --target nrf54lm20a --chip.
  3. Immediately attempt pyocd flash --target nrf54lm20a <path to firmware.hex>
    observe the crash with IPSR=3 (HardFault) during programming (see log below).
    Check the serial port: no output.
  4. On that same chip (still in the failed/erased state from step 3), immediately
    attempt pio run -t upload (project: Zephyr, board seeed-xiao-nrf54lm20a,
    platform installed from Seeed-Studio/platform-seeedboards git HEAD) —
    observe the clearing lockup after double fault / Polling failed errors
    followed by a reported [SUCCESS] (see log below). Check the serial port
    again: still no output.
  5. Power-cycle the board and check the serial port a third time to rule out a
    halted-CPU/needs-reset explanation — output is still absent.

Expected behavior

Flash completes successfully and the resulting firmware boots and produces the
expected serial output, regardless of whether the chip was previously erased —
matching normal OpenOCD/pyocd behavior on other Cortex-M / TrustZone-M targets.
At minimum, a tool that cannot verify the target booted should not report
[SUCCESS].

Actual behavior — logs

Step 2: pyocd erase (succeeds)

PS C:\Users\steve\.platformio\platforms\seeedstudio\scripts\factory_reset\.venv\Scripts> ./pyocd.exe erase --target nrf54lm20a --chip
0000960 W NRF54LM20A is not in a secure state [target_nRF54LM20A]
0001270 I Erasing chip... [eraser]
0001647 I Chip erase complete [eraser]

Step 3: pyocd flash on the freshly-erased chip (crashes)

PS C:\Users\steve\.platformio\platforms\seeedstudio\scripts\factory_reset\.venv\Scripts> .\pyocd.exe flash --target nrf54lm20a "E:\Users\Steve\Documents\PlatformIO\Projects\weather_station_lm20a\.pio\build\seeed-xiao-nrf54lm20a\firmware.hex"
0001082 W NRF54LM20A is not in a secure state [target_nRF54LM20A]
0001395 I Loading E:\Users\Steve\Documents\PlatformIO\Projects\weather_station_lm20a\.pio\build\seeed-xiao-nrf54lm20a\firmware.hex [load_cmd]
0001476 I Erasing... [loader]
[==================================================] 100%
0001682 I Programming... [loader]
0001912 C target was not halted as expected after calling flash algorithm routine (IPSR=3) [__main__]

Step 4: pio run -t upload (OpenOCD) on the same chip, right after step 3 (reports false-positive SUCCESS)

PS E:\Users\Steve\Documents\PlatformIO\Projects\weather_station_lm20a> pio run -t upload

Processing seeed-xiao-nrf54lm20a (platform: https://github.com/Seeed-Studio/platform-seeedboards.git; framework: zephyr; board: seeed-xiao-nrf54lm20a)
-------------------------------------------------------------------------------------------------------------------------------
PLATFORM: Seeed Studio Xiao Series (1.0.0+sha.dd79008) > Seeed Studio XIAO nRF54LM20A
HARDWARE: NRF54LM20A 128MHz, 512KB RAM, 1.99MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, jlink)

[west/pip dependency-check output omitted for brevity]

Checking size .pio\build\seeed-xiao-nrf54lm20a\firmware.elf
RAM:   [          ]   2.9% (used 15344 bytes from 524288 bytes)
Flash: [          ]   3.4% (used 71700 bytes from 2084864 bytes)
Configuring upload protocol...
AVAILABLE: cmsis-dap, jlink, probe-rs, pyocd
CURRENT: upload_protocol = cmsis-dap
Uploading .pio\build\seeed-xiao-nrf54lm20a\firmware.hex
xPack Open On-Chip Debugger 0.12.0+dev-02228-ge5888bda3-dirty (2025-10-04-22:44)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Warn : Adapter driver already configured, ignoring
Error: [nrf54lm20a.cpu] clearing lockup after double fault
Error: [nrf54lm20a.cpu] Polling failed, trying to reexamine
================================================ [SUCCESS] Took 14.20 seconds ================================================

Confirmed: despite [SUCCESS], COM5 showed no output — checked immediately
after upload, and again after a full power-cycle of the board (ruling out a
halted-CPU/needs-manual-reset explanation).

Additional notes / working theory

  • The "<device> is not in a secure state" warning appears on every pyocd
    invocation against this board, including the successful erase, which points at
    this being a TrustZone-M Secure/Non-Secure debug-access-port state issue rather
    than an intermittent/timing bug.
  • Flashing succeeds reliably when overwriting existing, already-booting firmware
    (both tools work fine in that case); it is specifically attach-and-program
    against a blank/erased (all-0xFF, no valid vector table) chip that triggers
    the crash/lockup in both tools.
  • OpenOCD's false-positive [SUCCESS] is arguably the more actionable part of
    this report for tooling maintainers: the process exit code does not reflect
    whether the flash actually produced working firmware, which could silently
    ship broken firmware to anyone who doesn't independently verify serial output.
    This was specifically confirmed not to be a "just needs a reset" situation —
    we power-cycled the board and it still produced no output.
  • This is a very new chip (nRF54LM20A), so it's plausible neither pyocd's flash
    algorithm nor this platform's OpenOCD config/reset sequence has been
    validated yet against this specific erase-then-attach case.
  • Happy to test candidate fixes (alternate reset_config, adapter speed,
    explicit secure-AP dap selection, srst/sysresetreq sequencing, etc.) against
    hardware if maintainers can suggest something to try.

Workarounds tried

  • Retrying pio run -t upload multiple times without erasing first — this is the
    only thing that has reliably worked, and only when the chip was never mass-erased
    to begin with. Once a mass-erase has been performed, the chip appears to remain
    in this broken state; reflashing (via either tool) no longer recovers it.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions