The Olimex ARM-USB-OCD (and any FT2232 with a custom USB id) couldn't be enumerated by libftd2xx and needed a manual ftdi_sio unbind. libftdi1 (libusb) opens any VID:PID and auto-detaches the kernel driver. - rewrite drivers/ftdi_jtag on libftdi1: enumerate a known VID:PID list (incl. Olimex 15ba:0003/002b) with per-chip channel counts, open by bus/addr + interface, MPSSE via ftdi_write_data/ftdi_read_data (+ SEND_IMMEDIATE for deterministic reads). MPSSE command building, pin map and clocking unchanged. - CMake: link libftdi1 + libusb-1.0 (pkg-config), drop FTDILIB/FTD2XX defines and the libftd2xx.a link; remove the vendored src/libs/libftd2xx. - registry: NXP LPC2103 (ARM7TDMI-S) entry, IDCODE 0x4F1F0F0F. - docs updated (deps, layout, decision note, roadmap phase 8). Validated on hardware: ARM-USB-OCD enumerates, jtag_scan reads the LPC2103 IDCODE 0x4F1F0F0F, target_info -> [cpu, ARM7] prog: arm_flash. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
215 lines
9.6 KiB
Markdown
215 lines
9.6 KiB
Markdown
# 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](https://github.com/viveris/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`](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](https://digilent.com/shop/software/digilent-adept/)
|
|
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
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
./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:**
|
|
|
|
```sh
|
|
# 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:**
|
|
|
|
```sh
|
|
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`](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`.
|