diff --git a/CLAUDE.md b/CLAUDE.md index f6bb116..e9083d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 ``` diff --git a/README.md b/README.md index eb8e5c3..318091c 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/doc/tutorial.md b/doc/tutorial.md new file mode 100644 index 0000000..5009d74 --- /dev/null +++ b/doc/tutorial.md @@ -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. + completes commands. + +bs_explorer> +``` + +`` completes commands; `help ` 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 0 +bs_explorer> jtag_set_spi_clk_pin 0 0 +bs_explorer> jtag_set_spi_mosi_pin 0 0 +bs_explorer> jtag_set_spi_miso_pin 0 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 ` 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/`.