doc: add end-to-end tutorial
doc/tutorial.md walks from probe detection to JEDEC ID over EXTEST and forward-references the BSCAN proxy path. Includes: - prerequisites and build/launch - chain scan, FPGA identification, registry lookup - IR/DR primitive sanity check via the IDCODE register - SPI JEDEC ID over EXTEST (with the speed caveat) - recipe to add a new FPGA target - troubleshooting cheat sheet README and CLAUDE.md updated to point at it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ modules/ — Viveris's library (LGPL, unchanged) —
|
||||
└── natsort/ Natural pin-name sorting
|
||||
bsdl_files/ BSDL files for target FPGAs
|
||||
scripts/ Example scripts
|
||||
doc/ Tutorial and longer-form docs (doc/tutorial.md is the end-to-end walkthrough)
|
||||
libs/libftd2xx/ Vendored FTDI SDK
|
||||
```
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ bs_explorer> jtag_spi_rd_wr 9F000000
|
||||
```
|
||||
|
||||
A minimal example script is provided in `scripts/example_script.txt`.
|
||||
A full step-by-step walkthrough (probe detection → JEDEC ID → flashing
|
||||
via BSCAN proxy) lives in [`doc/tutorial.md`](doc/tutorial.md).
|
||||
|
||||
## Main commands
|
||||
|
||||
@@ -126,6 +128,7 @@ modules/
|
||||
└── natsort/ Natural-order pin-name sorting
|
||||
bsdl_files/ BSDL files for target FPGAs
|
||||
scripts/ Example scripts
|
||||
doc/ Tutorial and longer-form docs
|
||||
libs/libftd2xx/ Vendored FTDI SDK
|
||||
```
|
||||
|
||||
|
||||
231
doc/tutorial.md
Normal file
231
doc/tutorial.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# Tutorial — from probe detection to SPI flash
|
||||
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A JTAG probe physically wired to the target's TCK/TDI/TDO/TMS/TRST.
|
||||
- `libftd2xx` reachable at runtime (already vendored under
|
||||
`libs/libftd2xx/`).
|
||||
- The target's BSDL in `bsdl_files/` (KU15P: `xcku15p_ffve1517.bsd` is
|
||||
bundled).
|
||||
- An entry for the target in `modules/fpga/fpga.c` (KU15P is bundled).
|
||||
See [Adding a new FPGA](#adding-a-new-fpga-target) below.
|
||||
- For SPI flashing, eventually: a BSCAN proxy bitstream — see the
|
||||
[Phase 2.5 caveat](#phase-25-bscan-proxy) at the end.
|
||||
|
||||
## Build & launch
|
||||
|
||||
```sh
|
||||
mkdir build && cd build
|
||||
cmake .. && make
|
||||
./bs/bs
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```
|
||||
Boundary Scan Explorer v2.6.7.1
|
||||
Based on Viveris jtag-boundary-scanner
|
||||
|
||||
3 probe driver(s) available.
|
||||
Type 'help' or '?' to list commands, 'exit' or Ctrl-D to quit.
|
||||
<Tab> completes commands.
|
||||
|
||||
bs_explorer>
|
||||
```
|
||||
|
||||
`<Tab>` completes commands; `help <cmd>` shows per-command help;
|
||||
Ctrl-D or `exit` quits.
|
||||
|
||||
## 1. Detect and open the probe
|
||||
|
||||
```
|
||||
bs_explorer> jtag_get_probes_list
|
||||
```
|
||||
|
||||
Lists each probe the loaded drivers can see. With an FTDI cable
|
||||
plugged in, you should get at least one entry. Pick its index (1-based
|
||||
in the output) and open it:
|
||||
|
||||
```
|
||||
bs_explorer> jtag_open_probe 1
|
||||
```
|
||||
|
||||
If `jtag_open_probe` fails: check `lsusb` for the FTDI 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`).
|
||||
|
||||
## 2. Scan the JTAG chain
|
||||
|
||||
The fastest path is `jtag_autoinit`: it scans the chain *and*
|
||||
auto-loads every BSDL in `bsdl_files/` whose IDCODE matches a device.
|
||||
|
||||
```
|
||||
bs_explorer> jtag_autoinit
|
||||
```
|
||||
|
||||
Expected output for a single-FPGA board:
|
||||
|
||||
```
|
||||
1 device(s) found
|
||||
Device 0 (04A56093 - XCKU15P_FFVE1517) - BSDL Loaded : ...xcku15p_ffve1517.bsd
|
||||
```
|
||||
|
||||
If the device count is 0 or the IDCODE is `0xFFFFFFFF`/`0x00000000`:
|
||||
TDI/TDO are likely swapped, TRST not released, or the probe is
|
||||
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
|
||||
compile-time registry in `modules/fpga/fpga.c`:
|
||||
|
||||
```
|
||||
bs_explorer> fpga_info
|
||||
Device 0 IDCODE 0x04A56093 -> Xilinx Kintex UltraScale+ XCKU15P [Xilinx UltraScale+]
|
||||
quirk: CCLK routed via STARTUP primitive (not drivable in EXTEST)
|
||||
```
|
||||
|
||||
If you get `not in registry`, add an entry — see
|
||||
[Adding a new FPGA](#adding-a-new-fpga-target).
|
||||
|
||||
`fpga_list` prints the whole registry without needing a probe.
|
||||
|
||||
## 4. (Optional) Sanity-check the low-level JTAG primitives
|
||||
|
||||
Before doing anything fancy, you can verify that `bscan_set_ir` and
|
||||
`bscan_shift_dr` actually move bits correctly on your hardware. Drop
|
||||
the BSDL state (so `jtag_core` doesn't fight us on IR caching) and
|
||||
shift IDCODE manually:
|
||||
|
||||
```
|
||||
bs_explorer> jtag_open_probe 1
|
||||
bs_explorer> jtag_init_scan # detects devices, does NOT load BSDL
|
||||
bs_explorer> bscan_set_ir 9 6 # IDCODE opcode (KU15P: 0x09, IR=6 bits)
|
||||
bs_explorer> bscan_shift_dr 32
|
||||
DR = 04 A5 60 93 # bytes printed MSB-first
|
||||
```
|
||||
|
||||
Match → primitives are healthy. Mismatch → wiring or clock issue, fix
|
||||
that before moving on. The opcode and IR length come from the
|
||||
`fpga_target` (and ultimately the BSDL `INSTRUCTION_OPCODE` block).
|
||||
|
||||
## 5. Read the SPI flash JEDEC ID via EXTEST (validation only)
|
||||
|
||||
This is the *slow* path — useful to confirm the SPI pins are wired
|
||||
correctly to the flash, **not** a viable way to flash megabytes. See
|
||||
[Phase 2.5](#phase-25-bscan-proxy) for the production path.
|
||||
|
||||
Put the FPGA in EXTEST, then map the four SPI signals onto the FPGA's
|
||||
BSDL pin names:
|
||||
|
||||
```
|
||||
bs_explorer> jtag_autoinit
|
||||
bs_explorer> jtag_set_mode 0 EXTEST
|
||||
bs_explorer> jtag_set_spi_cs_pin 0 <PIN_CS> 0
|
||||
bs_explorer> jtag_set_spi_clk_pin 0 <PIN_CLK> 0
|
||||
bs_explorer> jtag_set_spi_mosi_pin 0 <PIN_MOSI> 0
|
||||
bs_explorer> jtag_set_spi_miso_pin 0 <PIN_MISO> 0
|
||||
```
|
||||
|
||||
Pin names depend on the board: dump `jtag_get_pins_list 0` to discover
|
||||
them. On Xilinx FPGAs, the SPI flash is typically wired to the
|
||||
configuration bank (e.g. `D00_MOSI_0`, `D01_DIN_0`, `FCS_B_0`) —
|
||||
**except** `CCLK`, which goes through the `STARTUPE3` primitive and is
|
||||
not drivable in EXTEST (the `CCLK_VIA_STARTUP` quirk on the target).
|
||||
|
||||
Send the JEDEC ID command (`0x9F` + 3 dummy bytes):
|
||||
|
||||
```
|
||||
bs_explorer> jtag_spi_rd_wr 9F000000
|
||||
SPI TX: 9F 00 00 00
|
||||
SPI RX: FF XX YY ZZ # XX YY ZZ identifies the flash vendor/part
|
||||
```
|
||||
|
||||
This takes several seconds even for 4 bytes — that's the
|
||||
~30 bytes/sec EXTEST ceiling. Don't try to read the whole flash this
|
||||
way; you'd be there for weeks.
|
||||
|
||||
## 6. Add a new FPGA target
|
||||
|
||||
For an FPGA that's not in the registry yet:
|
||||
|
||||
1. **Drop the BSDL** in `bsdl_files/`. The file you want is on the
|
||||
vendor's site (Xilinx: in the device download under
|
||||
"BSDL files"; Intel: in Quartus install dir; Lattice: per part).
|
||||
|
||||
2. **Read the facts you need** from the BSDL:
|
||||
```
|
||||
attribute IDCODE_REGISTER ... -> IDCODE pattern (4-bit version
|
||||
masked, lower 28 bits matter)
|
||||
attribute INSTRUCTION_LENGTH ... -> ir_length
|
||||
attribute INSTRUCTION_OPCODE ... -> opcodes for IDCODE, EXTEST,
|
||||
SAMPLE, BYPASS, and private
|
||||
instructions for the family
|
||||
(USER1, CFG_IN, JPROGRAM,
|
||||
JSTART, JSHUTDOWN, ISC_DISABLE
|
||||
on Xilinx)
|
||||
```
|
||||
|
||||
3. **Add an entry** to `fpga_registry[]` in `modules/fpga/fpga.c`,
|
||||
mirroring the existing KU15P entry. Set `proxy_bitstream` to
|
||||
`NULL` for now; wire it up when you have one. Set quirks as
|
||||
appropriate (e.g. `FPGA_QUIRK_CCLK_VIA_STARTUP` for any
|
||||
Xilinx 7-Series/UltraScale/UltraScale+).
|
||||
|
||||
4. **Rebuild**. The registry is compile-time, no runtime registration.
|
||||
|
||||
5. **Verify** with `fpga_info` after `jtag_autoinit`.
|
||||
|
||||
## Phase 2.5: BSCAN proxy
|
||||
|
||||
Flashing tens of megabytes via the EXTEST SPI bridge is not feasible
|
||||
(~30 B/s, days to weeks). The realistic path is to load a tiny "BSCAN
|
||||
proxy" bitstream into the FPGA fabric, then talk SPI through the
|
||||
`USER1` instruction at fabric speed (~50–200 KB/s).
|
||||
|
||||
The infrastructure is in `modules/bscan_spi/`:
|
||||
|
||||
```
|
||||
bs_explorer> jtag_autoinit
|
||||
bs_explorer> bscan_load_bitstream 0 bscan_spi_xcku15p.bit
|
||||
```
|
||||
|
||||
This runs JPROGRAM → CFG_IN → shift → JSTART. The bitstream itself is
|
||||
**not yet bundled**. Sources:
|
||||
- `quartiq/bscan_spi_bitstreams` on GitHub (BSD-2). Pre-built `.bit`
|
||||
files for most Xilinx parts; Migen sources to rebuild any missing
|
||||
part using Vivado.
|
||||
- OpenOCD reference: `src/flash/nor/jtagspi.c` documents the host
|
||||
side protocol.
|
||||
|
||||
Once a proxy is loaded, the matching `bscan_spi_xfer()` function (and
|
||||
the script command that will wrap it) will run real SPI transfers. The
|
||||
proxy protocol detail is still a TODO in `modules/bscan_spi/bscan_spi.c`
|
||||
— it will be wired against an actual `.bit` so we can validate
|
||||
end-to-end.
|
||||
|
||||
## Troubleshooting cheat sheet
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---------|--------------|
|
||||
| `jtag_get_probes_list` returns nothing | FTDI not enumerated. Check `lsusb`, udev permissions, conflicting process. |
|
||||
| `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 or wrong voltage reference. |
|
||||
| All IDCODEs read `0x00000000` | TDO tied low or no clock reaching the target. |
|
||||
| `fpga_info` says "not in registry" | Add the part to `fpga_registry[]`. |
|
||||
| `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_rd_wr` is hopelessly slow | That's expected via EXTEST — switch to BSCAN proxy (Phase 2.5). |
|
||||
|
||||
## Where to go from here
|
||||
|
||||
- `help` in the REPL lists all commands; `help <cmd>` gives details.
|
||||
- `CLAUDE.md` at the repo root captures the architecture, roadmap and
|
||||
technical decisions (machine-independent).
|
||||
- `README.md` is the user-facing summary.
|
||||
- The original Viveris library and its docs live untouched under
|
||||
`modules/jtag_core/`, `modules/bsdl_parser/`, `modules/bus_over_jtag/`.
|
||||
Reference in New Issue
Block a user