target: generalize the registry to FPGAs + CPUs, add program dispatch
Restructure in anticipation of programming ARM CPUs (ARM7/9 via EmbeddedICE, e.g. over an Olimex ARM-USB-OCD); FPGA path unchanged. - modules/fpga -> modules/target; fpga_target -> jtag_target with a `kind` (fpga|cpu) and grouped fpga/cpu sub-structs; data/targets.yaml (env BS_TARGETS); API target_*; commands target_list/target_info (kind-aware). Add arm7/arm9 families, arm_flash prog, embeddedice debug, and cpu fields (ram_base/size, flash_base/size). - new program/: `program <dev> <file>` dispatches by the target's prog (svf wired; proxy_spi points at the flash workflow; arm_flash -> arm_debug). - new arm_debug/: EmbeddedICE halt/resume/mem + arm_flash backend declared, not implemented yet. - bscan_* take const jtag_target* and read the fpga sub-struct. - data/probes.yaml: arm-usb-ocd profile slot; data/targets.yaml: an ARM7 example entry. Docs + an ARM-debug design note in CLAUDE.md. Builds; FPGA path re-validated on the IGLOO2 (target_list shows the CPU example; jtag_open/autoinit/program 0 <svf> all work). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
110
CLAUDE.md
110
CLAUDE.md
@@ -18,8 +18,9 @@ configuration flash via a BSCAN proxy (started with the KU15P), and
|
||||
other families (Lattice, Microsemi, …) by playing a vendor-exported SVF.
|
||||
|
||||
The Viveris library itself lives unchanged in `src/modules/`. Everything
|
||||
new is in `src/bs/` (the REPL) and the project modules (`fpga/`, `bscan/`,
|
||||
`spi_flash/`, `svf/`, `probes/`) sitting alongside the Viveris ones.
|
||||
new is in `src/bs/` (the REPL) and the project modules (`target/`,
|
||||
`bscan/`, `spi_flash/`, `svf/`, `probes/`, `program/`, `arm_debug/`)
|
||||
sitting alongside the Viveris ones.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -38,14 +39,16 @@ src/ — code + libs —
|
||||
├── os_interface/ Portable fs/network wrappers
|
||||
└── natsort/ Natural pin-name sorting
|
||||
— new (this project) —
|
||||
├── fpga/ Registry loader (parses data/fpga_registry.yaml, libyaml)
|
||||
├── target/ Target registry: FPGAs + CPUs (parses data/targets.yaml)
|
||||
├── bscan/ JTAG TAP primitives (set_ir/shift_ir/shift_dr/tap_reset/
|
||||
│ idle_cycles) + BSCAN proxy (bitstream load, SPI-over-USER1)
|
||||
├── spi_flash/ SPI NOR chip DB + read/erase/program/verify over a callback
|
||||
├── svf/ SVF player (svf_play): SIR/SDR/RUNTEST/STATE, masked compare
|
||||
└── probes/ Probe-config profiles loader (parses data/probes.yaml, libyaml)
|
||||
├── 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)
|
||||
data/ — runtime resources, looked up CWD-relative —
|
||||
├── fpga_registry.yaml FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats, prog, max_tck)
|
||||
├── targets.yaml Target registry (FPGAs + CPUs: IDCODE, BSDL/proxy, debug, flash, prog)
|
||||
├── probes.yaml Probe-config profiles (defaults + per-probe overrides)
|
||||
├── bsdl_files/ BSDL files for target FPGAs
|
||||
├── bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq)
|
||||
@@ -64,12 +67,13 @@ Adding a feature usually means adding a new script command in
|
||||
| Phase | Module | Status | Summary |
|
||||
|-------|--------|--------|---------|
|
||||
| 1 | `bs/` cleanup, REPL polish, README | **done** (commit `7cb3627`) | Fix format-strings, delete dead code, tab-completion, banner |
|
||||
| 2 | `fpga/` | **done** (commit `545fe09`) | Per-target descriptor (IDCODE, BSDL, IR codes, proxy path, caveats). Now a **runtime YAML** registry (`data/fpga_registry.yaml`, libyaml), later gaining `prog` method + `max_tck_khz`. |
|
||||
| 2 | `fpga/` | **done** (commit `545fe09`) | Per-target descriptor (IDCODE, BSDL, IR codes, proxy path, caveats). Now a **runtime YAML** registry (`data/targets.yaml`, libyaml), later gaining `prog` method + `max_tck_khz`. |
|
||||
| 2.5 | `bscan/` | **done** (commit `dec0d14`) | Load BSCAN proxy bitstream via `CFG_IN`, expose fast `bscan_spi_xfer()` via `USER1`. Required for realistic flashing speeds. |
|
||||
| 3 | `spi_flash/` | **done** (commit `c4afe87`) | Chip DB (JEDEC ID → page/sector/cmd set) + generic `read/erase/program/verify` over an `xfer` callback. detect+read validated on KCU105; erase/program implemented but not yet hardware-tested. |
|
||||
| 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 <idx> <profile>`, `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; a real Libero SVF and a generic `program` dispatch off the `prog` tag are still TODO. |
|
||||
| 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 <dev> <file>` 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. |
|
||||
|
||||
Move forward phase by phase: validate one with the user before starting
|
||||
the next. Don't break the validated path
|
||||
@@ -95,25 +99,25 @@ internally — the `STARTUPE3` problem disappears). Realistic throughput
|
||||
50–200 KB/s, so ~10–40 min for 128 MB. This is what Vivado, OpenOCD's
|
||||
`jtagspi` driver, and `xc3sprog` all do.
|
||||
|
||||
### Per-FPGA descriptor
|
||||
### Per-target descriptor
|
||||
|
||||
`fpga_target` struct (Phase 2) holds the per-target facts that can't be
|
||||
derived from the BSDL alone:
|
||||
- `idcode` + `idcode_mask` — match the device on the chain
|
||||
- `bsdl_filename` — BSDL to auto-load
|
||||
- `cfg_in_ir_code`, `user1_ir_code`, `jprogram_ir_code` — Xilinx-specific
|
||||
private IR opcodes (read from BSDL when available)
|
||||
- `proxy_bitstream_path` — path to the BSCAN proxy `.bit` for this part
|
||||
- `caveats` — flags for known hardware gotchas (e.g. CCLK via STARTUPE3)
|
||||
The `jtag_target` struct (`target/target.h`) holds the per-target facts
|
||||
that can't be derived from the IDCODE/BSDL alone. A `kind` (fpga|cpu)
|
||||
selects a sub-struct; the rest is shared:
|
||||
- common — `idcode`+`idcode_mask` (chain match), `family`, `ir_length`,
|
||||
`prog` (programming backend), `max_tck_khz`
|
||||
- `fpga` sub-struct — `bsdl_filename`, the Xilinx config IR opcodes
|
||||
(`cfg_in`/`user1`/`jprogram`/…), `proxy_bitstream`, `caveats`
|
||||
- `cpu` sub-struct — `debug` transport (EmbeddedICE), work-RAM
|
||||
(`ram_base`/`size`) and on-chip flash region (`flash_base`/`size`)
|
||||
|
||||
Registry is a **runtime YAML file** (`data/fpga_registry.yaml` at the repo
|
||||
root), parsed via libyaml — no longer a compile-time array. It is looked
|
||||
up CWD-relative (like `data/bsdl_files/`), overridable with
|
||||
`$BS_FPGA_REGISTRY`, and loaded lazily on first access. Adding a part =
|
||||
one YAML entry + its `.bsd` in `data/bsdl_files/` + (optionally) its proxy
|
||||
`.bit` in `data/bscan_proxies/` — no rebuild. The `family` field accepts
|
||||
`xilinx_*`, `microsemi_igloo2/smartfusion2`, `lattice_machxo2/3`
|
||||
(enum in `fpga.h`).
|
||||
Registry is a **runtime YAML file** (`data/targets.yaml`), parsed via
|
||||
libyaml — looked up CWD-relative (like `data/bsdl_files/`), overridable
|
||||
with `$BS_TARGETS`, loaded lazily. Adding a target = one flat YAML entry
|
||||
(+ a `.bsd`/proxy for FPGAs) — no rebuild. `family` accepts `xilinx_*`,
|
||||
`microsemi_*`, `lattice_*`, `arm7`/`arm9`; `prog` is
|
||||
`proxy_spi`/`svf`/`arm_flash`/`none` (inferred when omitted). Enums in
|
||||
`target.h`.
|
||||
|
||||
### Digilent SMT2 modules need libdjtg, not raw MPSSE
|
||||
|
||||
@@ -171,7 +175,7 @@ driver-neutral terms that get resolved per session.**
|
||||
|-------|-------------|----------------|---------|
|
||||
| **Probe (sonde)** | driver + interface, pin map, buffer-enable, TRST/SRST pins, level-shift, *max TCK the adapter supports* | `data/probes.yaml` (done) | at `jtag_open` |
|
||||
| **JTAG link** | TCK freq, RTCK, reset behaviour, chain layout — **driver-neutral names**, resolved to effective values | *missing today* | open, then refined after detect |
|
||||
| **Device** | IDCODE/BSDL/IR/proxy/caveats, programming method, *max TCK the part/board tolerates* | `data/fpga_registry.yaml` (done) | after IDCODE match |
|
||||
| **Device** | IDCODE/BSDL/IR/proxy/caveats, programming method, *max TCK the part/board tolerates* | `data/targets.yaml` (done) | after IDCODE match |
|
||||
|
||||
### The smell that motivated this
|
||||
|
||||
@@ -195,7 +199,7 @@ fact bounded by both the probe and the board/device.
|
||||
method) is known — using the `jtag_close`→reopen seam if a re-init is
|
||||
needed. This also dissolves the chicken-and-egg: a conservative link
|
||||
default gets you to detection, then the device refines it.
|
||||
- `data/probes.yaml` gains an optional `max_tck_khz`; `data/fpga_registry.yaml`
|
||||
- `data/probes.yaml` gains an optional `max_tck_khz`; `data/targets.yaml`
|
||||
gains optional `max_tck_khz` + a `prog` method tag (`proxy_spi`/`svf`).
|
||||
|
||||
### Phasing
|
||||
@@ -213,7 +217,7 @@ fact bounded by both the probe and the board/device.
|
||||
still TODO.
|
||||
- **C (done)** — `prog` method tag (`proxy_spi`/`svf`/`none`) on each
|
||||
registry entry, inferred when omitted (proxy → `proxy_spi`; Microsemi/
|
||||
Lattice → `svf`); shown by `fpga_info`/`fpga_list` and available via
|
||||
Lattice → `svf`); shown by `target_info`/`target_list` and available via
|
||||
`fpga_prog_method_name()` for backend dispatch. RTCK generalised as a
|
||||
neutral `JTAG_RTCK` (mirrored to `PROBE_FTDI_JTAG_ENABLE_RTCK`,
|
||||
FTDI-only). Reset abstraction deferred — it's a bundle of probe-
|
||||
@@ -221,7 +225,7 @@ fact bounded by both the probe and the board/device.
|
||||
actual `program` dispatch command lands with the SVF player.
|
||||
|
||||
What exists already: the **probe layer** (`data/probes.yaml`) and the
|
||||
**device layer** (`data/fpga_registry.yaml`). The new work is the **JTAG-link
|
||||
**device layer** (`data/targets.yaml`). The new work is the **JTAG-link
|
||||
layer** in the middle.
|
||||
|
||||
## Programming backends: beyond Xilinx external flash (design note)
|
||||
@@ -298,6 +302,52 @@ a registry entry.
|
||||
one-time NVCM) with minimal JTAG — out of scope for this JTAG-centric
|
||||
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.
|
||||
|
||||
### Why CPUs are a different shape
|
||||
|
||||
A CPU isn't configured like an FPGA — there's no bitstream or BSDL pin
|
||||
map to drive. You **halt the core through its debug unit, write a small
|
||||
flash loader into on-chip RAM, run it to program the internal flash, and
|
||||
verify** — the OpenOCD approach. So the target carries different facts
|
||||
(captured in the `cpu` sub-struct): the debug transport, a work-RAM
|
||||
region for the loader, and the flash region.
|
||||
|
||||
### The debug seam (`arm_debug/`)
|
||||
|
||||
ARM7/ARM9 expose **EmbeddedICE** on a JTAG TAP: a scan chain (DSCR, DCC,
|
||||
breakpoint regs) reached via the standard IR/DR shifts. The seam is four
|
||||
primitives — `halt`, `resume`, `mem_read`, `mem_write` — built on the
|
||||
existing `bscan_*` IR/DR functions; everything else (the RAM loader, the
|
||||
per-MCU flash algorithm) sits on top. Cortex-M is the same idea behind a
|
||||
*different* transport (ADIv5/DAP, and SWD which isn't JTAG); it would be
|
||||
a second `debug` value behind the same seam, not a rewrite.
|
||||
|
||||
### Backend dispatch (`program/`)
|
||||
|
||||
`program <dev> <file>` looks the device up in the registry and routes by
|
||||
its `prog` tag: `svf`→SVF player, `proxy_spi`→the Xilinx flash workflow,
|
||||
`arm_flash`→`arm_debug`'s RAM-loader flash. Adding a backend = one `prog`
|
||||
value + one case. This is the seam that makes new target classes drop in
|
||||
without touching the REPL.
|
||||
|
||||
### Probe
|
||||
|
||||
ARM-USB-OCD is an Olimex **FT2232** (OpenOCD-class) adapter — the
|
||||
existing FTDI driver drives it; it just needs a `probes.yaml` profile
|
||||
(`arm-usb-ocd`) with the Olimex control-pin map (TRST/SRST, buffer
|
||||
enable). The profile slot exists; the exact pin numbers still need
|
||||
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.
|
||||
|
||||
## Embedded port (design note)
|
||||
|
||||
Not yet implemented — captured for a possible standalone programmer.
|
||||
@@ -365,9 +415,9 @@ mkdir build && cd build && cmake .. && make
|
||||
```
|
||||
|
||||
Build needs **libyaml** (pkg-config `yaml-0.1`; Arch `libyaml`, Debian
|
||||
`libyaml-dev`) — the FPGA registry is parsed from `data/fpga_registry.yaml`
|
||||
`libyaml-dev`) — the FPGA registry is parsed from `data/targets.yaml`
|
||||
at runtime. Run `bs` from the repo root so it finds that file (and
|
||||
`data/bsdl_files/`, `data/bscan_proxies/`), or point `$BS_FPGA_REGISTRY` at it.
|
||||
`data/bsdl_files/`, `data/bscan_proxies/`), or point `$BS_TARGETS` at it.
|
||||
|
||||
The Digilent SMT2 backend is built by default on UNIX (disable with
|
||||
`-DBS_ENABLE_DIGILENT=OFF`). To actually use such a probe, install the
|
||||
|
||||
Reference in New Issue
Block a user