diff --git a/CLAUDE.md b/CLAUDE.md index 5f4598c..1a55d54 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ to FPGAs (Xilinx KU15P first, then others) over JTAG, from a CLI tool running on a host with an FTDI probe. The Viveris library itself lives unchanged in `modules/`. Everything -new is in `bs/` (the REPL) and future modules (`fpga/`, `bscan_spi/`, +new is in `bs/` (the REPL) and future modules (`fpga/`, `bscan/`, `spi_flash/`) sitting alongside the Viveris ones. ## Architecture @@ -50,7 +50,7 @@ Adding a feature usually means adding a new script command in |-------|--------|--------|---------| | 1 | `bs/` cleanup, REPL polish, README | **done** (commit `7cb3627`) | Fix format-strings, delete dead code, tab-completion, banner | | 2 | `fpga/` | **done** (commit `545fe09`) | Per-target descriptor (IDCODE, BSDL, IR codes, proxy path, caveats). Compile-time registry. | -| 2.5 | `bscan_spi/` | **done** (commit `dec0d14`) | Load BSCAN proxy bitstream via `CFG_IN`, expose fast `bscan_spi_xfer()` via `USER1`. Required for realistic flashing speeds. | +| 2.5 | `bscan/` | **done** (commit `dec0d14`) | Load BSCAN proxy bitstream via `CFG_IN`, expose fast `bscan_spi_xfer()` via `USER1`. Required for realistic flashing speeds. | | 3 | `spi_flash/` | **done** (commit `c4afe87`) | Chip DB (JEDEC ID → page/sector/cmd set) + generic `read/erase/program/verify` over an `xfer` callback. detect+read validated on KCU105; erase/program implemented but not yet hardware-tested. | | 4 | script commands | **done** (commit `d6f843e`) | `flash_detect`, `flash_read` (+file), `flash_erase`, `flash_write`, `flash_verify`. Full set validated on KCU105 (save/erase/write-random/verify/restore round-trip). ~100 KB/s write once the proxy is loaded. | @@ -234,7 +234,7 @@ progress and partial ops. The equivalent SVF ("indirect flash") is huge | Target | Recommended path | |--------|------------------| -| Xilinx external SPI config flash | **native proxy** (`bscan_spi/`+`spi_flash/`, done). SVF works but is bloated. | +| Xilinx external SPI config flash | **native proxy** (`bscan/`+`spi_flash/`, done). SVF works but is bloated. | | Xilinx fabric config (volatile) | SVF, or our `bscan_load_bitstream` (equivalent) | | Lattice MachXO2/3 (internal flash) | **SVF** (Diamond/Radiant export). Native IEEE-1532 ISC optional, only for a self-contained `.jed` flow. | | Microsemi IGLOO2 / SmartFusion2 | **SVF/STAPL** (Libero/FlashPro export). Native algorithm too complex/proprietary. | diff --git a/README.md b/README.md index 3b217d1..a51bd83 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ modules/ ├── 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 +├── bscan/ JTAG TAP primitives + BSCAN proxy (bitstream, SPI-over-USER1) ├── spi_flash/ SPI NOR chip database + read/erase/program/verify ├── script/ Script engine ├── config/ Built-in config.script diff --git a/bscan_proxies/README.md b/bscan_proxies/README.md index edfe9e6..141aad1 100644 --- a/bscan_proxies/README.md +++ b/bscan_proxies/README.md @@ -7,7 +7,7 @@ enabling fast SPI flashing — see `doc/tutorial.md`, Phase 2.5. These `.bit` files are **not** built here. They come from [quartiq/bscan_spi_bitstreams](https://github.com/quartiq/bscan_spi_bitstreams), © QUARTIQ GmbH, MIT-licensed — see `LICENSE.quartiq`. The host-side -framing in `modules/bscan_spi/` matches OpenOCD's `jtagspi` so the same +framing in `modules/bscan/` matches OpenOCD's `jtagspi` so the same bitstreams work. | File | Part | Used by | diff --git a/doc/tutorial.md b/doc/tutorial.md index bb9f72e..841d803 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -357,7 +357,7 @@ read-latency skew. ### The transfer primitive `bscan_spi_xfer(jc, t, tx, txlen, rx, rxlen)` in -`modules/bscan_spi/bscan_spi.c` performs one CS-framed transaction: +`modules/bscan/bscan.c` performs one CS-framed transaction: clock out `txlen` MOSI bytes, then read `rxlen` MISO bytes. It builds the quartiq/OpenOCD jtagspi DR frame (`marker | bit-count | MOSI | latency-skip | MISO`) and matches diff --git a/modules/bscan_spi/CMakeLists.txt b/modules/bscan/CMakeLists.txt similarity index 78% rename from modules/bscan_spi/CMakeLists.txt rename to modules/bscan/CMakeLists.txt index 598da03..c1aa299 100644 --- a/modules/bscan_spi/CMakeLists.txt +++ b/modules/bscan/CMakeLists.txt @@ -4,4 +4,4 @@ file(GLOB_RECURSE ALL_SOURCES "*.c") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -add_library(bscan_spi ${ALL_SOURCES}) +add_library(bscan ${ALL_SOURCES}) diff --git a/modules/bscan_spi/bscan_spi.c b/modules/bscan/bscan.c similarity index 85% rename from modules/bscan_spi/bscan_spi.c rename to modules/bscan/bscan.c index f767055..0f0e1a4 100644 --- a/modules/bscan_spi/bscan_spi.c +++ b/modules/bscan/bscan.c @@ -2,7 +2,7 @@ #include #include -#include "bscan_spi.h" +#include "bscan.h" /* JTAG byte format expected by drv_TXRX_DATA / drv_TX_TMS: * bit 0 (JTAG_STR_DOUT) = TDI value @@ -108,6 +108,78 @@ int bscan_shift_dr(jtag_core *jc, const uint8_t *tdi, uint8_t *tdo, int nbits) return 0; } +int bscan_shift_ir(jtag_core *jc, const uint8_t *tdi, uint8_t *tdo, int nbits) +{ + unsigned char tms_buf[8]; + unsigned char *buf_out, *buf_in; + int i; + + if (!drv_ok(jc) || nbits <= 0) { + return -1; + } + + /* Idle -> Select-DR -> Select-IR -> Capture-IR -> Shift-IR */ + tms_buf[0] = JTAG_STR_TMS; + tms_buf[1] = JTAG_STR_TMS; + tms_buf[2] = 0; + tms_buf[3] = 0; + jc->io_functions.drv_TX_TMS(jc, tms_buf, 4); + + buf_out = malloc(nbits); + if (!buf_out) return -1; + buf_in = tdo ? malloc(nbits) : NULL; + if (tdo && !buf_in) { free(buf_out); return -1; } + + for (i = 0; i < nbits; i++) { + uint8_t bit = 0; + if (tdi) { + bit = (tdi[i / 8] >> (i & 7)) & 1u; + } + buf_out[i] = bit ? JTAG_STR_DOUT : 0; + if (i == nbits - 1) { + buf_out[i] |= JTAG_STR_TMS; /* last bit -> Exit1-IR */ + } + } + jc->io_functions.drv_TXRX_DATA(jc, buf_out, buf_in, nbits); + + if (tdo && buf_in) { + memset(tdo, 0, (size_t)((nbits + 7) / 8)); + for (i = 0; i < nbits; i++) { + if (buf_in[i]) { + tdo[i / 8] |= (uint8_t)(1u << (i & 7)); + } + } + } + + free(buf_out); + free(buf_in); + + /* Exit1-IR -> Update-IR -> Idle */ + tms_buf[0] = JTAG_STR_TMS; + tms_buf[1] = 0; + jc->io_functions.drv_TX_TMS(jc, tms_buf, 2); + + return 0; +} + +int bscan_tap_reset(jtag_core *jc) +{ + unsigned char tms_buf[8]; + + if (!drv_ok(jc)) return -1; + + /* 5 TMS=1 forces Test-Logic-Reset from any state, then 1 TMS=0 + * lands in Run-Test/Idle (where the shift primitives start). */ + tms_buf[0] = JTAG_STR_TMS; + tms_buf[1] = JTAG_STR_TMS; + tms_buf[2] = JTAG_STR_TMS; + tms_buf[3] = JTAG_STR_TMS; + tms_buf[4] = JTAG_STR_TMS; + tms_buf[5] = 0; + jc->io_functions.drv_TX_TMS(jc, tms_buf, 6); + return 0; +} + int bscan_idle_cycles(jtag_core *jc, int ncycles) { unsigned char *buf; diff --git a/modules/bscan_spi/bscan_spi.h b/modules/bscan/bscan.h similarity index 74% rename from modules/bscan_spi/bscan_spi.h rename to modules/bscan/bscan.h index 3b4d117..48b2ad5 100644 --- a/modules/bscan_spi/bscan_spi.h +++ b/modules/bscan/bscan.h @@ -1,22 +1,23 @@ -#ifndef _BSCAN_SPI_H -#define _BSCAN_SPI_H +#ifndef _BSCAN_H +#define _BSCAN_H /* - * BSCAN-proxy SPI bridge (Phase 2.5). + * Single-device JTAG primitives and BSCAN-proxy operations. * * Provides: - * - low-level JTAG primitives (set_ir, shift_dr, idle_cycles) that - * operate directly on jc->io_functions, leaving jtag_core untouched; + * - low-level JTAG TAP primitives (set_ir, shift_ir, shift_dr, + * idle_cycles, tap_reset) that operate directly on jc->io_functions, + * leaving jtag_core untouched. These are the building blocks the SVF + * player and the proxy paths share; * - bitstream loading via CFG_IN to install a BSCAN proxy in the FPGA - * fabric; + * fabric (Xilinx); * - a fast SPI transfer routine via USER1 once the proxy is loaded. * + * (Was modules/bscan_spi/ — renamed once it grew past the SPI bridge + * into general TAP primitives.) + * * Current assumption: single device on the JTAG chain. Multi-device * support requires knowing the IR length of bypassed devices; defer. - * - * The SPI transfer entry point is wired against the quartiq jtagspi - * proxy convention but the protocol header still needs to be confirmed - * against an actual proxy bitstream (see openocd src/flash/nor/jtagspi.c). */ #include @@ -34,6 +35,11 @@ int bscan_set_ir(jtag_core *jc, unsigned int opcode, int ir_length); * `tdo` may be NULL (write only). Both buffers are LSB-first per byte. */ int bscan_shift_dr(jtag_core *jc, const uint8_t *tdi, uint8_t *tdo, int nbits); +/* Like bscan_shift_dr but through Shift-IR — a general IR scan with TDO + * capture (bscan_set_ir is opcode-only). Single-device chain; buffers + * LSB-first per byte. Both ends in Run-Test/Idle. */ +int bscan_shift_ir(jtag_core *jc, const uint8_t *tdi, uint8_t *tdo, int nbits); + /* Emit `ncycles` TCK cycles while staying in Run-Test/Idle. */ int bscan_idle_cycles(jtag_core *jc, int ncycles); diff --git a/modules/script/script.c b/modules/script/script.c index af8523a..fc0e79a 100644 --- a/modules/script/script.c +++ b/modules/script/script.c @@ -39,7 +39,7 @@ #include "os_interface/os_interface.h" #include "fpga/fpga.h" #include "probes/probes.h" -#include "bscan_spi/bscan_spi.h" +#include "bscan/bscan.h" #include "spi_flash/spi_flash.h" #include "env.h"