phase 2: add fpga/ module — per-target descriptor & registry
modules/fpga/ holds an fpga_target struct (IDCODE/mask, family, IR length and private opcodes, proxy bitstream path, quirks) and a compile-time registry. Initial entry: Xilinx Kintex UltraScale+ XCKU15P, populated from bsdl_files/xcku15p_ffve1517.bsd (IDCODE 0x04A56093, IR 6, USER1=0x02, CFG_IN=0x05, JPROGRAM=0x0B, JSTART=0x0C, JSHUTDOWN=0x0D, ISC_DISABLE=0x16, quirk CCLK_VIA_STARTUP). Two new script commands: - fpga_list: enumerate the registry - fpga_info: match each device on the JTAG chain against the registry and surface known quirks Adding another FPGA = one entry in fpga_registry[] + its .bsd in bsdl_files/. Proxy .bit will be wired in phase 2.5 (bscan_spi/). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
7
modules/fpga/CMakeLists.txt
Normal file
7
modules/fpga/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE ALL_SOURCES "*.c")
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||||
|
|
||||||
|
add_library(fpga ${ALL_SOURCES})
|
||||||
64
modules/fpga/fpga.c
Normal file
64
modules/fpga/fpga.c
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "fpga.h"
|
||||||
|
|
||||||
|
static const fpga_target fpga_registry[] = {
|
||||||
|
/* Xilinx Kintex UltraScale+ XCKU15P
|
||||||
|
* IDCODE_REGISTER and INSTRUCTION_OPCODE values come from
|
||||||
|
* bsdl_files/xcku15p_ffve1517.bsd
|
||||||
|
* IR length 6 bits, version nibble (bits 31:28) ignored. */
|
||||||
|
{
|
||||||
|
.name = "Xilinx Kintex UltraScale+ XCKU15P",
|
||||||
|
.idcode = 0x04A56093,
|
||||||
|
.idcode_mask = 0x0FFFFFFF,
|
||||||
|
.family = FPGA_FAMILY_XILINX_USP,
|
||||||
|
.bsdl_filename = "xcku15p_ffve1517.bsd",
|
||||||
|
.ir_length = 6,
|
||||||
|
.ir_cfg_in = 0x05,
|
||||||
|
.ir_user1 = 0x02,
|
||||||
|
.ir_jprogram = 0x0B,
|
||||||
|
.ir_jstart = 0x0C,
|
||||||
|
.ir_jshutdown = 0x0D,
|
||||||
|
.ir_isc_disable = 0x16,
|
||||||
|
.proxy_bitstream = NULL, /* TODO Phase 2.5: bscan_spi_xcku15p.bit */
|
||||||
|
.quirks = FPGA_QUIRK_CCLK_VIA_STARTUP,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FPGA_REGISTRY_LEN ((int)(sizeof(fpga_registry) / sizeof(fpga_registry[0])))
|
||||||
|
|
||||||
|
int fpga_get_target_count(void)
|
||||||
|
{
|
||||||
|
return FPGA_REGISTRY_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fpga_target *fpga_get_target_by_index(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= FPGA_REGISTRY_LEN) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &fpga_registry[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fpga_target *fpga_lookup_by_idcode(unsigned long idcode)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < FPGA_REGISTRY_LEN; i++) {
|
||||||
|
const fpga_target *t = &fpga_registry[i];
|
||||||
|
if ((idcode & t->idcode_mask) == (t->idcode & t->idcode_mask)) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *fpga_family_name(fpga_family f)
|
||||||
|
{
|
||||||
|
switch (f) {
|
||||||
|
case FPGA_FAMILY_XILINX_7: return "Xilinx 7-Series";
|
||||||
|
case FPGA_FAMILY_XILINX_US: return "Xilinx UltraScale";
|
||||||
|
case FPGA_FAMILY_XILINX_USP: return "Xilinx UltraScale+";
|
||||||
|
case FPGA_FAMILY_UNKNOWN:
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
57
modules/fpga/fpga.h
Normal file
57
modules/fpga/fpga.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef _FPGA_H
|
||||||
|
#define _FPGA_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-target FPGA descriptor and registry.
|
||||||
|
*
|
||||||
|
* Holds the facts that cannot be derived from the BSDL alone:
|
||||||
|
* - IDCODE pattern to match on the chain
|
||||||
|
* - private IR opcodes (USER1, CFG_IN, JPROGRAM, …) needed for
|
||||||
|
* configuration and for the BSCAN proxy bridge (Phase 2.5)
|
||||||
|
* - path to the BSCAN proxy bitstream
|
||||||
|
* - per-target quirks
|
||||||
|
*
|
||||||
|
* Adding an FPGA = one entry in fpga_registry[] + its .bsd in
|
||||||
|
* bsdl_files/ + (optionally) its proxy .bit in bscan_proxies/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jtag_core/jtag_core.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FPGA_FAMILY_UNKNOWN = 0,
|
||||||
|
FPGA_FAMILY_XILINX_7,
|
||||||
|
FPGA_FAMILY_XILINX_US,
|
||||||
|
FPGA_FAMILY_XILINX_USP,
|
||||||
|
} fpga_family;
|
||||||
|
|
||||||
|
/* Quirk flags */
|
||||||
|
#define FPGA_QUIRK_CCLK_VIA_STARTUP (1u << 0) /* CCLK not directly drivable in EXTEST */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name; /* human-readable part name */
|
||||||
|
unsigned long idcode; /* IDCODE pattern */
|
||||||
|
unsigned long idcode_mask; /* bits to ignore (typically 0x0FFFFFFF for Xilinx — version masked) */
|
||||||
|
fpga_family family;
|
||||||
|
const char *bsdl_filename; /* basename within bsdl_files/ */
|
||||||
|
int ir_length; /* IR width in bits */
|
||||||
|
|
||||||
|
/* Private IR opcodes (0 = N/A for this family).
|
||||||
|
* For Xilinx, these are read from the BSDL INSTRUCTION_OPCODE block. */
|
||||||
|
unsigned int ir_cfg_in;
|
||||||
|
unsigned int ir_user1;
|
||||||
|
unsigned int ir_jprogram;
|
||||||
|
unsigned int ir_jstart;
|
||||||
|
unsigned int ir_jshutdown;
|
||||||
|
unsigned int ir_isc_disable;
|
||||||
|
|
||||||
|
const char *proxy_bitstream; /* path under bscan_proxies/, NULL if not yet available */
|
||||||
|
unsigned int quirks;
|
||||||
|
} fpga_target;
|
||||||
|
|
||||||
|
/* Registry access */
|
||||||
|
int fpga_get_target_count(void);
|
||||||
|
const fpga_target * fpga_get_target_by_index(int index);
|
||||||
|
const fpga_target * fpga_lookup_by_idcode(unsigned long idcode);
|
||||||
|
const char * fpga_family_name(fpga_family f);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "bsdl_parser/bsdl_loader.h"
|
#include "bsdl_parser/bsdl_loader.h"
|
||||||
#include "os_interface/os_interface.h"
|
#include "os_interface/os_interface.h"
|
||||||
|
#include "fpga/fpga.h"
|
||||||
|
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
|
|
||||||
@@ -2723,6 +2724,75 @@ static int cmd_get_pins_list(script_ctx *ctx, char *line)
|
|||||||
return JTAG_CORE_BAD_PARAMETER;
|
return JTAG_CORE_BAD_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *cmd_fpga_list_help[] = {
|
||||||
|
"",
|
||||||
|
"Lists the FPGA targets known to this build (registry in modules/fpga/).",
|
||||||
|
""};
|
||||||
|
static int cmd_fpga_list(script_ctx *ctx, char *line)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
const fpga_target *t;
|
||||||
|
|
||||||
|
(void)line;
|
||||||
|
|
||||||
|
n = fpga_get_target_count();
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0, "%d FPGA target(s) registered:\n", n);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
t = fpga_get_target_by_index(i);
|
||||||
|
ctx->script_printf(ctx, MSG_NONE,
|
||||||
|
" [%d] IDCODE %.8lX/%.8lX %s (%s)\n",
|
||||||
|
i, t->idcode, t->idcode_mask,
|
||||||
|
t->name, fpga_family_name(t->family));
|
||||||
|
ctx->script_printf(ctx, MSG_NONE,
|
||||||
|
" bsdl=%s ir=%d proxy=%s quirks=0x%x\n",
|
||||||
|
t->bsdl_filename, t->ir_length,
|
||||||
|
t->proxy_bitstream ? t->proxy_bitstream : "(none yet)",
|
||||||
|
t->quirks);
|
||||||
|
}
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cmd_fpga_info_help[] = {
|
||||||
|
"",
|
||||||
|
"Reports, for each device on the JTAG chain, whether its IDCODE",
|
||||||
|
"matches a known FPGA target. Requires jtag_init_scan or jtag_autoinit first.",
|
||||||
|
""};
|
||||||
|
static int cmd_fpga_info(script_ctx *ctx, char *line)
|
||||||
|
{
|
||||||
|
jtag_core *jc;
|
||||||
|
int i, n;
|
||||||
|
unsigned long idcode;
|
||||||
|
const fpga_target *t;
|
||||||
|
|
||||||
|
(void)line;
|
||||||
|
jc = (jtag_core *)ctx->app_ctx;
|
||||||
|
|
||||||
|
n = jtagcore_get_number_of_devices(jc);
|
||||||
|
if (n <= 0) {
|
||||||
|
ctx->script_printf(ctx, MSG_WARNING, "No device on the chain. Run jtag_autoinit first.\n");
|
||||||
|
return JTAG_CORE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
idcode = jtagcore_get_dev_id(jc, i);
|
||||||
|
t = fpga_lookup_by_idcode(idcode);
|
||||||
|
if (t) {
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0,
|
||||||
|
"Device %d IDCODE 0x%.8lX -> %s [%s]\n",
|
||||||
|
i, idcode, t->name, fpga_family_name(t->family));
|
||||||
|
if (t->quirks & FPGA_QUIRK_CCLK_VIA_STARTUP) {
|
||||||
|
ctx->script_printf(ctx, MSG_NONE,
|
||||||
|
" quirk: CCLK routed via STARTUP primitive (not drivable in EXTEST)\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0,
|
||||||
|
"Device %d IDCODE 0x%.8lX -> not in registry\n",
|
||||||
|
i, idcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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},
|
||||||
@@ -2765,6 +2835,8 @@ cmd_list script_commands_list[] =
|
|||||||
{"jtag_set_spi_miso_pin", cmd_set_spi_miso_pin, cmd_set_spi_miso_pin_help},
|
{"jtag_set_spi_miso_pin", cmd_set_spi_miso_pin, cmd_set_spi_miso_pin_help},
|
||||||
{"jtag_set_spi_clk_pin", cmd_set_spi_clk_pin, cmd_set_spi_clk_pin_help},
|
{"jtag_set_spi_clk_pin", cmd_set_spi_clk_pin, cmd_set_spi_clk_pin_help},
|
||||||
{"jtag_spi_rd_wr", cmd_spi_rd_wr, cmd_spi_rd_wr_help},
|
{"jtag_spi_rd_wr", cmd_spi_rd_wr, cmd_spi_rd_wr_help},
|
||||||
|
{"fpga_list", cmd_fpga_list, cmd_fpga_list_help},
|
||||||
|
{"fpga_info", cmd_fpga_info, cmd_fpga_info_help},
|
||||||
{0, 0}};
|
{0, 0}};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user