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

@@ -12,28 +12,40 @@ when reality changes, not for every transient task.
`bs_explorer` is an application layer on top of Viveris's `bs_explorer` is an application layer on top of Viveris's
[jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner) [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner)
library (LGPL). End goal: program SPI configuration memories attached library (LGPL). End goal: program FPGAs/CPLDs over JTAG from a CLI tool
to FPGAs (Xilinx KU15P first, then others) over JTAG, from a CLI tool on a host with an FTDI/Digilent/J-Link probe — Xilinx external SPI
running on a host with an FTDI probe. 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 `modules/`. Everything The Viveris library itself lives unchanged in `modules/`. Everything
new is in `bs/` (the REPL) and future modules (`fpga/`, `bscan/`, new is in `bs/` (the REPL) and the project modules (`fpga/`, `bscan/`,
`spi_flash/`) sitting alongside the Viveris ones. `spi_flash/`, `svf/`, `probes/`) sitting alongside the Viveris ones.
## Architecture ## Architecture
``` ```
bs/ Application (readline REPL, no business logic) bs/ Application (readline REPL, no business logic)
modules/ — Viveris's library (LGPL, unchanged) — modules/
— Viveris's library (LGPL, unchanged) —
├── jtag_core/ TAP state machine, IR/DR shifts ├── jtag_core/ TAP state machine, IR/DR shifts
├── bsdl_parser/ .bsd loader ├── bsdl_parser/ .bsd loader
├── bus_over_jtag/ SPI/I²C/MDIO/parallel mem bit-bang over EXTEST ├── bus_over_jtag/ SPI/I²C/MDIO/parallel mem bit-bang over EXTEST
├── drivers/ FTDI, J-Link, Linux GPIO, LPT, Digilent (optional) ├── drivers/ FTDI, J-Link, Linux GPIO, LPT, Digilent (optional, dlopen)
├── script/ Script engine (40+ commands, the real UI) ├── script/ Script engine (the real UI)
├── config/ Built-in config.script ├── config/ Built-in config.script
├── os_interface/ Portable fs/network wrappers ├── os_interface/ Portable fs/network wrappers
── natsort/ Natural pin-name sorting ── natsort/ Natural pin-name sorting
— new (this project) —
├── fpga/ Registry loader (parses fpga_registry.yaml, libyaml)
├── 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 probes.yaml, libyaml)
fpga_registry.yaml FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats, prog, max_tck)
probes.yaml Probe-config profiles (defaults + per-probe overrides)
bsdl_files/ BSDL files for target FPGAs bsdl_files/ BSDL files for target FPGAs
bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq)
scripts/ Example scripts scripts/ Example scripts
doc/ Tutorial and longer-form docs (doc/tutorial.md is the end-to-end walkthrough) doc/ Tutorial and longer-form docs (doc/tutorial.md is the end-to-end walkthrough)
libs/libftd2xx/ Vendored FTDI SDK libs/libftd2xx/ Vendored FTDI SDK
@@ -49,10 +61,12 @@ Adding a feature usually means adding a new script command in
| Phase | Module | Status | Summary | | Phase | Module | Status | Summary |
|-------|--------|--------|---------| |-------|--------|--------|---------|
| 1 | `bs/` cleanup, REPL polish, README | **done** (commit `7cb3627`) | Fix format-strings, delete dead code, tab-completion, banner | | 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). Compile-time registry. | | 2 | `fpga/` | **done** (commit `545fe09`) | Per-target descriptor (IDCODE, BSDL, IR codes, proxy path, caveats). Now a **runtime YAML** registry (`fpga_registry.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. | | 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. | | 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. | | 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** | `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. |
Move forward phase by phase: validate one with the user before starting Move forward phase by phase: validate one with the user before starting
the next. Don't break the validated path the next. Don't break the validated path

View File

@@ -1,10 +1,14 @@
# bs_explorer — Boundary Scan Explorer # bs_explorer — Boundary Scan Explorer
Command-line tool to explore a JTAG chain, drive an FPGA's pins through Command-line tool to explore a JTAG chain, drive an FPGA's pins through
boundary scan (BSDL), and program the SPI configuration flash attached boundary scan (BSDL), and **program** parts over JTAG, from a host with
to an FPGA (Xilinx and others) over JTAG — fast, via a BSCAN proxy an FTDI / Digilent / J-Link probe. Two programming paths:
- **Xilinx external SPI configuration flash** — fast, via a BSCAN proxy
bitstream loaded into the fabric (~100 KB/s), or slowly via EXTEST pin bitstream loaded into the fabric (~100 KB/s), or slowly via EXTEST pin
bit-bang for one-shot checks. bit-bang for one-shot checks;
- **everything else** (Lattice, Microsemi, CPLDs, …) — by playing a
vendor-exported **SVF** file (`svf_play`), one near-universal backend.
Based on the [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner) Based on the [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner)
library by Viveris (LGPL). library by Viveris (LGPL).
@@ -14,15 +18,17 @@ library by Viveris (LGPL).
- JTAG chain detection through FTDI / J-Link / Linux GPIO / Digilent SMT2 probes: OK - JTAG chain detection through FTDI / J-Link / Linux GPIO / Digilent SMT2 probes: OK
- Automatic BSDL loading by IDCODE: OK - Automatic BSDL loading by IDCODE: OK
- Pin control in SAMPLE / EXTEST, incl. slow SPI bit-bang: OK - Pin control in SAMPLE / EXTEST, incl. slow SPI bit-bang: OK
- Per-FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats): OK - FPGA registry (runtime YAML: IDCODE → BSDL, IR opcodes, proxy, caveats, programming method): OK
- Probe-config profiles (`probes.yaml`) + driver-neutral JTAG clock with per-device cap: OK
- BSCAN proxy SPI bridge (load proxy bitstream, talk SPI via `USER1`): OK - BSCAN proxy SPI bridge (load proxy bitstream, talk SPI via `USER1`): OK
- SPI flash detect / read / erase / program / verify: OK (~100 KB/s via the proxy) - SPI flash detect / read / erase / program / verify: OK (~100 KB/s via the proxy)
- SVF player (`svf_play`) — program any device from a vendor-exported SVF: OK (single-device subset)
Bundled BSDLs: Xilinx Kintex UltraScale+ KU15P Bundled BSDLs: Xilinx Kintex UltraScale+ KU15P
(`bsdl_files/xcku15p_ffve1517.bsd`) and Kintex UltraScale KU040 (`xcku15p_ffve1517.bsd`), Kintex UltraScale KU040 (`xcku040_ffva1156.bsd`),
(`bsdl_files/xcku040_ffva1156.bsd`). Add more by dropping `.bsd` files and Microsemi IGLOO2 M2GL010T (`m2gl010t-fg484.bsd`). Add more by dropping
in `bsdl_files/` (see [`doc/tutorial.md`](doc/tutorial.md) for adding a `.bsd` files in `bsdl_files/` plus an entry in `fpga_registry.yaml` (see
target). [`doc/tutorial.md`](doc/tutorial.md) for adding a target).
## Dependencies ## Dependencies
@@ -56,15 +62,23 @@ cmake -DBS_ENABLE_DIGILENT=OFF ..
## Run ## Run
Run from the repository root so the runtime data files are found — they
are looked up relative to the current directory:
```sh ```sh
cd build ./build/bs/bs
./bs/bs
``` ```
At startup, `bs_explorer` looks for a `config.script` file in the `bs_explorer` reads, when present in that directory:
current directory to override default settings (FTDI clock, TRST/SRST
pin mapping, etc.). See `modules/config/config.script` for the full - `config.script` — overrides built-in probe variables (FTDI clock,
list of variables. TRST/SRST pin mapping, …); see `modules/config/config.script` for the
full list. Loaded at startup.
- `probes.yaml` — probe-config profiles, applied with
`jtag_open <idx> <profile>` (`$BS_PROBES` overrides the path).
- `fpga_registry.yaml` — the FPGA target registry
(`$BS_FPGA_REGISTRY` overrides the path).
- `bsdl_files/`, `bscan_proxies/` — BSDLs and proxy bitstreams.
## REPL ## REPL
@@ -76,31 +90,39 @@ list of variables.
## Typical flow ## Typical flow
**Xilinx external SPI flash — via the BSCAN proxy:**
```sh ```sh
# 1. List probes, open one by its index (the [N] in the list) # 1. List probes, open one by its index ([N]). A probe that needs tweaks
# (e.g. an embedded FlashPro) takes a profile from probes.yaml:
bs_explorer> jtag_probes bs_explorer> jtag_probes
[0] 0x00000000 <probe description> bs_explorer> jtag_profiles # available profiles
bs_explorer> jtag_open 0 # or the raw 0x id shown next to it bs_explorer> jtag_open 0 # or: jtag_open 0 <profile>
# 2. Scan the chain and auto-load matching BSDLs # 2. Scan the chain and auto-load matching BSDLs
bs_explorer> jtag_autoinit bs_explorer> jtag_autoinit # fpga_info then shows the prog method
# 3. Load the BSCAN proxy into the fabric (fast SPI bridge) # 3. Load the BSCAN proxy into the fabric (fast SPI bridge)
bs_explorer> bscan_load_bitstream 0 bscan_proxies/bscan_spi_xcku040.bit bs_explorer> bscan_load_bitstream 0 bscan_proxies/bscan_spi_xcku040.bit
# 4. Talk to the SPI flash through the proxy # 4. Talk to the SPI flash through the proxy
bs_explorer> flash_detect 0 # JEDEC ID -> chip name / size bs_explorer> flash_detect 0 # JEDEC ID -> chip name / size
bs_explorer> flash_read 0 0x0 256 # hex dump
bs_explorer> flash_erase 0 0x10000 4096
bs_explorer> flash_write 0 0x10000 image.bin bs_explorer> flash_write 0 0x10000 image.bin
bs_explorer> flash_verify 0 0x10000 image.bin bs_explorer> flash_verify 0 0x10000 image.bin
``` ```
**Anything else (Lattice, Microsemi, …) — play a vendor-exported SVF:**
```sh
bs_explorer> jtag_open 0 <profile> # e.g. flashpro for a Microsemi kit
bs_explorer> jtag_autoinit # identify; prog method should be 'svf'
bs_explorer> svf_play design.svf # exported from Libero / Diamond / Radiant
```
The slow EXTEST path (bit-bang SPI on boundary-scan pins, `jtag_mode 0 The slow EXTEST path (bit-bang SPI on boundary-scan pins, `jtag_mode 0
EXTEST` + `jtag_spi_*`) is only useful for one-shot checks — see the EXTEST` + `jtag_spi_*`) is only useful for one-shot checks. A minimal
tutorial. A minimal example script is in `scripts/example_script.txt`; example script is in `scripts/example_script.txt`; the full walkthrough
the full walkthrough (probe → proxy → flash) lives in lives in [`doc/tutorial.md`](doc/tutorial.md).
[`doc/tutorial.md`](doc/tutorial.md).
## Main commands ## Main commands
@@ -159,6 +181,8 @@ modules/
├── fpga/ Registry loader (parses fpga_registry.yaml at runtime) ├── fpga/ Registry loader (parses fpga_registry.yaml at runtime)
├── bscan/ JTAG TAP primitives + BSCAN proxy (bitstream, SPI-over-USER1) ├── bscan/ JTAG TAP primitives + BSCAN proxy (bitstream, SPI-over-USER1)
├── spi_flash/ SPI NOR chip database + read/erase/program/verify ├── spi_flash/ SPI NOR chip database + read/erase/program/verify
├── svf/ SVF player (program from a vendor-exported SVF)
├── probes/ Probe-config profiles loader (probes.yaml)
├── script/ Script engine ├── script/ Script engine
├── config/ Built-in config.script ├── config/ Built-in config.script
├── os_interface/ Portable fs/network wrappers ├── os_interface/ Portable fs/network wrappers

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 This walks through the full `bs_explorer` flow: detect a probe, scan the
UltraScale+ KU15P board connected via an FTDI MPSSE probe. The chain, identify the device, and program it. There are two programming
commands are identical for any FPGA registered in `modules/fpga/`; only paths and the tutorial covers both:
the IDCODE and BSDL filename change.
- **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 ## 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. [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, If your board uses a Digilent JTAG-SMT2 / SMT2-NC module (KCU105,
ZCU102, …), you need the optional Digilent backend: install the Adept ZCU102, …), the Digilent backend is built in by default on Linux — just
Runtime system-wide and configure with `cmake -DBS_ENABLE_DIGILENT=ON install the Adept Runtime system-wide so `libdjtg.so`/`libdmgr.so` are
..`. Plain MPSSE does not work on those modules — see the README and present at runtime. Plain MPSSE does not work on those modules — see the
the `Digilent SMT2` block in `CLAUDE.md` for the why. README and the `Digilent SMT2` block in `CLAUDE.md` for the why.
## Build & launch ## Build & launch
```sh ```sh
mkdir build && cd build mkdir build && cd build && cmake .. && make && cd ..
cmake .. && make ./build/bs/bs # run from the repo root: probes.yaml, fpga_registry.yaml,
./bs/bs # bsdl_files/ and bscan_proxies/ are looked up in the CWD
``` ```
You should see: 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 sure the user has access to the USB device (udev rule or group), and
confirm no other process holds the probe (e.g. `openocd`). 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 ## 2. Scan the JTAG chain
The fastest path is `jtag_autoinit`: it scans the chain *and* The fastest path is `jtag_autoinit`: it scans the chain *and*
@@ -97,14 +127,38 @@ registry in `fpga_registry.yaml`:
``` ```
bs_explorer> fpga_info bs_explorer> fpga_info
Device 0 IDCODE 0x04A56093 -> Xilinx Kintex UltraScale+ XCKU15P [Xilinx UltraScale+] Device 0 IDCODE 0x04A56093 -> Xilinx Kintex UltraScale+ XCKU15P [Xilinx UltraScale+]
prog: proxy_spi
caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST) 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 If you get `not in registry`, add an entry — see
[Adding a new FPGA](#6-add-a-new-fpga-target). [Adding a new FPGA](#6-add-a-new-fpga-target).
`fpga_list` prints the whole registry without needing a probe. `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 ## 4. (Optional) Sanity-check the low-level JTAG primitives
Before doing anything fancy, you can verify that `bscan_set_ir` and 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 | | `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` | | `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` | | `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`): The resulting entry (verbatim from `fpga_registry.yaml`):
@@ -224,6 +280,7 @@ The resulting entry (verbatim from `fpga_registry.yaml`):
ir_isc_disable: 0x16 ir_isc_disable: 0x16
proxy_bitstream: bscan_spi_xcku040.bit proxy_bitstream: bscan_spi_xcku040.bit
caveats: cclk_via_startup caveats: cclk_via_startup
prog: proxy_spi
``` ```
Omit any field that doesn't apply: a missing `proxy_bitstream` means 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 flash `read`/`erase`/`program`/`verify` (Phase 3) will be built on top
of this primitive. 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 ## Troubleshooting cheat sheet
| Symptom | Likely cause | | Symptom | Likely cause |
@@ -376,6 +487,9 @@ of this primitive.
| `fpga_info` says "not in registry" | Add an entry to `fpga_registry.yaml`. | | `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). | | `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). | | `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 ## Where to go from here