doc: document SPI flashing through the BSCAN proxy

Rewrite the Phase 2.5 tutorial section for the now-working path: fetch
the proxy bitstream, bscan_load_bitstream, bscan_jedec, and the
bscan_spi_xfer primitive. Note the JPROGRAM reconfiguration caveat. The
shown JEDEC output is illustrative — not yet hardware-confirmed.
This commit is contained in:
2026-05-23 17:16:36 +02:00
parent 8e3428788c
commit f1c8a8aac7

View File

@@ -15,7 +15,7 @@ the IDCODE and BSDL filename change.
- 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.
[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
@@ -128,7 +128,7 @@ that before moving on. The opcode and IR length come from the
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.
[Phase 2.5](#phase-25-spi-through-the-bscan-proxy-bridge-bitstream) for the production path.
Put the FPGA in EXTEST, then map the four SPI signals onto the FPGA's
BSDL pin names:
@@ -191,33 +191,63 @@ For an FPGA that's not in the registry yet:
5. **Verify** with `fpga_info` after `jtag_autoinit`.
## Phase 2.5: BSCAN proxy
## Phase 2.5: SPI through the BSCAN proxy (bridge bitstream)
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 (~50200 KB/s).
Talking to the SPI flash via EXTEST is fine for a JEDEC ID but useless
for real flashing (~30 B/s, days to weeks for a config part). The
production path loads a tiny **BSCAN proxy** bitstream into the FPGA
fabric, then runs SPI through the `USER1` instruction at fabric speed
(~50200 KB/s). The proxy uses a `BSCANE2` primitive to bridge the
`USER1` DR shift to the flash pins, and drives `CCLK` from the fabric
internally — so the `STARTUPE3`/CCLK problem of EXTEST disappears.
The infrastructure is in `modules/bscan_spi/`:
### Get the bridge bitstream
Pre-built proxies live in `quartiq/bscan_spi_bitstreams` (MIT). Drop
the one for your part in `bscan_proxies/`:
```sh
curl -L -o bscan_proxies/bscan_spi_xcku040.bit \
https://raw.githubusercontent.com/quartiq/bscan_spi_bitstreams/master/bscan_spi_xcku040.bit
```
The registry entry for the part points at this file via its
`proxy_bitstream` field (e.g. the XCKU040 entry → `bscan_spi_xcku040.bit`).
### Load the bridge and talk SPI
```
bs_explorer> jtag_open 1
bs_explorer> jtag_autoinit
bs_explorer> bscan_load_bitstream 0 bscan_spi_xcku15p.bit
bs_explorer> bscan_load_bitstream 0 bscan_proxies/bscan_spi_xcku040.bit
bs_explorer> bscan_jedec 0
JEDEC ID: 20 XX XX (manufacturer 0x20, device 0xXXXX)
```
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.
(The exact device bytes depend on the part fitted; on the KCU105 the
manufacturer byte should read `0x20` = Micron.)
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.
`bscan_load_bitstream` runs JPROGRAM → CFG_IN → shift → JSTART, which
**reconfigures the FPGA fabric**: the design currently running on the
part is wiped and replaced by the proxy. This is undone by a
power-cycle (the configuration flash reloads the original design at the
next boot), but be aware the board stops doing whatever it was doing.
`bscan_jedec` issues `0x9F` + 3 read bytes through the proxy. A sane
answer (here `0x20` = Micron, the KCU105's config flash) confirms the
whole proxy path end to end: the `bscan_spi_xfer()` framing, the
MSB-first bit order, and the TDO read-latency skew.
### The transfer primitive
`bscan_spi_xfer(jc, t, tx, txlen, rx, rxlen)` in
`modules/bscan_spi/bscan_spi.c` performs one CS-framed transaction:
clock out `txlen` MOSI bytes, then read `rxlen` MISO bytes. It builds
the quartiq/OpenOCD jtagspi DR frame
(`marker | bit-count | MOSI | latency-skip | MISO`) and matches
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.
## Troubleshooting cheat sheet