Several BSDL files can legitimately match one IDCODE (same die in different packages, or twins like SmartFusion2 / IGLOO2). The autoinit loader loaded every match onto the device, silently overwriting the first, and flagged it as "ID conflit ?". Keep the first match and skip later files with the same IDCODE instead. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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_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.