diff --git a/doc/build_xilinx_proxy.md b/doc/build_xilinx_proxy.md new file mode 100644 index 0000000..f11e43d --- /dev/null +++ b/doc/build_xilinx_proxy.md @@ -0,0 +1,173 @@ +# Building a BSCAN SPI proxy bitstream for a Xilinx FPGA + +The fast SPI-flash path in `bs_explorer` ([tutorial.md, Phase 2.5](tutorial.md#phase-25-spi-through-the-bscan-proxy-bridge-bitstream)) +loads a tiny "BSCAN proxy" bitstream into the FPGA fabric so SPI can run +through the JTAG `USER1` instruction at fabric speed. Pre-built proxies +for many parts ship in [quartiq/bscan_spi_bitstreams](https://github.com/quartiq/bscan_spi_bitstreams) +(MIT) — that repo also contains the **generator** (Migen + Vivado) used +to make them. + +For parts the generator doesn't already know — **all of UltraScale+** +(XCKU15P, XCKU3P, XCKU11P, XCZU…, Virtex US+), plus any 7-Series / +UltraScale part missing from its table — you have to add a platform +entry and build the `.bit` yourself. This document walks through that, +using the **Kintex UltraScale+ XCKU15P** as the worked example. + +The same recipe applies to any other Xilinx part: only the device +string, package pin LOCs and I/O standard change. + +## What you end up doing + +1. install Vivado and the quartiq generator's Python deps; +2. add a Migen **platform entry** for your part (the generator picks + parts out of a hard-coded table — it is *not* just a CLI flag); +3. run the generator, which calls Vivado to synth / place / route into a + `.bit`; +4. drop the `.bit` into `data/bscan_proxies/` and reference it from + `data/targets.yaml`; +5. smoke-test on the board. + +## Prerequisites + +- **Vivado** matching your part's family. UltraScale+ needs Vivado + 2019.2 or newer (2022.2 is what quartiq's README pins). The free + WebPACK / Standard edition covers the smaller US+ devices like the + XCKU3P / XCKU11P; larger parts (KU15P included) may need a paid + Vivado licence. ISE 14.7 only for Spartan-6 and earlier. +- **Python ≥ 3.8** with `venv`, and the Migen revision pinned in the + generator's `requirements.txt`. +- Roughly 30–80 GB of disk for Vivado, plus a few GB scratch per + synthesis run. + +Confirm Vivado is reachable from your shell: + +```sh +source /opt/Xilinx/Vivado/2022.2/settings64.sh +vivado -version # should print the version +``` + +## 1. Clone quartiq and install its Python deps + +```sh +git clone https://github.com/quartiq/bscan_spi_bitstreams +cd bscan_spi_bitstreams +python3 -m venv --system-site-packages .venv +./.venv/bin/pip install -r requirements.txt +``` + +`--system-site-packages` lets the venv see a system-wide Migen if one +is installed; drop the flag for a fully isolated venv. + +## 2. Add your part to the generator's table + +Open `xilinx_bscan_spi.py`. Near the bottom you'll find a table of +`Platform` subclasses, one per supported part, each setting at least: + +- `device` — the full Vivado part string, e.g. + `"xcku15p-ffve1517-2-e"`. Speed grade and temp range matter — copy + them from your board's schematic or the Vivado project that targets + the same silicon. +- `toolchain` — `"vivado"` for 7-Series and newer, `"ise"` for older. +- The SPI-flash pin **locations** for the part's *configuration bank* + (typically `D00_MOSI_0`, `D01_DIN_0`, `FCS_B_0`). **CCLK is not + declared here** — it is driven inside the FPGA via `STARTUPE3` for + UltraScale/+ and `STARTUPE2` for 7-Series, which the generator + already instantiates. (This is exactly what dissolves the + `cclk_via_startup` caveat described in `CLAUDE.md`.) +- The I/O standard for those pins (`LVCMOS18` for 1.8 V banks like the + KCU105/KU15P config bank, `LVCMOS25`/`LVCMOS33` otherwise). Wrong + voltage ⇒ Vivado DRC errors, or worse, damaged I/O on the flash. + +Pick the closest existing entry as a template. For the KU15P, the +KU040 entry is the right neighbour: same toolchain (`vivado`), same +`STARTUPE3`, same bank-0 pinout shape. Duplicate it, change: + +- the **device string** to your part + package + speed grade; +- the **pin LOCs** if the package differs (e.g. `FFVE1517` vs + `FFVA1156`); +- the **class name**, and add it to the dispatch dictionary at the + bottom of the file so a CLI key resolves to your new class. + +Cross-check the four pin LOCs against the part's *Package Pin* table +in the Xilinx/AMD datasheet — these pads are board-independent (they +are the hard-wired configuration-bank pins), but the package suffix +changes them. + +## 3. Run the generator + +```sh +source /opt/Xilinx/Vivado/2022.2/settings64.sh +./.venv/bin/python3 xilinx_bscan_spi.py xcku15p +``` + +The argument is the **key you added to the dispatch dictionary** in +step 2 — not the raw Vivado part string. The script writes a Migen +build tree, calls Vivado, and on success drops +`bscan_spi_xcku15p.bit` (a few KB — just a `BSCANE2`, a `STARTUPE3` +and a small FSM) in the current directory. + +Expect 1–3 minutes on a modern host. A failed run leaves its logs +under `build_xcku15p/`; common culprits: + +| Vivado error | Fix | +|--------------|-----| +| `[Place 30-574]` pin LOC not on package | wrong package suffix in `device`, or copied LOCs from a different package | +| `[Drc NSTD-1]` unconstrained I/O | missing I/O standard on a pin → fix the platform entry | +| `Part xcku15p-… is not installed` | install the device support pack in Vivado, or pick a part covered by your licence | +| Vivado not found | forgot to `source settings64.sh` before launching the generator | + +## 4. Drop it into bs_explorer + +```sh +cp bscan_spi_xcku15p.bit /path/to/bs_explorer/data/bscan_proxies/ +``` + +The directory's `LICENSE.quartiq` (MIT) covers your build too — leave +it in place. Point the registry entry at the new file (this is the +KU15P entry that currently has `proxy_bitstream` omitted in +`data/targets.yaml`): + +```yaml + - name: "Xilinx Kintex UltraScale+ XCKU15P" + idcode: 0x04A56093 + idcode_mask: 0x0FFFFFFF + family: xilinx_usp + bsdl: xcku15p_ffve1517.bsd + ir_length: 6 + ir_cfg_in: 0x05 + ir_user1: 0x02 + ir_jprogram: 0x0B + ir_jstart: 0x0C + ir_jshutdown: 0x0D + ir_isc_disable: 0x16 + proxy_bitstream: bscan_spi_xcku15p.bit # <-- the file you just built + caveats: cclk_via_startup + prog: proxy_spi +``` + +No rebuild — the registry is loaded at runtime. + +## 5. Smoke-test on the board + +``` +bs_explorer> jtag_open 1 +bs_explorer> jtag_autoinit +bs_explorer> bscan_load_bitstream 0 data/bscan_proxies/bscan_spi_xcku15p.bit +bs_explorer> bscan_jedec 0 +JEDEC ID: 20 BB 19 # or whatever flash your board carries +``` + +A plausible JEDEC ID — manufacturer byte matching the flash on the +schematic (`0x20` Micron, `0xEF` Winbond, `0xC2` Macronix, `0x01` +Cypress/Infineon, `0x9D` ISSI, …) — confirms the proxy is wired right: +`STARTUPE3` is driving `CCLK`, the `BSCANE2` capture is on `USER1`, +and the four pin LOCs match the physical flash. A `00 00 00` or +`FF FF FF` reply almost always means one of the LOCs is wrong — +re-check the package's configuration-bank pinout. + +## Upstreaming (optional) + +If your new part is broadly useful — any stock dev-board MPSoC, common +KU/VU/ZU device — the quartiq project takes PRs against +`xilinx_bscan_spi.py`. Once merged, future users get a pre-built +`.bit` and skip this whole document. diff --git a/doc/tutorial.md b/doc/tutorial.md index d78b30d..ddbc4ce 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -353,24 +353,16 @@ The registry entry for the part points at this file via its quartiq ships `.bit` only for the parts its generator knows — it has **no UltraScale+** proxy (its single UltraScale entry is the KU040), so -the KU15P has to be built from source. You need (o)Migen + Vivado -(2022.2; ISE 14.7 for older parts). From a clone of the quartiq repo, -per its README: +anything in the UltraScale+ family (XCKU15P, XCKU3P, XCKU11P, XCZU…, +Virtex US+, …) has to be built from source. You need (o)Migen + Vivado +(2022.2 for UltraScale/+; ISE 14.7 only for Spartan-6 and earlier). -```sh -python3 -m venv --system-site-packages .venv -./.venv/bin/pip install -r requirements.txt -PATH=$PATH:/opt/Xilinx/Vivado/2022.2/bin \ - ./.venv/bin/python3 xilinx_bscan_spi.py ... -``` +Step-by-step walkthrough in [doc/build_xilinx_proxy.md](build_xilinx_proxy.md), +worked out on the KU15P. -The XCKU15P first has to be **added to the generator's device table** -(a Migen platform entry) — it's not just a command-line part flag. - -Once built, drop `bscan_spi_xcku15p.bit` into `data/bscan_proxies/` (it's +Once built, drop `bscan_spi_.bit` into `data/bscan_proxies/` (it's MIT, like the KU040 — keep `data/bscan_proxies/LICENSE.quartiq`) and set the -`proxy_bitstream` field on the KU15P entry in `data/targets.yaml` -(currently omitted). +`proxy_bitstream` field on the matching entry in `data/targets.yaml`. ### Load the bridge and talk SPI