doc: refresh README/tutorial/CLAUDE for profiles, clock, SVF

Bring the docs up to date and keep each in its lane:
- README (overview): both programming paths (Xilinx proxy flash + SVF),
  probe profiles, neutral JTAG clock + per-device cap, runtime YAML
  registry, IGLOO2 bundled; run-from-repo-root fixed
- tutorial (user view): probe profiles + jtag_close, the prog tag, a
  JTAG-clock section, a new "Programming via SVF" section, prog/max_tck
  in the add-a-target table, troubleshooting rows
- CLAUDE.md (design): architecture tree lists the project modules + YAML
  data files; roadmap gains phases 5 (probes/JTAG-link) and 6 (SVF)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 14:50:02 +02:00
parent c77d86efd0
commit cc2ee5d92c
3 changed files with 199 additions and 47 deletions

View File

@@ -1,9 +1,16 @@
# Tutorial — from probe detection to SPI flash
# Tutorial — from probe to programmed part
This walks through the full `bs_explorer` flow on a Xilinx Kintex
UltraScale+ KU15P board connected via an FTDI MPSSE probe. The
commands are identical for any FPGA registered in `modules/fpga/`; only
the IDCODE and BSDL filename change.
This walks through the full `bs_explorer` flow: detect a probe, scan the
chain, identify the device, and program it. There are two programming
paths and the tutorial covers both:
- **Xilinx external SPI configuration flash**, via the BSCAN proxy —
shown on a Kintex UltraScale+ KU15P / UltraScale KU040;
- **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 `fpga_registry.yaml`;
only the IDCODE and BSDL filename change.
## Prerequisites
@@ -18,17 +25,17 @@ the IDCODE and BSDL filename change.
[Phase 2.5 caveat](#phase-25-spi-through-the-bscan-proxy-bridge-bitstream) at the end.
If your board uses a Digilent JTAG-SMT2 / SMT2-NC module (KCU105,
ZCU102, …), you need the optional Digilent backend: install the Adept
Runtime system-wide and configure with `cmake -DBS_ENABLE_DIGILENT=ON
..`. Plain MPSSE does not work on those modules — see the README and
the `Digilent SMT2` block in `CLAUDE.md` for the why.
ZCU102, …), the Digilent backend is built in by default on Linux — just
install the Adept Runtime system-wide so `libdjtg.so`/`libdmgr.so` are
present at runtime. Plain MPSSE does not work on those modules — see the
README and the `Digilent SMT2` block in `CLAUDE.md` for the why.
## Build & launch
```sh
mkdir build && cd build
cmake .. && make
./bs/bs
mkdir build && cd build && cmake .. && make && cd ..
./build/bs/bs # run from the repo root: probes.yaml, fpga_registry.yaml,
# bsdl_files/ and bscan_proxies/ are looked up in the CWD
```
You should see:
@@ -69,6 +76,29 @@ If `jtag_open` fails: check `lsusb` for the probe VID:PID, make
sure the user has access to the USB device (udev rule or group), and
confirm no other process holds the probe (e.g. `openocd`).
### Probe profiles
Some probes need pin tweaks the built-in defaults don't cover — e.g. the
embedded **FlashPro** on Microsemi eval kits (an FT4232H whose JTAG sits
on channel A = index 0 and needs `ADBUS4` left high-Z, or the chain stays
silent). `probes.yaml` captures these as named profiles; apply one when
opening:
```
bs_explorer> jtag_profiles
2 probe profile(s) in probes.yaml:
flashpro
ft2232h
bs_explorer> jtag_open 0 flashpro
Applied probe profile 'flashpro'.
Probe Ok !
```
`jtag_close` releases the current probe (frees its USB handle) — use it
to hand the probe to another tool, or to program two boards on different
probes in turn: `jtag_close`, then `jtag_open` the next one and
`jtag_autoinit` to rescan.
## 2. Scan the JTAG chain
The fastest path is `jtag_autoinit`: it scans the chain *and*
@@ -97,14 +127,38 @@ registry in `fpga_registry.yaml`:
```
bs_explorer> fpga_info
Device 0 IDCODE 0x04A56093 -> Xilinx Kintex UltraScale+ XCKU15P [Xilinx UltraScale+]
prog: proxy_spi
caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST)
```
`prog:` is the programming backend the registry assigns the part —
`proxy_spi` (Xilinx external flash, [§Phase 2.5](#phase-25-spi-through-the-bscan-proxy-bridge-bitstream))
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.
## JTAG clock (optional)
Each driver has its own default TCK (FTDI 1 MHz, Digilent 4 MHz). To set
a single driver-neutral clock, before `jtag_open`:
```
bs_explorer> set JTAG_TCK_FREQ_KHZ 1000
```
It's applied at open (mapped to the FTDI driver's variable, or read
directly by the Digilent one). A registry entry may declare a
`max_tck_khz`; if your requested clock exceeds it, `jtag_autoinit` clamps
it and re-opens the probe at the safe rate:
```
WARNING : JTAG clock 2000 kHz exceeds the device max 1000 kHz; clamping.
Re-opening at 1000 kHz and re-scanning.
```
## 4. (Optional) Sanity-check the low-level JTAG primitives
Before doing anything fancy, you can verify that `bscan_set_ir` and
@@ -206,6 +260,8 @@ Append a list item under `fpgas:` in `fpga_registry.yaml`:
| `ir_cfg_in` / `ir_user1` / `ir_jprogram` / `ir_jstart` / `ir_jshutdown` / `ir_isc_disable` | private IR opcodes (omit = 0/N/A) | from the BSDL |
| `proxy_bitstream` | BSCAN proxy `.bit` in `bscan_proxies/` (omit if none) | `bscan_spi_xcku040.bit` |
| `caveats` | space/comma-separated flag names (omit if none) | `cclk_via_startup` |
| `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 `fpga_registry.yaml`):
@@ -224,6 +280,7 @@ The resulting entry (verbatim from `fpga_registry.yaml`):
ir_isc_disable: 0x16
proxy_bitstream: bscan_spi_xcku040.bit
caveats: cclk_via_startup
prog: proxy_spi
```
Omit any field that doesn't apply: a missing `proxy_bitstream` means
@@ -365,6 +422,60 @@ OpenOCD's `src/flash/nor/jtagspi.c` so the same bitstreams work. Generic
flash `read`/`erase`/`program`/`verify` (Phase 3) will be built on top
of this primitive.
## Programming via SVF (Lattice, Microsemi, …)
The BSCAN-proxy path above is Xilinx-specific (external SPI config flash).
For everything else, the universal path is to play an **SVF** file
exported by the vendor tool — Libero / FlashPro Express (Microsemi),
Diamond / Radiant (Lattice), Vivado (Xilinx fabric), Quartus, … The
vendor bakes the programming *algorithm* into the SVF; `bs_explorer` just
replays its `SIR`/`SDR`/`RUNTEST` vectors and checks the masked `TDO`
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> svf_play design.svf
...
SVF done: 1342 commands, 1338 scans, 71 compares
```
A `TDO` mismatch stops play and points at the failing vector:
```
ERROR : line 842: SDR TDO mismatch at bit 17 (len 696)
```
— usually a wrong device, a too-fast clock, or a part that isn't
erased/unlocked.
You can sanity-check the player without a programming file using a tiny
hand-written IDCODE check (after `STATE RESET` the TAP auto-loads IDCODE
into DR):
```
! idcode.svf — masked IDCODE check (top nibble = revision)
STATE RESET;
SDR 32 TDO (0F8031CF) MASK (0FFFFFFF);
```
```
bs_explorer> svf_play idcode.svf
SVF done: 2 commands, 1 scans, 1 compares
```
**Supported subset (single-device chain):** `SIR`/`SDR` with
`TDI`/`TDO`/`MASK`/`SMASK` and the masked compare; `RUNTEST` (TCK/SCK
counts and SEC delays); `STATE` (RESET/IDLE); `ENDIR`/`ENDDR` (IDLE only);
`HIR`/`HDR`/`TIR`/`TDR` (length 0 only); `TRST`; `FREQUENCY`. SMASK is
parsed but not applied. Multi-device headers/trailers (non-zero) and
non-IDLE end states are rejected with a clear error.
**Generating the SVF is the closed step.** There is no open-source
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.
## Troubleshooting cheat sheet
| Symptom | Likely cause |
@@ -376,6 +487,9 @@ of this primitive.
| `fpga_info` says "not in registry" | Add an entry to `fpga_registry.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. |
| 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. |
## Where to go from here