script: add flash_detect and flash_read commands
Read-only SPI flash commands over the BSCAN proxy (via a small adapter to spi_flash's xfer callback). flash_detect identifies the chip from its JEDEC ID; flash_read hex-dumps a region. Validated on the KCU105: flash_detect -> Micron MT25QU256, flash_read shows the stored bitstream (sync word 0xAA995566 at 0x50). Erase/program commands deferred to Phase 4.
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
#include "os_interface/os_interface.h"
|
#include "os_interface/os_interface.h"
|
||||||
#include "fpga/fpga.h"
|
#include "fpga/fpga.h"
|
||||||
#include "bscan_spi/bscan_spi.h"
|
#include "bscan_spi/bscan_spi.h"
|
||||||
|
#include "spi_flash/spi_flash.h"
|
||||||
|
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
|
|
||||||
@@ -2997,6 +2998,140 @@ static int cmd_bscan_jedec(script_ctx *ctx, char *line)
|
|||||||
return JTAG_CORE_NO_ERROR;
|
return JTAG_CORE_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Adapt the BSCAN proxy to the spi_flash transport callback. */
|
||||||
|
struct flash_proxy_ctx {
|
||||||
|
jtag_core *jc;
|
||||||
|
const fpga_target *t;
|
||||||
|
};
|
||||||
|
static int flash_proxy_xfer(void *c, const uint8_t *tx, size_t txlen,
|
||||||
|
uint8_t *rx, size_t rxlen)
|
||||||
|
{
|
||||||
|
struct flash_proxy_ctx *p = (struct flash_proxy_ctx *)c;
|
||||||
|
return bscan_spi_xfer(p->jc, p->t, tx, txlen, rx, rxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look up the FPGA target for a chain device and wire a spi_flash over
|
||||||
|
* the proxy. Returns 0 and fills *sf/*pc, or prints an error and returns
|
||||||
|
* a JTAG_CORE_* code. */
|
||||||
|
static int flash_setup(script_ctx *ctx, int device,
|
||||||
|
spi_flash *sf, struct flash_proxy_ctx *pc)
|
||||||
|
{
|
||||||
|
jtag_core *jc = (jtag_core *)ctx->app_ctx;
|
||||||
|
unsigned long idcode;
|
||||||
|
const fpga_target *t;
|
||||||
|
|
||||||
|
if (jtagcore_get_number_of_devices(jc) <= 0) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "No device on the chain. Run jtag_autoinit first.\n");
|
||||||
|
return JTAG_CORE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
idcode = jtagcore_get_dev_id(jc, device);
|
||||||
|
t = fpga_lookup_by_idcode(idcode);
|
||||||
|
if (!t) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "Device %d IDCODE 0x%.8lX not in FPGA registry\n", device, idcode);
|
||||||
|
return JTAG_CORE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
pc->jc = jc;
|
||||||
|
pc->t = t;
|
||||||
|
memset(sf, 0, sizeof(*sf));
|
||||||
|
sf->xfer = flash_proxy_xfer;
|
||||||
|
sf->ctx = pc;
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cmd_flash_detect_help[] = {
|
||||||
|
"<device>",
|
||||||
|
"Identify the SPI flash behind the loaded BSCAN proxy: read its JEDEC",
|
||||||
|
"ID and match the built-in chip database. Requires a proxy loaded.",
|
||||||
|
""};
|
||||||
|
static int cmd_flash_detect(script_ctx *ctx, char *line)
|
||||||
|
{
|
||||||
|
char dev_txt[DEFAULT_BUFLEN];
|
||||||
|
struct flash_proxy_ctx pc;
|
||||||
|
spi_flash sf;
|
||||||
|
int device, r;
|
||||||
|
|
||||||
|
if (get_param(ctx, line, 1, dev_txt) < 0) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "Usage: flash_detect <device>\n");
|
||||||
|
return JTAG_CORE_BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
device = (int)strtoul(dev_txt, NULL, 0);
|
||||||
|
if ((r = flash_setup(ctx, device, &sf, &pc)) != JTAG_CORE_NO_ERROR) return r;
|
||||||
|
|
||||||
|
r = spi_flash_detect(&sf);
|
||||||
|
if (r < 0) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "spi_flash_detect failed (proxy not loaded?)\n");
|
||||||
|
return JTAG_CORE_IO_ERROR;
|
||||||
|
}
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0, "JEDEC ID: %.2X %.2X %.2X\n",
|
||||||
|
sf.jedec[0], sf.jedec[1], sf.jedec[2]);
|
||||||
|
ctx->last_data_value = (sf.jedec[0] << 16) | (sf.jedec[1] << 8) | sf.jedec[2];
|
||||||
|
if (!sf.chip) {
|
||||||
|
ctx->script_printf(ctx, MSG_WARNING, "Flash not in database — add it to spi_flash.c\n");
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0,
|
||||||
|
"Flash: %s - %u MB, page %u B, sector %u B, %d-byte address\n",
|
||||||
|
sf.chip->name, sf.chip->size_bytes / (1024u * 1024u),
|
||||||
|
sf.chip->page_size, sf.chip->sector_size, sf.chip->addr_bytes);
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cmd_flash_read_help[] = {
|
||||||
|
"<device> <addr> <len>",
|
||||||
|
"Read <len> bytes from the SPI flash at <addr> (hex) and hex-dump them.",
|
||||||
|
"Read-only. Requires a proxy loaded and a flash recognised by flash_detect.",
|
||||||
|
""};
|
||||||
|
static int cmd_flash_read(script_ctx *ctx, char *line)
|
||||||
|
{
|
||||||
|
char dev_txt[DEFAULT_BUFLEN], addr_txt[DEFAULT_BUFLEN], len_txt[DEFAULT_BUFLEN];
|
||||||
|
struct flash_proxy_ctx pc;
|
||||||
|
spi_flash sf;
|
||||||
|
uint8_t *buf;
|
||||||
|
uint32_t addr;
|
||||||
|
size_t len, i;
|
||||||
|
int device, r;
|
||||||
|
|
||||||
|
if (get_param(ctx, line, 1, dev_txt) < 0 || get_param(ctx, line, 2, addr_txt) < 0 ||
|
||||||
|
get_param(ctx, line, 3, len_txt) < 0) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "Usage: flash_read <device> <addr> <len>\n");
|
||||||
|
return JTAG_CORE_BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
device = (int)strtoul(dev_txt, NULL, 0);
|
||||||
|
addr = (uint32_t)strtoul(addr_txt, NULL, 0);
|
||||||
|
len = (size_t)strtoul(len_txt, NULL, 0);
|
||||||
|
if (len == 0) return JTAG_CORE_NO_ERROR;
|
||||||
|
if (len > 65536) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "flash_read: len capped at 65536 for an interactive dump\n");
|
||||||
|
return JTAG_CORE_BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
if ((r = flash_setup(ctx, device, &sf, &pc)) != JTAG_CORE_NO_ERROR) return r;
|
||||||
|
|
||||||
|
if (spi_flash_detect(&sf) < 0 || !sf.chip) {
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "Flash not detected/known — run flash_detect first.\n");
|
||||||
|
return JTAG_CORE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(len);
|
||||||
|
if (!buf) return JTAG_CORE_BAD_PARAMETER;
|
||||||
|
if (spi_flash_read(&sf, addr, buf, len) < 0) {
|
||||||
|
free(buf);
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR, "spi_flash_read failed\n");
|
||||||
|
return JTAG_CORE_IO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i += 16) {
|
||||||
|
char linebuf[16 * 3 + 16];
|
||||||
|
size_t j, pos = 0;
|
||||||
|
pos += (size_t)snprintf(linebuf + pos, sizeof(linebuf) - pos, "%.8X: ", (unsigned)(addr + i));
|
||||||
|
for (j = 0; j < 16 && (i + j) < len; j++) {
|
||||||
|
pos += (size_t)snprintf(linebuf + pos, sizeof(linebuf) - pos, "%.2X ", buf[i + j]);
|
||||||
|
}
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0, "%s\n", linebuf);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
cmd_list script_commands_list[] =
|
cmd_list script_commands_list[] =
|
||||||
{
|
{
|
||||||
{"print", cmd_print, cmd_print_help},
|
{"print", cmd_print, cmd_print_help},
|
||||||
@@ -3045,6 +3180,8 @@ cmd_list script_commands_list[] =
|
|||||||
{"bscan_shift_dr", cmd_bscan_shift_dr, cmd_bscan_shift_dr_help},
|
{"bscan_shift_dr", cmd_bscan_shift_dr, cmd_bscan_shift_dr_help},
|
||||||
{"bscan_load_bitstream", cmd_bscan_load_bitstream, cmd_bscan_load_bitstream_help},
|
{"bscan_load_bitstream", cmd_bscan_load_bitstream, cmd_bscan_load_bitstream_help},
|
||||||
{"bscan_jedec", cmd_bscan_jedec, cmd_bscan_jedec_help},
|
{"bscan_jedec", cmd_bscan_jedec, cmd_bscan_jedec_help},
|
||||||
|
{"flash_detect", cmd_flash_detect, cmd_flash_detect_help},
|
||||||
|
{"flash_read", cmd_flash_read, cmd_flash_read_help},
|
||||||
{0, 0}};
|
{0, 0}};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user