diff --git a/CLAUDE.md b/CLAUDE.md index 602aa50..f32948a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,7 +45,7 @@ src/ ├── svf/ SVF player (svf_play): SIR/SDR/RUNTEST/STATE, masked compare ├── probes/ Probe-config profiles loader (parses data/probes.yaml, libyaml) ├── program/ `program` dispatch: routes a target to its backend by `prog` - └── arm_debug/ ARM (EmbeddedICE) debug + flash backend (not implemented yet) + └── arm_debug/ ARM7TDMI (EmbeddedICE) debug: halt/resume, Thumb->ARM, memory read (works); flash-write backend TODO data/ — runtime resources, looked up CWD-relative — ├── targets.yaml Target registry (FPGAs + CPUs: IDCODE, BSDL/proxy, debug, flash, prog) ├── probes.yaml Probe-config profiles (defaults + per-probe overrides) @@ -72,7 +72,7 @@ Adding a feature usually means adding a new script command in | 4 | script commands | **done** (commit `d6f843e`) | `flash_detect`, `flash_read` (+file), `flash_erase`, `flash_write`, `flash_verify`. Full set validated on KCU105 (save/erase/write-random/verify/restore round-trip). ~100 KB/s write once the proxy is loaded. | | 5 | `probes/` + JTAG-link | **done** | `data/probes.yaml` probe-config profiles (`jtag_open `, `jtag_profiles`, `jtag_close`); driver-neutral `JTAG_TCK_FREQ_KHZ`/`JTAG_RTCK`; device `max_tck_khz` clock cap resolved at `jtag_autoinit`; `prog` method tag. See the config-strategy design note. Validated on the IGLOO2 (FlashPro). | | 6 | `svf/` | **done** (subset, commit `c77d86e`) | SVF player + `svf_play`: SIR/SDR with masked TDO compare, RUNTEST, STATE — single-device. Validated on the IGLOO2 IDCODE. | -| 7 | `target/` + `program/` + `arm_debug/` | **structure done; ARM impl TODO** | Generalized `fpga/` into a kind-aware `target/` registry (FPGA \| CPU). `program ` dispatches by `prog` (svf wired; proxy_spi points at the flash workflow). `arm_debug/` (EmbeddedICE) + `arm_flash` backend are declared but not implemented; `arm-usb-ocd` probe profile added. FPGA path re-validated on the IGLOO2. See the ARM-debug design note. | +| 7 | `target/` + `program/` + `arm_debug/` | **structure done; ARM read works, flash-write TODO** | Generalized `fpga/` into a kind-aware `target/` registry (FPGA \| CPU). `program ` dispatches by `prog` (svf wired; proxy_spi points at the flash workflow). `arm_debug/` (ARM7TDMI EmbeddedICE) does halt/resume, Thumb->ARM, and system-speed **memory read** — `cpu_read`/`cpu_halt`/`cpu_resume`; validated by dumping an LPC2103's 32 KB flash to Intel HEX. Context save/restore + the `arm_flash` write backend are TODO. `arm-usb-ocd` probe profile added. See the ARM-debug design note. | | 8 | FTDI driver → libftdi1 | **done** | Replaced the proprietary libftd2xx with open-source libftdi1 (libusb): any VID:PID + auto kernel-detach. Detected an NXP LPC2103 (ARM7TDMI-S, IDCODE 0x4F1F0F0F) over an Olimex ARM-USB-OCD — the probe the old lib couldn't enumerate. Vendored `src/libs/libftd2xx` removed. | @@ -318,8 +318,12 @@ tool. ## Programming CPUs over JTAG: ARM7/9 via EmbeddedICE (design note) -Structure in place (`target/` kind=cpu, `program/` dispatch, `arm_debug/` -+ `arm_flash` declared); the debug/flash code is the next real work. +Memory **read works** (`cpu_read`/`cpu_halt`/`cpu_resume` on ARM7TDMI +EmbeddedICE): halt, Thumb->ARM switch, system-speed `LDMIA` read, dumped +to bin/Intel HEX — validated by an LPC2103 32 KB flash dump. Context +save/restore (for clean resume + repeated reads) and the `arm_flash` +write backend are the remaining work. See "What's left" and the +arm7-debug-dclk-timing note in `~/.claude/` for the cycle-exact timing. ### Why CPUs are a different shape @@ -358,9 +362,14 @@ filling from the Olimex schematic / OpenOCD's interface config. ### What's left (the implementation) -EmbeddedICE scan-chain access + halt/resume + memory R/W, then a per-MCU -RAM flash loader (LPC2xxx, AT91SAM7, …) and the `arm_flash` backend. The -registry, dispatch, probe-profile and config layers are ready for it. +Done: EmbeddedICE scan-chain access, halt/resume, Thumb->ARM, debug-speed +register read/write, and system-speed **memory read** (`cpu_read`). +Reliable in a power-on → one-halt → dump flow; reads clobber r0..r14/PC +with no context save/restore, so resume isn't clean and repeated halts in +one session degrade (power-cycle between dumps). Left: register **context +save/restore** (clean resume + repeated reads), then a per-MCU RAM flash +loader (LPC2xxx, AT91SAM7, …) and the `arm_flash` write backend. The +registry, dispatch, probe-profile and config layers are ready. ## Embedded port (design note) diff --git a/doc/tutorial.md b/doc/tutorial.md index d78b30d..e76b8e0 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -490,16 +490,55 @@ bs_explorer> program 0 design.svf # prog=svf -> plays the SVF (`bscan_load_bitstream` + `flash_write`/`flash_verify`); `arm_flash` routes to the ARM backend. -### CPU targets (ARM7/9) — structure only +### CPU targets (ARM7/9): reading memory over JTAG The registry also describes **CPUs** (`kind: cpu`): an ARM debug transport (`debug: embeddedice`), work-RAM and an on-chip flash region. -`target_list` shows them and `program` routes `prog: arm_flash` to the -ARM backend — but that backend (halt the core over JTAG, load a RAM -flasher, program internal flash) is **not implemented yet**. An Olimex -ARM-USB-OCD is an FT2232, so it opens with the existing FTDI driver via -the `arm-usb-ocd` probe profile. See the ARM-debug design note in -`CLAUDE.md`. +An Olimex ARM-USB-OCD is an FT2232, so it opens with the existing FTDI +driver via the `arm-usb-ocd` probe profile. + +For an ARM7TDMI core (EmbeddedICE) three commands work today: + +``` +bs_explorer> jtag_open 0 arm-usb-ocd +bs_explorer> jtag_scan # IDCODE, e.g. 0x4F1F0F0F (LPC2103) +bs_explorer> cpu_read 0 0x0 0x8000 flash.hex hex # dump 32 KB flash to Intel HEX +bs_explorer> cpu_halt 0 # halt only (DBGACK) +bs_explorer> cpu_resume 0 # release from debug +``` + +`cpu_read ` halts the core, reads +memory by **instruction injection** (halt via EmbeddedICE, switch a +Thumb-state core to ARM, then a system-speed `LDMIA` reads real memory), +and writes the bytes as raw binary or Intel HEX. Omit `` for a +console hex-dump. Validated by dumping an LPC2103's full 32 KB flash and +round-tripping the `.hex` through `objcopy` (all records/checksums valid, +correct ARM vector table). Debug-speed core-register read/write also +works (it is how the address is set up and the loaded words are read +back). + +**Operating context — when it works, and the limits.** Reading is +reliable in this flow: + +- **One halt per power-cycle.** The intended sequence is *power on the + board → `jtag_scan` → one `cpu_read` (which halts, reads, leaves the + core halted)*. A single `cpu_read` call dumps any length in one halt + (it reads in 14-register blocks internally), so dumping all of flash is + one command. +- **Reads clobber r0–r14 and the PC**, and there is **no register + context save/restore yet**. So `cpu_resume` cannot cleanly continue the + original firmware, and a *second* `cpu_read` (or `cpu_halt`) in the + same session re-halts an already-halted, register-clobbered core, which + is messy and can time out (`sys-speed access timed out`). If that + happens, **power-cycle the board** and run one `cpu_read` again. +- ARM7TDMI only so far (the EmbeddedICE scan-chain debug). Cortex-M + (ADIv5/SWD) is a different transport. + +The why-and-how of the cycle-exact JTAG timing this relies on is in the +ARM-debug design note in `CLAUDE.md`. The next step toward clean resume +and repeated reads is register **context save/restore**; the `arm_flash` +*write* backend (program internal flash via a RAM loader) builds on that +and is not implemented yet. ## Troubleshooting cheat sheet @@ -515,6 +554,7 @@ the `arm-usb-ocd` probe profile. See the ARM-debug design note in | Detected fine, then reads turn to garbage / `0x00000000` mid-session | Target board lost power — JTAG floats (the USB probe stays enumerated regardless). Re-power the board. | | FT4232H FlashPro: `jtag_scan` finds 0 devices | JTAG is on channel A (index 0) and needs `ADBUS4` high-Z — open with the profile: `jtag_open 0 flashpro`. | | `svf_play` mismatches only on the very first compare | FTDI link warm-up; `svf_play` handles it, but a bare `bscan_shift_dr` straight after `jtag_open` may need a `jtag_scan` first. | +| `cpu_read`: `sys-speed access timed out` | The core was re-halted in a degraded state (a previous `cpu_read`/`cpu_halt` left it halted with clobbered registers). Power-cycle the board, then run one `cpu_read`. | ## Where to go from here