From 545fe09fd52116acf6a807bc8f4213c4d5599508 Mon Sep 17 00:00:00 2001 From: francois Date: Wed, 20 May 2026 22:52:52 +0200 Subject: [PATCH] =?UTF-8?q?phase=202:=20add=20fpga/=20module=20=E2=80=94?= =?UTF-8?q?=20per-target=20descriptor=20&=20registry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- modules/fpga/CMakeLists.txt | 7 ++++ modules/fpga/fpga.c | 64 +++++++++++++++++++++++++++++++++ modules/fpga/fpga.h | 57 +++++++++++++++++++++++++++++ modules/script/script.c | 72 +++++++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 modules/fpga/CMakeLists.txt create mode 100644 modules/fpga/fpga.c create mode 100644 modules/fpga/fpga.h diff --git a/modules/fpga/CMakeLists.txt b/modules/fpga/CMakeLists.txt new file mode 100644 index 0000000..6264b85 --- /dev/null +++ b/modules/fpga/CMakeLists.txt @@ -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}) diff --git a/modules/fpga/fpga.c b/modules/fpga/fpga.c new file mode 100644 index 0000000..f5087ce --- /dev/null +++ b/modules/fpga/fpga.c @@ -0,0 +1,64 @@ +#include + +#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"; + } +} diff --git a/modules/fpga/fpga.h b/modules/fpga/fpga.h new file mode 100644 index 0000000..0cd89ca --- /dev/null +++ b/modules/fpga/fpga.h @@ -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 diff --git a/modules/script/script.c b/modules/script/script.c index 252cec1..a5f5aa9 100644 --- a/modules/script/script.c +++ b/modules/script/script.c @@ -37,6 +37,7 @@ #include "bsdl_parser/bsdl_loader.h" #include "os_interface/os_interface.h" +#include "fpga/fpga.h" #include "env.h" @@ -2723,6 +2724,75 @@ static int cmd_get_pins_list(script_ctx *ctx, char *line) 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[] = { {"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_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}, + {"fpga_list", cmd_fpga_list, cmd_fpga_list_help}, + {"fpga_info", cmd_fpga_info, cmd_fpga_info_help}, {0, 0}}; ///////////////////////////////////////////////////////////////////////////////