bscan_spi: implement bscan_spi_xfer over the jtagspi proxy
One CS-framed transaction: marker + 32-bit count + MOSI + read-latency skip + MISO, MSB-first on the wire, matching OpenOCD's jtagspi so the quartiq proxy bitstreams work unchanged. Half-duplex (tx,txlen,rx,rxlen) signature, single-device chain. NOT yet validated on hardware — protocol follows the OpenOCD reference but has not been confirmed against a live proxy + flash. Validation (read JEDEC ID on the KCU105) is the next step.
This commit is contained in:
@@ -261,17 +261,86 @@ int bscan_load_bitstream_file(jtag_core *jc, const fpga_target *t,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Pipeline latency between a MOSI bit going in and its MISO bit
|
||||
* appearing on TDO, in TCK cycles. For a single-device chain this is
|
||||
* one (the proxy registers TDO); equals jtag_tap_count_enabled() in
|
||||
* OpenOCD's jtagspi. The header asserts a single-device chain. */
|
||||
#define BSCAN_SPI_READ_LATENCY 1
|
||||
|
||||
int bscan_spi_xfer(jtag_core *jc, const fpga_target *t,
|
||||
const uint8_t *tx, uint8_t *rx, size_t nbytes)
|
||||
const uint8_t *tx, size_t txlen,
|
||||
uint8_t *rx, size_t rxlen)
|
||||
{
|
||||
/* Skeleton — to be completed once we can validate the protocol
|
||||
* against an actual quartiq jtagspi proxy bitstream.
|
||||
*
|
||||
* Expected outline:
|
||||
* - bscan_set_ir(jc, t->ir_user1, t->ir_length)
|
||||
* - bscan_shift_dr with <header><payload>, where the header
|
||||
* encodes the bit count and CS state per the proxy convention.
|
||||
* - extract MISO bytes from the TDO data. */
|
||||
(void)jc; (void)t; (void)tx; (void)rx; (void)nbytes;
|
||||
return -1;
|
||||
/* DR frame (quartiq/OpenOCD jtagspi proxy, single device):
|
||||
* marker(1)=1 | count(32, MSB-first) | MOSI(txlen*8, MSB-first/byte)
|
||||
* | latency skip | MISO capture(rxlen*8, MSB-first/byte)
|
||||
* count = total SPI bits - 1. The skip absorbs the TDO pipeline
|
||||
* delay so the captured MISO aligns to byte boundaries.
|
||||
* Bits are placed LSB-first per byte, the layout bscan_shift_dr
|
||||
* shifts in order; ordering them here gives MSB-first on the wire. */
|
||||
size_t spi_bytes = txlen + rxlen;
|
||||
uint32_t count;
|
||||
int total_bits, dr_bytes, capture_start, bit, j;
|
||||
size_t i;
|
||||
uint8_t *dr_out, *dr_in;
|
||||
|
||||
if (!drv_ok(jc) || !t || !t->ir_user1 || spi_bytes == 0) return -1;
|
||||
if (txlen && !tx) return -1;
|
||||
if (rxlen && !rx) return -1;
|
||||
|
||||
count = (uint32_t)(spi_bytes * 8u) - 1u;
|
||||
|
||||
total_bits = 1 + 32 + (int)txlen * 8;
|
||||
if (rxlen) total_bits += BSCAN_SPI_READ_LATENCY + (int)rxlen * 8;
|
||||
dr_bytes = (total_bits + 7) / 8;
|
||||
|
||||
dr_out = calloc(1, (size_t)dr_bytes);
|
||||
dr_in = rxlen ? calloc(1, (size_t)dr_bytes) : NULL;
|
||||
if (!dr_out || (rxlen && !dr_in)) { free(dr_out); free(dr_in); return -1; }
|
||||
|
||||
#define BS_SET(buf, pos) ((buf)[(pos) >> 3] |= (uint8_t)(1u << ((pos) & 7)))
|
||||
#define BS_GET(buf, pos) (((buf)[(pos) >> 3] >> ((pos) & 7)) & 1u)
|
||||
|
||||
bit = 0;
|
||||
BS_SET(dr_out, bit); bit++; /* marker = 1 */
|
||||
|
||||
for (j = 31; j >= 0; j--) { /* count, MSB-first */
|
||||
if (count & (1u << j)) BS_SET(dr_out, bit);
|
||||
bit++;
|
||||
}
|
||||
|
||||
for (i = 0; i < txlen; i++) { /* MOSI, MSB-first/byte */
|
||||
for (j = 7; j >= 0; j--) {
|
||||
if (tx[i] & (1u << j)) BS_SET(dr_out, bit);
|
||||
bit++;
|
||||
}
|
||||
}
|
||||
|
||||
capture_start = -1;
|
||||
if (rxlen) {
|
||||
bit += BSCAN_SPI_READ_LATENCY; /* skip pipeline delay */
|
||||
capture_start = bit;
|
||||
bit += (int)rxlen * 8; /* MISO region (MOSI=0) */
|
||||
}
|
||||
|
||||
if (bscan_set_ir(jc, t->ir_user1, t->ir_length) < 0 ||
|
||||
bscan_shift_dr(jc, dr_out, dr_in, total_bits) < 0) {
|
||||
free(dr_out); free(dr_in);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rxlen) {
|
||||
memset(rx, 0, rxlen);
|
||||
for (i = 0; i < rxlen * 8; i++) {
|
||||
if (BS_GET(dr_in, capture_start + (int)i)) {
|
||||
rx[i >> 3] |= (uint8_t)(1u << (7 - (i & 7))); /* MSB-first */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef BS_SET
|
||||
#undef BS_GET
|
||||
free(dr_out);
|
||||
free(dr_in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,14 @@ int bscan_load_bitstream(jtag_core *jc, const fpga_target *t,
|
||||
int bscan_load_bitstream_file(jtag_core *jc, const fpga_target *t,
|
||||
const char *path);
|
||||
|
||||
/* Transfer `nbytes` of SPI data through the BSCAN proxy (USER1 DR).
|
||||
* Placeholder: protocol details deferred until an actual proxy
|
||||
* bitstream is available for testing. */
|
||||
/* One CS-framed SPI transaction through the BSCAN proxy (USER1 DR):
|
||||
* clock out `txlen` MOSI bytes (e.g. command + address + write data),
|
||||
* then read `rxlen` MISO bytes into `rx`. Either length may be 0.
|
||||
* Bytes are MSB-first on the wire; bit-order juggling is internal.
|
||||
* Follows the quartiq/OpenOCD jtagspi proxy framing. Single-device
|
||||
* chain only. Requires a proxy bitstream already loaded (USER1 live). */
|
||||
int bscan_spi_xfer(jtag_core *jc, const fpga_target *t,
|
||||
const uint8_t *tx, uint8_t *rx, size_t nbytes);
|
||||
const uint8_t *tx, size_t txlen,
|
||||
uint8_t *rx, size_t rxlen);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user