bscan: rename from bscan_spi, add TAP primitives for the SVF player
The module outgrew its "SPI bridge" name — it's mostly generic single-device JTAG TAP primitives now. Rename modules/bscan_spi -> modules/bscan (dir, files, library target, includes, doc paths); bscan_* function names and bscan_spi_xfer() kept. Add the two primitives the SVF player needs beyond shift_dr: - bscan_shift_ir: general IR scan with TDO capture (bscan_set_ir is opcode-only, no readback) - bscan_tap_reset: force Test-Logic-Reset, land in Run-Test/Idle Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -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. |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
@@ -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 <stddef.h>
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user