You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The z_fatal_error(reason=33) / usage-fault "hangs" observed during HWIL debugging on the V5e (RP2350) — on bothmain (b2437a9) and feat/sband-pr1-infra (2558ac3, PR #448) — are caused by the debugger, not by flight software. Both firmwares run indefinitely with clean telemetry when no debugger is attached.
OpenOCD's RP2350 flash driver executes an "RCP init" stub on the target that overwrites CPACR with 0xC000 (enabling only the redundancy coprocessor CP7), which disables the RP2350 GPIO coprocessor (CP0) and the DCP (CP4). The pico-sdk HAL uses CP0 for all GPIO access (mcr/mrc p0), so the next GPIO touch after resume raises a K_ERR_ARM_USAGE_NO_COPROCESSOR (reason=33) fatal, and Zephyr's default handler spins in arch_system_halt — which looks exactly like a firmware hang.
Mechanism
tools/openocd/src/flash/nor/rp2xxx.c, rcp_init_code (~line 101), run by rp2350_init_arm_core0 → setup_for_raw_flash_cmd → rp2xxx_flash_probe. The stub does str r1, [CPACR] with r1 = M33_CPACR_CP7_BITS only; a comment admits "we assume no other co-processors are enabled at this point to save space."
Flash auto-probe fires when GDB connects (memory-map query), so a plain gdb attach to a healthy running board is enough — no flashing needed.
The driver saves/restores the core's security state and ACCESSCTRL around ROM calls, but not CPACR.
Faulting instruction at gpio_rpi_port_get_raw is 0xee103018 = MRC p0, #0, r3, c0, c1, #0 — a genuine CP0 access, not an assert/UDF trap (rules out an F´ FW_ASSERT explanation).
reason=33 = K_ERR_ARM_USAGE_NO_COPROCESSOR; Zephyr only assigns this when the hardware UFSR NOCP bit is set.
Healthy running board: CPACR = 0xC303 (CP0 enabled — set by soc_reset_hook → runtime_init_per_core_enable_coprocessors() at every boot). No code in Zephyr/HAL/F´ writes CPACR at runtime; no PM/sleep configured.
Standalone runs clean:main firmware 90 s, sband firmware 3+ min, steady telemetry, including the watchdog-pet GPIO path that "faulted immediately" under the debugger.
Causation on demand: attach OpenOCD+GDB to the healthy running board → CPACR reads 0xC000 at first halt → continue → immediate NOCP fatal, identical backtrace (watchdog pet on sband; SX126x BUSY-pin read on main — simply whichever code touches GPIO first after resume).
rp2350.cfg sets no cortex_m vector_catch; the static halt is Zephyr's stock for(;;) — after the debugger-manufactured fault.
Impact
Any RP2350 HWIL session that attaches GDB (or runs a flash command) while firmware is running will fatal the board on the next GPIO access. Easily misdiagnosed as a firmware GPIO/coprocessor bug — it already cost multiple debug sessions here, and likely explains the earlier GRC-board "NOCP fault on every LoRa TX" (June 2026) that was worked around with PICO_USE_GPIO_COPROCESSOR=0.
Patch our vendored OpenOCD: make rcp_init_code read-modify-write CPACR (OR in CP7 bits instead of overwriting), or save/restore CPACR like ACCESSCTRL. Upstream-reportable to raspberrypi/openocd.
Bench workaround: after any GDB attach, restore with mww 0xE000ED88 0xC303 before resuming.
Build workaround for debug sessions: PICO_USE_GPIO_COPROCESSOR=0 (MMIO SIO GPIO, immune to the clobber).
Summary
The
z_fatal_error(reason=33)/ usage-fault "hangs" observed during HWIL debugging on the V5e (RP2350) — on bothmain(b2437a9) andfeat/sband-pr1-infra(2558ac3, PR #448) — are caused by the debugger, not by flight software. Both firmwares run indefinitely with clean telemetry when no debugger is attached.OpenOCD's RP2350 flash driver executes an "RCP init" stub on the target that overwrites CPACR with 0xC000 (enabling only the redundancy coprocessor CP7), which disables the RP2350 GPIO coprocessor (CP0) and the DCP (CP4). The pico-sdk HAL uses CP0 for all GPIO access (
mcr/mrc p0), so the next GPIO touch after resume raises aK_ERR_ARM_USAGE_NO_COPROCESSOR(reason=33) fatal, and Zephyr's default handler spins inarch_system_halt— which looks exactly like a firmware hang.Mechanism
tools/openocd/src/flash/nor/rp2xxx.c,rcp_init_code(~line 101), run byrp2350_init_arm_core0→setup_for_raw_flash_cmd→rp2xxx_flash_probe. The stub doesstr r1, [CPACR]withr1 = M33_CPACR_CP7_BITSonly; a comment admits "we assume no other co-processors are enabled at this point to save space."gdb attachto a healthy running board is enough — no flashing needed.Evidence (V5e HWIL, probe E663AC91D32D6A39, 2026-07-19)
gpio_rpi_port_get_rawis0xee103018=MRC p0, #0, r3, c0, c1, #0— a genuine CP0 access, not an assert/UDF trap (rules out an F´FW_ASSERTexplanation).reason=33=K_ERR_ARM_USAGE_NO_COPROCESSOR; Zephyr only assigns this when the hardware UFSR NOCP bit is set.soc_reset_hook→runtime_init_per_core_enable_coprocessors()at every boot). No code in Zephyr/HAL/F´ writes CPACR at runtime; no PM/sleep configured.mainfirmware 90 s, sband firmware 3+ min, steady telemetry, including the watchdog-pet GPIO path that "faulted immediately" under the debugger.continue→ immediate NOCP fatal, identical backtrace (watchdog pet on sband; SX126x BUSY-pin read on main — simply whichever code touches GPIO first after resume).rp2350.cfgsets nocortex_m vector_catch; the static halt is Zephyr's stockfor(;;)— after the debugger-manufactured fault.Impact
PICO_USE_GPIO_COPROCESSOR=0.main.Possible fixes (for later discussion)
rcp_init_coderead-modify-write CPACR (OR in CP7 bits instead of overwriting), or save/restore CPACR like ACCESSCTRL. Upstream-reportable to raspberrypi/openocd.mww 0xE000ED88 0xC303before resuming.PICO_USE_GPIO_COPROCESSOR=0(MMIO SIO GPIO, immune to the clobber).🤖 Generated with Claude Code
https://claude.ai/code/session_019gMrPNe6LwGtBS6Y7B5bo8