Files
bs_explorer/README.md
François cc2ee5d92c doc: refresh README/tutorial/CLAUDE for profiles, clock, SVF
Bring the docs up to date and keep each in its lane:
- README (overview): both programming paths (Xilinx proxy flash + SVF),
  probe profiles, neutral JTAG clock + per-device cap, runtime YAML
  registry, IGLOO2 bundled; run-from-repo-root fixed
- tutorial (user view): probe profiles + jtag_close, the prog tag, a
  JTAG-clock section, a new "Programming via SVF" section, prog/max_tck
  in the add-a-target table, troubleshooting rows
- CLAUDE.md (design): architecture tree lists the project modules + YAML
  data files; roadmap gains phases 5 (probes/JTAG-link) and 6 (SVF)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 14:50:02 +02:00

8.7 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 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 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
  • FPGA registry (runtime YAML: IDCODE → BSDL, IR opcodes, proxy, caveats, programming method): OK
  • Probe-config profiles (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)

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 bsdl_files/ plus an entry in fpga_registry.yaml (see 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)
  • libftd2xx for FTDI probes (vendored in libs/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 is dlopen'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

Run from the repository root so the runtime data files are found — they are looked up relative to the current directory:

./build/bs/bs

bs_explorer reads, when present in that directory:

  • config.script — overrides built-in probe variables (FTDI clock, TRST/SRST pin mapping, …); see modules/config/config.script for the full list. Loaded at startup.
  • probes.yaml — probe-config profiles, applied with jtag_open <idx> <profile> ($BS_PROBES overrides the path).
  • fpga_registry.yaml — the FPGA target registry ($BS_FPGA_REGISTRY overrides the path).
  • bsdl_files/, bscan_proxies/ — BSDLs and proxy bitstreams.

REPL

  • <Tab> 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 <cmd> shows details.
  • exit, quit, or Ctrl-D to leave.

Typical flow

Xilinx external SPI flash — via the BSCAN proxy:

# 1. List probes, open one by its index ([N]). A probe that needs tweaks
#    (e.g. an embedded FlashPro) takes a profile from probes.yaml:
bs_explorer> jtag_probes
bs_explorer> jtag_profiles               # available profiles
bs_explorer> jtag_open 0                 # or: jtag_open 0 <profile>

# 2. Scan the chain and auto-load matching BSDLs
bs_explorer> jtag_autoinit               # fpga_info then shows the prog method

# 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_write  0 0x10000 image.bin
bs_explorer> flash_verify 0 0x10000 image.bin

Anything else (Lattice, Microsemi, …) — play a vendor-exported SVF:

bs_explorer> jtag_open 0 <profile>       # 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 scripts/example_script.txt; the full walkthrough 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_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
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
SVF player svf_play
Misc help, ?, version, exit

Use help <command> for per-command help.

Supported probes

  • FTDI MPSSE (FT2232D/H, FT4232H, …) — see the PROBE_FTDI_* block in 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 probes.yaml: jtag_profiles lists them, jtag_open <idx> <profile> applies one (e.g. jtag_open 0 flashpro).
  • 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 (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/           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 (probes.yaml)
├── 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)
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
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.