# bs_explorer — Boundary Scan Explorer Command-line tool to explore a JTAG chain, drive an FPGA's pins through boundary scan (BSDL), and **program** parts over JTAG, from a host with an FTDI / Digilent / J-Link probe. Two programming paths: - **Xilinx external SPI configuration flash** — fast, via a BSCAN proxy bitstream loaded into the fabric (~100 KB/s), or slowly via EXTEST pin bit-bang for one-shot checks; - **everything else** (Lattice, Microsemi, CPLDs, …) — by playing a vendor-exported **SVF** file (`svf_play`), one near-universal backend. Based on the [jtag-boundary-scanner](https://github.com/viveris/jtag-boundary-scanner) library by Viveris (LGPL). ## Status - JTAG chain detection through FTDI / J-Link / Linux GPIO / Digilent SMT2 probes: OK - Automatic BSDL loading by IDCODE: OK - Pin control in SAMPLE / EXTEST, incl. slow SPI bit-bang: OK - Target registry (runtime YAML, FPGAs + CPUs: IDCODE → BSDL/debug, IR opcodes, proxy, caveats, programming method): OK - Probe-config profiles (`data/probes.yaml`) + driver-neutral JTAG clock with per-device cap: OK - BSCAN proxy SPI bridge (load proxy bitstream, talk SPI via `USER1`): OK - SPI flash detect / read / erase / program / verify: OK (~100 KB/s via the proxy) - SVF player (`svf_play`) — program any device from a vendor-exported SVF: OK (single-device subset) - `program ` — dispatches to the right backend by the target's `prog` method: OK - ARM7/9 CPU flashing (EmbeddedICE, ARM-USB-OCD): structure in place; debug/flash backend not implemented yet Bundled BSDLs: Xilinx Kintex UltraScale+ KU15P (`xcku15p_ffve1517.bsd`), Kintex UltraScale KU040 (`xcku040_ffva1156.bsd`), and Microsemi IGLOO2 M2GL010T (`m2gl010t-fg484.bsd`). Add more by dropping `.bsd` files in `data/bsdl_files/` plus an entry in `data/targets.yaml` (see [`doc/tutorial.md`](doc/tutorial.md) for adding a target). ## Dependencies - CMake ≥ 3.10, gcc/clang - `readline` (Arch: `readline`, Debian/Ubuntu: `libreadline-dev`) - `libyaml` for the FPGA registry, found via pkg-config `yaml-0.1` (Arch: `libyaml`, Debian/Ubuntu: `libyaml-dev`) - `libftdi1` + `libusb-1.0` for FTDI/Olimex probes, via pkg-config `libftdi1` (Arch: `libftdi`, Debian/Ubuntu: `libftdi1-dev`). Open source — no vendored proprietary SDK; works with any VID:PID and auto-detaches the kernel `ftdi_sio` driver. - *To drive a Digilent SMT2/SMT2-NC probe:* the Digilent [Adept Runtime](https://digilent.com/shop/software/digilent-adept/) installed system-wide (provides `libdjtg.so` + `libdmgr.so`). Nothing from Digilent is vendored — the backend is `dlopen`'d at runtime, so it's built in by default and simply reports no probe if the libs are missing. ## Build ```sh mkdir build && cd build cmake .. make ``` The binary is produced at `build/bs`. The Digilent SMT2 backend is built by default on Linux. To leave it out: ```sh cmake -DBS_ENABLE_DIGILENT=OFF .. ``` ## Run Run from the repository root so the runtime data files are found — they are looked up relative to the current directory: ```sh ./build/bs ``` `bs_explorer` reads, when present in that directory: - `data/config.script` — overrides built-in probe variables (FTDI clock, TRST/SRST pin mapping, …); see `src/modules/config/config.script` for the full list. Loaded at startup. - `data/probes.yaml` — probe-config profiles, applied with `jtag_open ` (`$BS_PROBES` overrides the path). - `data/targets.yaml` — the FPGA target registry (`$BS_TARGETS` overrides the path). - `data/bsdl_files/`, `data/bscan_proxies/` — BSDLs and proxy bitstreams. ## REPL - `` completes command names. - Persistent history (GNU readline) in `~/.bs_explorer_history`: up/down arrows and Ctrl-R recall commands across sessions. - `help` or `?` lists commands; `help ` shows details. - `exit`, `quit`, or Ctrl-D to leave. ## Typical flow **Xilinx external SPI flash — via the BSCAN proxy:** ```sh # 1. List probes, open one by its index ([N]). A probe that needs tweaks # (e.g. an embedded FlashPro) takes a profile from data/probes.yaml: bs_explorer> jtag_probes bs_explorer> jtag_profiles # available profiles bs_explorer> jtag_open 0 # or: jtag_open 0 # 2. Scan the chain and auto-load matching BSDLs bs_explorer> jtag_autoinit # target_info then shows the prog method # 3. Load the BSCAN proxy into the fabric (fast SPI bridge) bs_explorer> bscan_load_bitstream 0 data/bscan_proxies/bscan_spi_xcku040.bit # 4. Talk to the SPI flash through the proxy bs_explorer> flash_detect 0 # JEDEC ID -> chip name / size bs_explorer> flash_write 0 0x10000 image.bin bs_explorer> flash_verify 0 0x10000 image.bin ``` **Anything else (Lattice, Microsemi, …) — play a vendor-exported SVF:** ```sh bs_explorer> jtag_open 0 # e.g. flashpro for a Microsemi kit bs_explorer> jtag_autoinit # identify; prog method should be 'svf' bs_explorer> svf_play design.svf # exported from Libero / Diamond / Radiant ``` The slow EXTEST path (bit-bang SPI on boundary-scan pins, `jtag_mode 0 EXTEST` + `jtag_spi_*`) is only useful for one-shot checks. A minimal example script is in `data/scripts/example_script.txt`; the full walkthrough lives in [`doc/tutorial.md`](doc/tutorial.md). ## Main commands | Category | Commands | |----------|----------| | Script control | `set`, `print`, `print_env_var`, `if`, `goto`, `call`, `return`, `rand`, `init_array`, `system`, `pause` | | Probe / chain | `jtag_probes`, `jtag_open`, `jtag_close`, `jtag_profiles`, `jtag_scan`, `jtag_autoinit`, `jtag_ndev`, `jtag_devices` | | BSDL / pins | `jtag_bsdl`, `jtag_pins`, `jtag_mode`, `jtag_pin_dir`, `jtag_pin_set`, `jtag_pin_get`, `jtag_push_pop` | | I²C / MDIO / SPI over BS pins (EXTEST) | `jtag_i2c_scl`, `jtag_i2c_sda`, `jtag_i2c_rd`, `jtag_i2c_wr`, `jtag_mdio_mdc`, `jtag_mdio_io`, `jtag_mdio_rd`, `jtag_mdio_wr`, `jtag_spi_cs/mosi/miso/clk`, `jtag_spi_xfer` | | Targets (FPGA/CPU) | `target_list`, `target_info` | | BSCAN proxy | `bscan_load_bitstream`, `bscan_jedec`, `bscan_set_ir`, `bscan_shift_dr` | | SPI flash (via proxy) | `flash_detect`, `flash_read`, `flash_erase`, `flash_write`, `flash_verify` | | Program | `program` (dispatch by target), `svf_play` | | Misc | `help`, `?`, `version`, `exit` | Use `help ` for per-command help. ## Supported probes - **FTDI** MPSSE (FT2232D/H, FT4232H, …) — see the `PROBE_FTDI_*` block in `src/modules/config/config.script` for pin mapping and TCK frequency. Boards that wire the FT4232H differently (e.g. the embedded FlashPro on Microsemi eval kits, which needs ADBUS4 left high-Z) are handled by a **probe profile** in `data/probes.yaml`: `jtag_profiles` lists them, `jtag_open ` applies one (e.g. `jtag_open 0 flashpro`). The Olimex **ARM-USB-OCD** (also an FT2232, for ARM CPU targets) has an `arm-usb-ocd` profile slot — pending its control-pin map. - **SEGGER J-Link** - **Linux GPIO** (sysfs; deprecated on recent kernels, libgpiod migration TBD) - **Digilent JTAG-SMT2 / SMT2-NC** — built in by default on Linux (`-DBS_ENABLE_DIGILENT=OFF` to drop it). Required for the USB-JTAG on Xilinx eval boards like the KCU105: those modules ship a Digilent-proprietary firmware that does not respond to plain MPSSE, so the FTDI driver appears to enumerate them but the JTAG chain stays silent. ## Known Xilinx caveats On 7-Series / UltraScale / UltraScale+, `CCLK` is not a regular I/O pin and goes through the `STARTUPE3` primitive, so it cannot be driven directly in EXTEST — the slow EXTEST SPI path therefore can't clock the flash on these parts. The **BSCAN proxy sidesteps this entirely**: it drives `CCLK` from the fabric internally, so flashing runs at full speed. Parts affected are flagged with the `CCLK_VIA_STARTUP` caveat in the registry (`target_info` shows it). ## Repository layout ``` src/ ├── bs/ Application (readline REPL) └── modules/ ├── jtag_core/ TAP state machine, IR/DR shifts ├── bsdl_parser/ .bsd loader ├── bus_over_jtag/ SPI / I²C / MDIO / parallel mem bit-bang (EXTEST) ├── drivers/ FTDI, J-Link, Linux GPIO, LPT, Digilent (optional) ├── target/ Target registry loader: FPGAs + CPUs (data/targets.yaml) ├── bscan/ JTAG TAP primitives + BSCAN proxy (bitstream, SPI-over-USER1) ├── spi_flash/ SPI NOR chip database + read/erase/program/verify ├── svf/ SVF player (program from a vendor-exported SVF) ├── probes/ Probe-config profiles loader (data/probes.yaml) ├── program/ `program` dispatch: routes a target to its backend by prog ├── arm_debug/ ARM (EmbeddedICE) debug + flash backend (not implemented yet) ├── script/ Script engine ├── config/ Built-in config.script ├── os_interface/ Portable fs/network wrappers └── natsort/ Natural-order pin-name sorting data/ — runtime resources, looked up from the CWD — ├── targets.yaml Target registry (FPGAs + CPUs) ├── probes.yaml Probe-config profiles (defaults + per-probe overrides) ├── bsdl_files/ BSDL files for target FPGAs ├── bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq) └── scripts/ Example scripts doc/ Tutorial and longer-form docs ``` ## License `src/modules/jtag_core/` and the original Viveris files are under LGPL 2.1. See `LICENSE` and `src/modules/jtag_core/COPYING.LESSER`. The proxy bitstreams in `data/bscan_proxies/` are from quartiq (MIT) — see `data/bscan_proxies/LICENSE.quartiq`.