Files
bs_explorer/README.md
François 4ee1c2b631 probes: add probe-config profiles loaded from probes.yaml
- new modules/probes/ parses probes.yaml (libyaml): a defaults: map
  applied on every jtag_open + named profiles: selected with
  `jtag_open <idx> <profile>` (jtag_profiles lists them); each value is
  pushed into the script envvar store the driver reads at open time
- ships a flashpro profile (ADBUS4 high-Z) that lets the IGLOO2 kit's
  embedded FlashPro (FT4232H, port 0) detect the chain
- CLAUDE.md: decision entry for probes.yaml + a design note on the
  probe / JTAG-link / device config strategy (driver-neutral link layer)

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

7.2 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)
  • 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

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.
  • help or ? 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_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
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_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)
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.