From 2a03cb1145e30e644f7e3d644b39acfd2785c947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 24 May 2026 11:53:02 +0200 Subject: [PATCH] jtag/fpga: prog method tag + RTCK link setting (phase C) - fpga_target gains a prog method (proxy_spi/svf/none), set in the registry or inferred when omitted (proxy_bitstream -> proxy_spi; Microsemi/Lattice -> svf); shown by fpga_info/fpga_list and exposed via fpga_prog_method_name() for the future program dispatch - generalise RTCK as a neutral JTAG_RTCK, mirrored to PROBE_FTDI_JTAG_ENABLE_RTCK at open (FTDI-only) - reset abstraction deferred (no clean neutral form yet); the program dispatch command itself lands with the SVF player Co-Authored-By: Claude Opus 4.7 --- CLAUDE.md | 10 ++++++++-- fpga_registry.yaml | 6 ++++++ modules/fpga/fpga.c | 39 ++++++++++++++++++++++++++++++++++++++- modules/fpga/fpga.h | 9 +++++++++ modules/script/script.c | 15 +++++++++++++-- 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bfd3b83..5f4598c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -194,8 +194,14 @@ fact bounded by both the probe and the board/device. apply, then re-scans. Within-cap / unset just report the cap. Effective `tck = min(request, device max)`; probe-max (`min(..., probe max)`) is still TODO. -- **C** — generalise the other link settings (reset/RTCK) and wire the - `prog` method tag into backend dispatch (ties into the SVF player). +- **C (done)** — `prog` method tag (`proxy_spi`/`svf`/`none`) on each + registry entry, inferred when omitted (proxy → `proxy_spi`; Microsemi/ + Lattice → `svf`); shown by `fpga_info`/`fpga_list` and available via + `fpga_prog_method_name()` for backend dispatch. RTCK generalised as a + neutral `JTAG_RTCK` (mirrored to `PROBE_FTDI_JTAG_ENABLE_RTCK`, + FTDI-only). Reset abstraction deferred — it's a bundle of probe- + specific pin/polarity/timing vars with no clean neutral form yet. The + actual `program` dispatch command lands with the SVF player. What exists already: the **probe layer** (`probes.yaml`) and the **device layer** (`fpga_registry.yaml`). The new work is the **JTAG-link diff --git a/fpga_registry.yaml b/fpga_registry.yaml index 0d18f7b..cd81e9b 100644 --- a/fpga_registry.yaml +++ b/fpga_registry.yaml @@ -22,6 +22,9 @@ # max_tck_khz max safe JTAG TCK in kHz for this part/board; if the # requested clock exceeds it, jtag_autoinit clamps and # re-opens at the cap (omit / 0 = unspecified) +# prog programming backend: proxy_spi | svf | none. Omit to +# infer (proxy_bitstream -> proxy_spi; Microsemi/Lattice +# -> svf; else none). fpgas: # Xilinx Kintex UltraScale+ XCKU15P @@ -39,6 +42,7 @@ fpgas: ir_jshutdown: 0x0D ir_isc_disable: 0x16 caveats: cclk_via_startup + prog: proxy_spi # proxy_bitstream not yet built for this part (see doc/tutorial.md, Phase 2.5) # Xilinx Kintex UltraScale XCKU040 (KCU105 eval board) @@ -57,6 +61,7 @@ fpgas: ir_isc_disable: 0x16 proxy_bitstream: bscan_spi_xcku040.bit caveats: cclk_via_startup + prog: proxy_spi # Microsemi IGLOO2 M2GL010T (M2GL-EVAL-KIT) # IDCODE / IR length from bsdl_files/m2gl010t-fg484.bsd @@ -72,3 +77,4 @@ fpgas: family: microsemi_igloo2 bsdl: m2gl010t-fg484.bsd ir_length: 8 + prog: svf diff --git a/modules/fpga/fpga.c b/modules/fpga/fpga.c index 017f519..aa6f26d 100644 --- a/modules/fpga/fpga.c +++ b/modules/fpga/fpga.c @@ -53,6 +53,29 @@ static fpga_family parse_family(const char *s) return FPGA_FAMILY_UNKNOWN; } +static fpga_prog_method parse_prog(const char *s) +{ + if (!s) return FPGA_PROG_NONE; + if (!strcmp(s, "proxy_spi")) return FPGA_PROG_PROXY_SPI; + if (!strcmp(s, "svf")) return FPGA_PROG_SVF; + if (!strcmp(s, "none")) return FPGA_PROG_NONE; + fprintf(stderr, "fpga: unknown prog method '%s'\n", s); + return FPGA_PROG_NONE; +} + +/* Guess the programming method when the entry doesn't state one. */ +static fpga_prog_method infer_prog(const fpga_target *t) +{ + if (t->proxy_bitstream) return FPGA_PROG_PROXY_SPI; + switch (t->family) { + case FPGA_FAMILY_MICROSEMI_IGLOO2: + case FPGA_FAMILY_MICROSEMI_SMARTFUSION2: + case FPGA_FAMILY_LATTICE_MACHXO2: + case FPGA_FAMILY_LATTICE_MACHXO3: return FPGA_PROG_SVF; + default: return FPGA_PROG_NONE; + } +} + static unsigned int parse_caveats(const char *s) { unsigned int flags = 0; @@ -90,6 +113,7 @@ static void set_field(fpga_target *t, const char *key, const char *val) else if (!strcmp(key, "proxy_bitstream")) { free((void *)t->proxy_bitstream); t->proxy_bitstream = xstrdup(val); } else if (!strcmp(key, "caveats")) t->caveats = parse_caveats(val); else if (!strcmp(key, "max_tck_khz")) t->max_tck_khz = (int)strtol(val, NULL, 0); + else if (!strcmp(key, "prog")) t->prog = parse_prog(val); else fprintf(stderr, "fpga: unknown key '%s'\n", key); } @@ -102,7 +126,10 @@ static int commit_entry(const fpga_target *cur) return -1; } g_registry = tmp; - g_registry[g_count++] = *cur; + g_registry[g_count] = *cur; + if (g_registry[g_count].prog == FPGA_PROG_NONE) + g_registry[g_count].prog = infer_prog(&g_registry[g_count]); + g_count++; return 0; } @@ -267,6 +294,16 @@ const char *fpga_family_name(fpga_family f) } } +const char *fpga_prog_method_name(fpga_prog_method m) +{ + switch (m) { + case FPGA_PROG_PROXY_SPI: return "proxy_spi"; + case FPGA_PROG_SVF: return "svf"; + case FPGA_PROG_NONE: + default: return "none"; + } +} + const char *fpga_registry_source(void) { ensure_loaded(); diff --git a/modules/fpga/fpga.h b/modules/fpga/fpga.h index e68cc0c..4dfa316 100644 --- a/modules/fpga/fpga.h +++ b/modules/fpga/fpga.h @@ -30,6 +30,13 @@ typedef enum { FPGA_FAMILY_LATTICE_MACHXO3, } fpga_family; +/* Programming method — which backend drives this part. */ +typedef enum { + FPGA_PROG_NONE = 0, /* no known method */ + FPGA_PROG_PROXY_SPI, /* Xilinx external SPI flash via the BSCAN proxy */ + FPGA_PROG_SVF, /* play a vendor-exported SVF (Lattice/Microsemi/…) */ +} fpga_prog_method; + /* Caveat flags: known hardware gotchas for a part. */ #define FPGA_CAVEAT_CCLK_VIA_STARTUP (1u << 0) /* CCLK not directly drivable in EXTEST */ @@ -53,6 +60,7 @@ typedef struct { const char *proxy_bitstream; /* path under bscan_proxies/, NULL if not yet available */ unsigned int caveats; /* FPGA_CAVEAT_* flags */ int max_tck_khz; /* max safe JTAG TCK for this part/board, 0 = unspecified */ + fpga_prog_method prog; /* programming backend; inferred when omitted */ } fpga_target; /* Registry access. The YAML file is loaded lazily on first call to any @@ -61,6 +69,7 @@ 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); +const char * fpga_prog_method_name(fpga_prog_method m); /* Path the registry was loaded from, or NULL if nothing loaded * (file missing / parse error). For diagnostics. */ diff --git a/modules/script/script.c b/modules/script/script.c index 5dccbff..af8523a 100644 --- a/modules/script/script.c +++ b/modules/script/script.c @@ -1960,6 +1960,15 @@ static int cmd_open_probe(script_ctx *ctx, char *line) } } + // Driver-neutral adaptive clock (JTAG_RTCK, 0/1): mirror to the FTDI + // driver's variable when explicitly set (0 is meaningful, so test + // presence rather than value). FTDI-only; no Digilent equivalent. + { + char rtck[32]; + if (getEnvVarDat((envvar_entry *)ctx->env, "JTAG_RTCK", rtck, sizeof(rtck))) + setEnvVarDat((envvar_entry *)ctx->env, "PROBE_FTDI_JTAG_ENABLE_RTCK", rtck); + } + ret = jtagcore_select_and_open_probe(jc, id); if (ret != JTAG_CORE_NO_ERROR) { @@ -2994,10 +3003,10 @@ static int cmd_fpga_list(script_ctx *ctx, char *line) 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 caveats=0x%x maxtck=%dkHz\n", + " bsdl=%s ir=%d proxy=%s caveats=0x%x maxtck=%dkHz prog=%s\n", t->bsdl_filename, t->ir_length, t->proxy_bitstream ? t->proxy_bitstream : "(none yet)", - t->caveats, t->max_tck_khz); + t->caveats, t->max_tck_khz, fpga_prog_method_name(t->prog)); } return JTAG_CORE_NO_ERROR; } @@ -3030,6 +3039,8 @@ static int cmd_fpga_info(script_ctx *ctx, char *line) ctx->script_printf(ctx, MSG_INFO_0, "Device %d IDCODE 0x%.8lX -> %s [%s]\n", i, idcode, t->name, fpga_family_name(t->family)); + ctx->script_printf(ctx, MSG_NONE, + " prog: %s\n", fpga_prog_method_name(t->prog)); if (t->caveats & FPGA_CAVEAT_CCLK_VIA_STARTUP) { ctx->script_printf(ctx, MSG_NONE, " caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST)\n");