# bs_explorer — Boundary Scan Explorer Command-line tool to explore a JTAG chain, drive an FPGA's pins through boundary scan (BSDL), and — eventually — program SPI memories attached to the FPGA (Xilinx and others) by bit-banging SPI on I/O pins placed in EXTEST. 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: OK - SPI bit-bang on 4 FPGA pins (MOSI/MISO/CS/CLK): OK (low-level primitive) - SPI flash programming (JEDEC detect, erase, page program, verify): **planned** - Multi-FPGA abstraction (per-target pin → flash mapping): **planned** Only one BSDL is bundled so far: Xilinx Kintex UltraScale+ KU15P (`bsdl_files/xcku15p_ffve1517.bsd`). Add more by dropping `.bsd` files in `bsdl_files/`. ## Dependencies - CMake ≥ 3.10, gcc/clang - `readline` (Arch: `readline`, Debian/Ubuntu: `libreadline-dev`) - `libftd2xx` for FTDI probes (vendored in `libs/libftd2xx/`) - *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/bs`. The Digilent SMT2 backend is built by default on Linux. To leave it out: ```sh cmake -DBS_ENABLE_DIGILENT=OFF .. ``` ## Run ```sh 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 - `` completes command names. - History handled by GNU readline (up/down arrows, Ctrl-R, …). - `help` or `?` lists commands; `help ` shows details. - `exit`, `quit`, or Ctrl-D to leave. ## Typical flow ```sh # 1. List probes, then open one by its index (the [N] in the list) bs_explorer> jtag_probes [0] 0x00000000 bs_explorer> jtag_open 0 # or the raw 0x id shown next to it # 2. Scan the chain and auto-load BSDL files bs_explorer> jtag_autoinit # 3. Switch device 0 to EXTEST (direct pin control) bs_explorer> jtag_mode 0 EXTEST # 4. Wire the 4 SPI pins onto the FPGA's BSDL pins # (exact names depend on the loaded BSDL) bs_explorer> jtag_spi_cs 0 0 bs_explorer> jtag_spi_clk 0 0 bs_explorer> jtag_spi_mosi 0 0 bs_explorer> jtag_spi_miso 0 0 # 5. Read the flash JEDEC ID (0x9F + 3 dummies) bs_explorer> jtag_spi_xfer 9F000000 ``` A minimal example script is provided in `scripts/example_script.txt`. A full step-by-step walkthrough (probe detection → JEDEC ID → flashing via BSCAN proxy) 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_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 | `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` | | Misc | `help`, `?`, `version`, `exit` | Use `help ` 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. - **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. Known workarounds: - use the private `ISC_DISABLE` instruction; - route the SPI clock through another user pin of the FPGA. To be handled when the FPGA abstraction layer is added. ## 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 ├── drivers/ FTDI, J-Link, Linux GPIO, LPT, Digilent (optional) ├── script/ Script engine (40+ commands) ├── config/ Built-in config.script ├── os_interface/ Portable fs/network wrappers └── natsort/ Natural-order pin-name sorting bsdl_files/ BSDL files for target FPGAs 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`.