François fda6aed077 arm_debug: cycle-exact chain-1, Thumb->ARM, sys-speed re-entry
The chain-1 access is now deterministic: a bare 33-bit bscan_shift_dr
(breakpoint | flip32(instr)) is exactly one debug clock per access (the
Update->Run-Test/Idle transition). The earlier "+1 idle" double-clocked
the pipeline and the earlier all-zero/constant reads were the core being
in Thumb state. Validated on the LPC2103 by a known-pattern register
round-trip (write r1..r15, read back -> exact match; r15/PC differs by
the expected pipeline offset).

- c1_xfer: drop the extra idle dwell (one access == one debug clock).
- mem_read: detect Thumb (ITBIT) and change_to_arm in one continuous
  chain-1 session so no chain switch clocks the core mid-sequence.
- execute_sys_speed: drop the post-RESTART idle burst and poll
  DBG_STATUS straight away (matches OpenOCD); the system-speed LDM now
  re-enters debug (DBGACK & SYSCOMP) instead of running free.

WIP: the read_core_regs after a system-speed access is phase-shifted by
the EmbeddedICE<->chain-1 switch, so memory reads come back misaligned
(capturing injected instructions). Next step + diagnosis in the
arm7-debug-dclk-timing note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:23:45 +02:00
2025-02-18 11:36:18 +01:00
2025-02-12 12:37:38 +01:00
2025-02-11 22:21:56 +01:00

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
  • 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 <dev> <file> — 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 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 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.

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_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 <idx> <profile> ($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

  • <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 data/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               # 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:

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 data/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
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 <command> 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 <idx> <profile> 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.

Description
No description provided
Readme LGPL-2.1 2 MiB
Languages
C 98.7%
CMake 1.3%