Files
bs_explorer/doc/build_xilinx_proxy.md

174 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 3080 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 13 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.