From 1f21fa09f4dd11c9f532c695dd108875543a6281 Mon Sep 17 00:00:00 2001 From: Andrew Rankin Date: Sun, 24 May 2026 16:40:04 -0400 Subject: [PATCH] tools/flash.sh: work around OpenOCD program-sector-0 bug on GD32F205 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bench-confirmed 2026-05-24: OpenOCD 0.12.0 + stm32f1x driver targeting GD32F205 (CPUTAPID 0x1ba01477) silently fails to write flash sector 0 via the high-level `program` command. The "Verified OK" reports a false pass because verify_image uses the same broken loader path and reads back the same address-byte garbage (00 01 02 03 04 ...) that write produced. Chip then hard-faults on next reset trying to load the corrupted reset vector. Manual proof that hardware is fine: direct FLASH_KEYR unlock + PG/STRT write of a half-word at 0x08000000 lands correctly and reads back right. Silicon is healthy, only the OpenOCD loader-algorithm path is broken. Workaround: manual mass erase via direct FLASH_CR poking, then `flash write_image` + `verify_image` (lower-level path that doesn't use the broken loader). Tested on rippleon ROC001 — wall unit recovered from yesterday's busy_wait incident now boots cleanly. Also adds a post-flash message reminding the operator to power-cycle the unit before expecting the GFCI self-test to pass: the SWD halt during flash exceeds the external CCID chip's refresh-cycle watchdog window, latching the chip into fail-safe. Power cycle clears it. See feedback memory feedback-openocd-program-skips-sector0-gd32f205 for full detail + detection recipe. Co-Authored-By: Claude Opus 4.7 --- tools/flash.sh | 70 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/tools/flash.sh b/tools/flash.sh index 323a3c8..928692c 100755 --- a/tools/flash.sh +++ b/tools/flash.sh @@ -32,20 +32,31 @@ if [[ $# -ge 1 ]]; then exit 1 fi case "$TARGET" in - *.elf) + *.elf|*.bin|*.hex) echo "Flashing $TARGET ..." + # OpenOCD's high-level `program` command silently fails to + # write flash sector 0 (the vector table) on GD32F205 — the + # write algorithm gets confused and leaves garbage there + # while still reporting "Verified OK". Bench-confirmed + # 2026-05-24: after `program`, 0x08000000-0x0800003F reads + # back as the address-byte pattern (00 01 02 03 ...) and + # the chip hard-faults on reset. + # + # Workaround: manual unlock + mass erase via direct FLASH + # controller writes, then `flash write_image` (lower-level + # path that doesn't have the bug). Tested on rippleon ROC001. openocd -f "$CFG" \ -c "init" \ -c "reset halt" \ - -c "program $TARGET verify reset" \ - -c "shutdown" - ;; - *.bin|*.hex) - echo "Flashing raw $TARGET at 0x08000000 ..." - openocd -f "$CFG" \ - -c "init" \ + -c "mww 0x40022004 0x45670123" \ + -c "mww 0x40022004 0xCDEF89AB" \ + -c "mww 0x40022010 0x00000004" \ + -c "mww 0x40022010 0x00000044" \ + -c "sleep 2000" \ -c "reset halt" \ - -c "program $TARGET 0x08000000 verify reset" \ + -c "flash write_image $TARGET" \ + -c "verify_image $TARGET" \ + -c "reset run" \ -c "shutdown" ;; *) @@ -53,6 +64,22 @@ if [[ $# -ge 1 ]]; then exit 1 ;; esac + cat <<'POSTFLASH' + +========================================================================== + Heartbeat LED on PD4 should be blinking at 1 Hz. + + NOTE: After SWD flash, the external GFCI/CCID chip is latched in + fail-safe state (MCU was halted for >6 s during flash, so the chip's + watchdog tripped). The boot self-test will FAIL until you power-cycle + the unit. After power cycle, the chip POSTs cleanly and the boot + self-test PASSes normally. + + Skip the power cycle only if you're sure the chip wasn't ever in + fail-safe (e.g., you flashed and the unit was already in READY with + the refresh task running before the SWD halt — rare). +========================================================================== +POSTFLASH else BUILD_DIR="$REPO_ROOT/build" ELF="$BUILD_DIR/openevcharger.elf" @@ -87,11 +114,32 @@ else cmake --build "$BUILD_DIR" fi echo "Flashing $ELF ..." + # See big comment above the explicit-path branch — `program` skips + # sector 0 silently on GD32F205. openocd -f "$CFG" \ -c "init" \ -c "reset halt" \ - -c "program $ELF verify reset" \ + -c "mww 0x40022004 0x45670123" \ + -c "mww 0x40022004 0xCDEF89AB" \ + -c "mww 0x40022010 0x00000004" \ + -c "mww 0x40022010 0x00000044" \ + -c "sleep 2000" \ + -c "reset halt" \ + -c "flash write_image $ELF" \ + -c "verify_image $ELF" \ + -c "reset run" \ -c "shutdown" fi -echo "Done. Heartbeat LED on PD4 should be blinking at 1 Hz." +cat <<'POSTFLASH' + +========================================================================== + Heartbeat LED on PD4 should be blinking at 1 Hz. + + NOTE: After SWD flash, the external GFCI/CCID chip is latched in + fail-safe state (MCU was halted for >6 s during flash, so the chip's + watchdog tripped). The boot self-test will FAIL until you power-cycle + the unit. After power cycle, the chip POSTs cleanly and the boot + self-test PASSes normally. +========================================================================== +POSTFLASH