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:
2026-05-24 15:33:58 +02:00
parent d1bdce91dc
commit 9ad776268e
20 changed files with 973 additions and 582 deletions

View File

@@ -9,7 +9,7 @@ paths and the tutorial covers both:
- **any other part** (Lattice, Microsemi, …), by playing a
vendor-exported **SVF** — shown on a Microsemi IGLOO2 M2GL010T.
The early steps are identical for any device in `data/fpga_registry.yaml`;
The early steps are identical for any device in `data/targets.yaml`;
only the IDCODE and BSDL filename change.
## Prerequisites
@@ -19,7 +19,7 @@ only the IDCODE and BSDL filename change.
`src/libs/libftd2xx/`).
- The target's BSDL in `data/bsdl_files/` (KU15P: `xcku15p_ffve1517.bsd` is
bundled).
- An entry for the target in `data/fpga_registry.yaml` (KU15P is bundled).
- An entry for the target in `data/targets.yaml` (KU15P is bundled).
See [Adding a new FPGA](#6-add-a-new-fpga-target) below.
- For SPI flashing, eventually: a BSCAN proxy bitstream — see the
[Phase 2.5 caveat](#phase-25-spi-through-the-bscan-proxy-bridge-bitstream) at the end.
@@ -34,7 +34,7 @@ README and the `Digilent SMT2` block in `CLAUDE.md` for the why.
```sh
mkdir build && cd build && cmake .. && make && cd ..
./build/bs # run from the repo root: data/probes.yaml, data/fpga_registry.yaml,
./build/bs # run from the repo root: data/probes.yaml, data/targets.yaml,
# data/bsdl_files/ and data/bscan_proxies/ are looked up in the CWD
```
@@ -121,11 +121,11 @@ mis-wired. Power-cycle and re-check the harness before going further.
## 3. Identify the FPGA against the registry
`fpga_info` walks the chain and matches each IDCODE against the
registry in `data/fpga_registry.yaml`:
`target_info` walks the chain and matches each IDCODE against the
registry in `data/targets.yaml`:
```
bs_explorer> fpga_info
bs_explorer> target_info
Device 0 IDCODE 0x04A56093 -> Xilinx Kintex UltraScale+ XCKU15P [Xilinx UltraScale+]
prog: proxy_spi
caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST)
@@ -138,7 +138,7 @@ or `svf` ([§Programming via SVF](#programming-via-svf-lattice-microsemi-)).
If you get `not in registry`, add an entry — see
[Adding a new FPGA](#6-add-a-new-fpga-target).
`fpga_list` prints the whole registry without needing a probe.
`target_list` prints the whole registry without needing a probe.
## JTAG clock (optional)
@@ -216,7 +216,7 @@ way; you'd be there for weeks.
## 6. Add a new FPGA target
The registry — `data/fpga_registry.yaml` at the repo root — holds the
The registry — `data/targets.yaml` at the repo root — holds the
per-part facts that can't be derived from the BSDL alone (or are tedious
to). It's parsed at runtime, so adding a part is **editing YAML, no
rebuild**. The XCKU040 entry already there was added exactly with the
@@ -247,7 +247,7 @@ masks them off.
### c. Add a YAML entry
Append a list item under `fpgas:` in `data/fpga_registry.yaml`:
Append a list item under `fpgas:` in `data/targets.yaml`:
| Key | What it is | XCKU040 |
|-----|-----------|---------|
@@ -263,7 +263,7 @@ Append a list item under `fpgas:` in `data/fpga_registry.yaml`:
| `max_tck_khz` | max safe JTAG TCK in kHz; `jtag_autoinit` clamps + re-opens if exceeded (omit = unspecified) | — |
| `prog` | programming backend `proxy_spi`/`svf`/`none` (omit → inferred: a proxy ⇒ `proxy_spi`, Microsemi/Lattice ⇒ `svf`) | `proxy_spi` |
The resulting entry (verbatim from `data/fpga_registry.yaml`):
The resulting entry (verbatim from `data/targets.yaml`):
```yaml
- name: "Xilinx Kintex UltraScale XCKU040"
@@ -293,7 +293,7 @@ defaults to exact match (`0xFFFFFFFF`).
an `FPGA_CAVEAT_*` bit in `fpga.h`, marking **known hardware gotchas
that change how the tool must drive the part**. It is *not* a free-text
note — each flag is something the code (or you) can branch on. Omit the
field when the part has none. `fpga_info` prints any flag that is set,
field when the part has none. `target_info` prints any flag that is set,
as a human-readable line.
Currently one flag exists:
@@ -310,7 +310,7 @@ Currently one flag exists:
To introduce a *new* caveat: add a `#define FPGA_CAVEAT_xxx (1u << n)`
in `fpga.h`, teach `parse_caveats()` in `fpga.c` its YAML name, use that
name in the YAML, and (if it should be visible) print it in
`cmd_fpga_info` in `script.c`.
`cmd_target_info` in `script.c`.
### d. Verify — no rebuild
@@ -319,12 +319,12 @@ the repo root and check:
```sh
./build/bs
bs_explorer> fpga_list # your part should appear, with its source file
bs_explorer> target_list # your part should appear, with its source file
bs_explorer> jtag_autoinit
bs_explorer> fpga_info # should show your part, family, and any caveats
bs_explorer> target_info # should show your part, family, and any caveats
```
(`fpga_list` reads the registry without needing a probe.)
(`target_list` reads the registry without needing a probe.)
## Phase 2.5: SPI through the BSCAN proxy (bridge bitstream)
@@ -369,7 +369,7 @@ The XCKU15P first has to be **added to the generator's device table**
Once built, drop `bscan_spi_xcku15p.bit` into `data/bscan_proxies/` (it's
MIT, like the KU040 — keep `data/bscan_proxies/LICENSE.quartiq`) and set the
`proxy_bitstream` field on the KU15P entry in `data/fpga_registry.yaml`
`proxy_bitstream` field on the KU15P entry in `data/targets.yaml`
(currently omitted).
### Load the bridge and talk SPI
@@ -434,7 +434,7 @@ compares that flag a failed erase/program.
```
bs_explorer> jtag_open 0 flashpro # the probe profile for your kit
bs_explorer> jtag_autoinit # fpga_info should show 'prog: svf'
bs_explorer> jtag_autoinit # target_info should show 'prog: svf'
bs_explorer> svf_play design.svf
...
SVF done: 1342 commands, 1338 scans, 71 compares
@@ -476,6 +476,31 @@ bitstream/SVF generator for Microsemi (or most vendors) — you need the
vendor tool to *produce* the SVF (Libero has a free tier covering the
small IGLOO2 parts). `bs_explorer` only *plays* it, which is fully open.
## One command: `program`
Rather than remember which backend a part uses, `program <dev> <file>`
dispatches on the device's registry `prog` method:
```
bs_explorer> jtag_autoinit
bs_explorer> program 0 design.svf # prog=svf -> plays the SVF
```
`svf` plays the file; `proxy_spi` points you at the flash workflow
(`bscan_load_bitstream` + `flash_write`/`flash_verify`); `arm_flash`
routes to the ARM backend.
### CPU targets (ARM7/9) — structure only
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`.
## Troubleshooting cheat sheet
| Symptom | Likely cause |
@@ -484,7 +509,7 @@ small IGLOO2 parts). `bs_explorer` only *plays* it, which is fully open.
| `jtag_autoinit` finds 0 devices | TDI/TDO swap, TRST held low, voltage mismatch, or chain broken. |
| All IDCODEs read `0xFFFFFFFF` | TDO floats high — broken TDO link, wrong voltage reference, or a Digilent SMT2 module being driven via raw FTDI MPSSE (use the Digilent backend instead). |
| All IDCODEs read `0x00000000` | TDO tied low or no clock reaching the target. |
| `fpga_info` says "not in registry" | Add an entry to `data/fpga_registry.yaml`. |
| `target_info` says "not in registry" | Add an entry to `data/targets.yaml`. |
| `bscan_shift_dr 32` doesn't return the expected IDCODE | Wrong IR opcode/length, wrong device index, or a multi-device chain (current primitives assume single device). |
| `jtag_spi_xfer` is hopelessly slow | That's expected via EXTEST — switch to BSCAN proxy (Phase 2.5). |
| 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. |