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 <noreply@anthropic.com>
This commit is contained in:
10
CLAUDE.md
10
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
|
apply, then re-scans. Within-cap / unset just report the cap. Effective
|
||||||
`tck = min(request, device max)`; probe-max (`min(..., probe max)`) is
|
`tck = min(request, device max)`; probe-max (`min(..., probe max)`) is
|
||||||
still TODO.
|
still TODO.
|
||||||
- **C** — generalise the other link settings (reset/RTCK) and wire the
|
- **C (done)** — `prog` method tag (`proxy_spi`/`svf`/`none`) on each
|
||||||
`prog` method tag into backend dispatch (ties into the SVF player).
|
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
|
What exists already: the **probe layer** (`probes.yaml`) and the
|
||||||
**device layer** (`fpga_registry.yaml`). The new work is the **JTAG-link
|
**device layer** (`fpga_registry.yaml`). The new work is the **JTAG-link
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
# max_tck_khz max safe JTAG TCK in kHz for this part/board; if the
|
# max_tck_khz max safe JTAG TCK in kHz for this part/board; if the
|
||||||
# requested clock exceeds it, jtag_autoinit clamps and
|
# requested clock exceeds it, jtag_autoinit clamps and
|
||||||
# re-opens at the cap (omit / 0 = unspecified)
|
# 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:
|
fpgas:
|
||||||
# Xilinx Kintex UltraScale+ XCKU15P
|
# Xilinx Kintex UltraScale+ XCKU15P
|
||||||
@@ -39,6 +42,7 @@ fpgas:
|
|||||||
ir_jshutdown: 0x0D
|
ir_jshutdown: 0x0D
|
||||||
ir_isc_disable: 0x16
|
ir_isc_disable: 0x16
|
||||||
caveats: cclk_via_startup
|
caveats: cclk_via_startup
|
||||||
|
prog: proxy_spi
|
||||||
# proxy_bitstream not yet built for this part (see doc/tutorial.md, Phase 2.5)
|
# proxy_bitstream not yet built for this part (see doc/tutorial.md, Phase 2.5)
|
||||||
|
|
||||||
# Xilinx Kintex UltraScale XCKU040 (KCU105 eval board)
|
# Xilinx Kintex UltraScale XCKU040 (KCU105 eval board)
|
||||||
@@ -57,6 +61,7 @@ fpgas:
|
|||||||
ir_isc_disable: 0x16
|
ir_isc_disable: 0x16
|
||||||
proxy_bitstream: bscan_spi_xcku040.bit
|
proxy_bitstream: bscan_spi_xcku040.bit
|
||||||
caveats: cclk_via_startup
|
caveats: cclk_via_startup
|
||||||
|
prog: proxy_spi
|
||||||
|
|
||||||
# Microsemi IGLOO2 M2GL010T (M2GL-EVAL-KIT)
|
# Microsemi IGLOO2 M2GL010T (M2GL-EVAL-KIT)
|
||||||
# IDCODE / IR length from bsdl_files/m2gl010t-fg484.bsd
|
# IDCODE / IR length from bsdl_files/m2gl010t-fg484.bsd
|
||||||
@@ -72,3 +77,4 @@ fpgas:
|
|||||||
family: microsemi_igloo2
|
family: microsemi_igloo2
|
||||||
bsdl: m2gl010t-fg484.bsd
|
bsdl: m2gl010t-fg484.bsd
|
||||||
ir_length: 8
|
ir_length: 8
|
||||||
|
prog: svf
|
||||||
|
|||||||
@@ -53,6 +53,29 @@ static fpga_family parse_family(const char *s)
|
|||||||
return FPGA_FAMILY_UNKNOWN;
|
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)
|
static unsigned int parse_caveats(const char *s)
|
||||||
{
|
{
|
||||||
unsigned int flags = 0;
|
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, "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, "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, "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);
|
else fprintf(stderr, "fpga: unknown key '%s'\n", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +126,10 @@ static int commit_entry(const fpga_target *cur)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
g_registry = tmp;
|
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;
|
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)
|
const char *fpga_registry_source(void)
|
||||||
{
|
{
|
||||||
ensure_loaded();
|
ensure_loaded();
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ typedef enum {
|
|||||||
FPGA_FAMILY_LATTICE_MACHXO3,
|
FPGA_FAMILY_LATTICE_MACHXO3,
|
||||||
} fpga_family;
|
} 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. */
|
/* Caveat flags: known hardware gotchas for a part. */
|
||||||
#define FPGA_CAVEAT_CCLK_VIA_STARTUP (1u << 0) /* CCLK not directly drivable in EXTEST */
|
#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 */
|
const char *proxy_bitstream; /* path under bscan_proxies/, NULL if not yet available */
|
||||||
unsigned int caveats; /* FPGA_CAVEAT_* flags */
|
unsigned int caveats; /* FPGA_CAVEAT_* flags */
|
||||||
int max_tck_khz; /* max safe JTAG TCK for this part/board, 0 = unspecified */
|
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;
|
} fpga_target;
|
||||||
|
|
||||||
/* Registry access. The YAML file is loaded lazily on first call to any
|
/* 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_get_target_by_index(int index);
|
||||||
const fpga_target * fpga_lookup_by_idcode(unsigned long idcode);
|
const fpga_target * fpga_lookup_by_idcode(unsigned long idcode);
|
||||||
const char * fpga_family_name(fpga_family f);
|
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
|
/* Path the registry was loaded from, or NULL if nothing loaded
|
||||||
* (file missing / parse error). For diagnostics. */
|
* (file missing / parse error). For diagnostics. */
|
||||||
|
|||||||
@@ -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);
|
ret = jtagcore_select_and_open_probe(jc, id);
|
||||||
if (ret != JTAG_CORE_NO_ERROR)
|
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,
|
i, t->idcode, t->idcode_mask,
|
||||||
t->name, fpga_family_name(t->family));
|
t->name, fpga_family_name(t->family));
|
||||||
ctx->script_printf(ctx, MSG_NONE,
|
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->bsdl_filename, t->ir_length,
|
||||||
t->proxy_bitstream ? t->proxy_bitstream : "(none yet)",
|
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;
|
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,
|
ctx->script_printf(ctx, MSG_INFO_0,
|
||||||
"Device %d IDCODE 0x%.8lX -> %s [%s]\n",
|
"Device %d IDCODE 0x%.8lX -> %s [%s]\n",
|
||||||
i, idcode, t->name, fpga_family_name(t->family));
|
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) {
|
if (t->caveats & FPGA_CAVEAT_CCLK_VIA_STARTUP) {
|
||||||
ctx->script_printf(ctx, MSG_NONE,
|
ctx->script_printf(ctx, MSG_NONE,
|
||||||
" caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST)\n");
|
" caveat: CCLK routed via STARTUP primitive (not drivable in EXTEST)\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user