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:
2026-05-24 12:00:38 +02:00
parent 2a03cb1145
commit 37efccaf50
8 changed files with 97 additions and 19 deletions

View File

@@ -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})

View File

@@ -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;

View File

@@ -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);

View File

@@ -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"