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:
138
doc/tutorial.md
138
doc/tutorial.md
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user