François 2c16a66beb arm_debug: cycle-exact JTAG layer; system-speed memory read works
Read CPU memory over JTAG via system-speed LDM. Validated on the LPC2103:
reads the real ARM reset vectors and contiguous multi-block code.

The core only advances on Run-Test/Idle debug clocks (not Update-DR), so
the trick is keeping that clock count exact:
- "quiet" TAP ops (quiet_set_ir / quiet_shift_dr / quiet_chain_select /
  quiet_eice_read / quiet_latch_chain1) pass through Update but park in
  Pause, never RTI -> they switch chains and read EmbeddedICE WITHOUT
  clocking the core, so they can't clobber the registers a sys-speed LDM
  just loaded.
- clock_core(n) is the only thing that advances the core (n RTI clocks).
- execute_sys_speed: RESTART, then drive the access one clock at a time
  with a quiet DBG_STATUS check between, stopping the instant
  SYSCOMP & DBGACK appear (no over-clock past re-entry).
- after sys-speed: quiet-switch to chain 1, quiet-latch a NOP to displace
  the stale LDMIA, then read_core_regs.
- pre-read pipeline normalization: change_to_arm (17 clocked instrs) for
  a Thumb halt; 17 ARM NOPs for an ARM halt.

WIP: not yet reliable across all halt states - the first read after some
halts times out (SYSCOMP never appears) and leaves the core running.
Within one good halt, reads are consistent and correct. Diagnosis and
next steps in the arm7-debug-dclk-timing note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:00:51 +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%