diff --git a/CLAUDE.md b/CLAUDE.md index 42d52b8..bfd3b83 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -188,7 +188,12 @@ fact bounded by both the probe and the board/device. read directly by our Digilent driver; unset → each driver's own default (FTDI 1000, Digilent 4000). Set it via `set`, a `probes.yaml` profile, or `defaults:`. (FTDI path hardware-validated; Digilent path untested.) -- **B** — device `max_tck_khz` + resolution after `jtag_autoinit`. +- **B (done)** — registry `max_tck_khz` caps the clock: `jtag_autoinit`, + after identifying the chain, clamps `JTAG_TCK_FREQ_KHZ` to the smallest + device max and re-opens the probe once (via the stored probe id) to + 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). diff --git a/fpga_registry.yaml b/fpga_registry.yaml index 456da89..0d18f7b 100644 --- a/fpga_registry.yaml +++ b/fpga_registry.yaml @@ -19,6 +19,9 @@ # (omit if none is available yet) # caveats space/comma-separated flags: cclk_via_startup # (omit if none) +# 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) fpgas: # Xilinx Kintex UltraScale+ XCKU15P diff --git a/modules/fpga/fpga.c b/modules/fpga/fpga.c index 130fc0f..017f519 100644 --- a/modules/fpga/fpga.c +++ b/modules/fpga/fpga.c @@ -89,6 +89,7 @@ static void set_field(fpga_target *t, const char *key, const char *val) else if (!strcmp(key, "ir_isc_disable")) t->ir_isc_disable = (unsigned int)strtoul(val, NULL, 0); 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 fprintf(stderr, "fpga: unknown key '%s'\n", key); } diff --git a/modules/fpga/fpga.h b/modules/fpga/fpga.h index f3d52f0..e68cc0c 100644 --- a/modules/fpga/fpga.h +++ b/modules/fpga/fpga.h @@ -52,6 +52,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_target; /* Registry access. The YAML file is loaded lazily on first call to any diff --git a/modules/script/script.c b/modules/script/script.c index 6b88854..5dccbff 100644 --- a/modules/script/script.c +++ b/modules/script/script.c @@ -1474,7 +1474,13 @@ const char *cmd_autoinit_help[] = { "explanations line two", "" }; -static int cmd_autoinit(script_ctx *ctx, char *line) +/* Id of the currently open probe (drv<<8|probe), -1 if none. Lets + * jtag_autoinit re-open the same probe to apply a clock re-tune. */ +static int g_probe_id = -1; + +/* Scan the chain and auto-load matching BSDLs. Returns the number of + * BSDLs loaded (>= 0), or a negative JTAG_CORE_* error. */ +static int autoinit_run(script_ctx *ctx) { jtag_core *jc; int number_of_devices, dev_nb; @@ -1588,9 +1594,86 @@ static int cmd_autoinit(script_ctx *ctx, char *line) return JTAG_CORE_ACCESS_ERROR; } + return loaded_bsdl; +} + +/* Smallest device-declared max TCK (kHz) over the matched chain devices, + * or 0 if none of them declare one. */ +static int chain_max_tck_khz(jtag_core *jc) +{ + int i, n, cap = 0; + const fpga_target *t; + + n = jtagcore_get_number_of_devices(jc); + for (i = 0; i < n; i++) { + t = fpga_lookup_by_idcode(jtagcore_get_dev_id(jc, i)); + if (t && t->max_tck_khz > 0 && (cap == 0 || t->max_tck_khz < cap)) + cap = t->max_tck_khz; + } + return cap; +} + +static int cmd_autoinit(script_ctx *ctx, char *line) +{ + jtag_core *jc; + int loaded, cap, req; + + (void)line; + jc = (jtag_core *)ctx->app_ctx; + + loaded = autoinit_run(ctx); + if (loaded < 0) + return loaded; + + // Phase B: resolve the JTAG clock against the devices now identified. + // The registry can cap the TCK for a part/board; if the requested + // clock exceeds that, clamp it and re-open the probe so it takes + // effect (the chain is then re-scanned at the safe clock). + cap = chain_max_tck_khz(jc); + req = jtagcore_getEnvVarValue(jc, "JTAG_TCK_FREQ_KHZ"); + if (cap > 0) + { + if (req > 0 && req > cap) + { + char buf[24]; + snprintf(buf, sizeof(buf), "%d", cap); + ctx->script_printf(ctx, MSG_WARNING, + "JTAG clock %d kHz exceeds the device max %d kHz; clamping.\n", req, cap); + setEnvVarDat((envvar_entry *)ctx->env, "JTAG_TCK_FREQ_KHZ", buf); + setEnvVarDat((envvar_entry *)ctx->env, "PROBE_FTDI_TCK_FREQ_KHZ", buf); + if (g_probe_id >= 0) + { + ctx->script_printf(ctx, MSG_INFO_0, + "Re-opening at %d kHz and re-scanning.\n", cap); + if (jtagcore_select_and_open_probe(jc, g_probe_id) == JTAG_CORE_NO_ERROR) + { + loaded = autoinit_run(ctx); + if (loaded < 0) + return loaded; + } + else + { + ctx->script_printf(ctx, MSG_ERROR, + "Re-open failed; the clock will apply on the next jtag_open.\n"); + } + } + else + { + ctx->script_printf(ctx, MSG_WARNING, + "Reopen the probe (jtag_close / jtag_open) to apply the clamped clock.\n"); + } + } + else + { + ctx->script_printf(ctx, MSG_INFO_0, + "Device max JTAG clock: %d kHz%s.\n", cap, + (req > 0) ? "" : " (set JTAG_TCK_FREQ_KHZ to control the clock)"); + } + } + // Expose the count to scripts via last_data_value, but report success // (a non-zero return is treated as an error code by the engine). - ctx->last_data_value = loaded_bsdl; + ctx->last_data_value = loaded; return JTAG_CORE_NO_ERROR; } @@ -1885,6 +1968,7 @@ static int cmd_open_probe(script_ctx *ctx, char *line) } else { + g_probe_id = id; ctx->script_printf(ctx, MSG_INFO_0, "Probe Ok !\n"); return JTAG_CORE_NO_ERROR; } @@ -1921,6 +2005,7 @@ static int cmd_close_probe(script_ctx *ctx, char *line) // chain looks probe-less until the next jtag_open. jc->io_functions.drv_DeInit(jc); memset(&jc->io_functions, 0, sizeof(jc->io_functions)); + g_probe_id = -1; ctx->script_printf(ctx, MSG_INFO_0, "Probe released.\n"); } else @@ -2909,10 +2994,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\n", + " bsdl=%s ir=%d proxy=%s caveats=0x%x maxtck=%dkHz\n", t->bsdl_filename, t->ir_length, t->proxy_bitstream ? t->proxy_bitstream : "(none yet)", - t->caveats); + t->caveats, t->max_tck_khz); } return JTAG_CORE_NO_ERROR; }