Lets a session hand a probe back (frees its USB handle) without opening another, e.g. to flash two FPGAs on different probes in turn: jtag_close, then jtag_open the next one and jtag_autoinit. Mirrors the DeInit teardown jtagcore_loaddriver already does when switching drivers. Also fix the help printer: no-arg commands (whose params slot is "") printed an empty body because "" was treated as the terminator. Params are now optional and the description always shows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6.9 KiB
bs_explorer — Boundary Scan Explorer
Command-line tool to explore a JTAG chain, drive an FPGA's pins through boundary scan (BSDL), and program the SPI configuration flash attached to an FPGA (Xilinx and others) over JTAG — fast, via a BSCAN proxy bitstream loaded into the fabric (~100 KB/s), or slowly via EXTEST pin bit-bang for one-shot checks.
Based on the 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
- Per-FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats): 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)
Bundled BSDLs: Xilinx Kintex UltraScale+ KU15P
(bsdl_files/xcku15p_ffve1517.bsd) and Kintex UltraScale KU040
(bsdl_files/xcku040_ffva1156.bsd). Add more by dropping .bsd files
in bsdl_files/ (see doc/tutorial.md for adding a
target).
Dependencies
- CMake ≥ 3.10, gcc/clang
readline(Arch:readline, Debian/Ubuntu:libreadline-dev)libyamlfor the FPGA registry, found via pkg-configyaml-0.1(Arch:libyaml, Debian/Ubuntu:libyaml-dev)libftd2xxfor FTDI probes (vendored inlibs/libftd2xx/)- To drive a Digilent SMT2/SMT2-NC probe: the Digilent
Adept Runtime
installed system-wide (provides
libdjtg.so+libdmgr.so). Nothing from Digilent is vendored — the backend isdlopen'd at runtime, so it's built in by default and simply reports no probe if the libs are missing.
Build
mkdir build && cd build
cmake ..
make
The binary is produced at build/bs/bs.
The Digilent SMT2 backend is built by default on Linux. To leave it out:
cmake -DBS_ENABLE_DIGILENT=OFF ..
Run
cd build
./bs/bs
At startup, bs_explorer looks for a config.script file in the
current directory to override default settings (FTDI clock, TRST/SRST
pin mapping, etc.). See modules/config/config.script for the full
list of variables.
REPL
<Tab>completes command names.- Persistent history (GNU readline) in
~/.bs_explorer_history: up/down arrows and Ctrl-R recall commands across sessions. helpor?lists commands;help <cmd>shows details.exit,quit, or Ctrl-D to leave.
Typical flow
# 1. List probes, open one by its index (the [N] in the list)
bs_explorer> jtag_probes
[0] 0x00000000 <probe description>
bs_explorer> jtag_open 0 # or the raw 0x id shown next to it
# 2. Scan the chain and auto-load matching BSDLs
bs_explorer> jtag_autoinit
# 3. Load the BSCAN proxy into the fabric (fast SPI bridge)
bs_explorer> bscan_load_bitstream 0 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_read 0 0x0 256 # hex dump
bs_explorer> flash_erase 0 0x10000 4096
bs_explorer> flash_write 0 0x10000 image.bin
bs_explorer> flash_verify 0 0x10000 image.bin
The slow EXTEST path (bit-bang SPI on boundary-scan pins, jtag_mode 0 EXTEST + jtag_spi_*) is only useful for one-shot checks — see the
tutorial. A minimal example script is in scripts/example_script.txt;
the full walkthrough (probe → proxy → flash) lives in
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_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 |
| FPGA registry | fpga_list, fpga_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 |
| Misc | help, ?, version, exit |
Use help <command> for per-command help.
Supported probes
- FTDI MPSSE (FT2232D/H, FT4232H, …) — see the
PROBE_FTDI_*block inmodules/config/config.scriptfor pin mapping and TCK frequency. - 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=OFFto 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 (fpga_info
shows it).
Repository layout
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)
├── fpga/ Registry loader (parses fpga_registry.yaml at runtime)
├── bscan_spi/ BSCAN proxy loader + fast SPI-over-USER1 bridge
├── spi_flash/ SPI NOR chip database + read/erase/program/verify
├── script/ Script engine
├── config/ Built-in config.script
├── os_interface/ Portable fs/network wrappers
└── natsort/ Natural-order pin-name sorting
fpga_registry.yaml FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats)
bsdl_files/ BSDL files for target FPGAs
bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq)
scripts/ Example scripts
doc/ Tutorial and longer-form docs
libs/libftd2xx/ Vendored FTDI SDK
License
modules/jtag_core/ and the original Viveris files are under LGPL 2.1.
See LICENSE and modules/jtag_core/COPYING.LESSER. The proxy
bitstreams in bscan_proxies/ are from quartiq (MIT) — see
bscan_proxies/LICENSE.quartiq.