jtag: cap the clock by device max_tck_khz at autoinit (phase B)
- fpga_target gains max_tck_khz (registry key), the max safe JTAG TCK for a part/board (0 = unspecified) - jtag_autoinit, after identifying the chain, resolves the clock: if the requested JTAG_TCK_FREQ_KHZ exceeds the smallest device max, it clamps it and re-opens the probe once (stored probe id) to apply, then re-scans; within-cap / unset just report the cap - autoinit body extracted into autoinit_run() so it can re-run after the re-tune; fpga_list shows maxtck Validated on the IGLOO2/FlashPro (req 500 -> clamp 200 -> reopen -> still detected; within-cap and unset paths don't reopen). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
read directly by our Digilent driver; unset → each driver's own default
|
||||||
(FTDI 1000, Digilent 4000). Set it via `set`, a `probes.yaml` profile,
|
(FTDI 1000, Digilent 4000). Set it via `set`, a `probes.yaml` profile,
|
||||||
or `defaults:`. (FTDI path hardware-validated; Digilent path untested.)
|
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
|
- **C** — generalise the other link settings (reset/RTCK) and wire the
|
||||||
`prog` method tag into backend dispatch (ties into the SVF player).
|
`prog` method tag into backend dispatch (ties into the SVF player).
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
# (omit if none is available yet)
|
# (omit if none is available yet)
|
||||||
# caveats space/comma-separated flags: cclk_via_startup
|
# caveats space/comma-separated flags: cclk_via_startup
|
||||||
# (omit if none)
|
# (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:
|
fpgas:
|
||||||
# Xilinx Kintex UltraScale+ XCKU15P
|
# Xilinx Kintex UltraScale+ XCKU15P
|
||||||
|
|||||||
@@ -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, "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, "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 fprintf(stderr, "fpga: unknown key '%s'\n", key);
|
else fprintf(stderr, "fpga: unknown key '%s'\n", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,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 */
|
||||||
} 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
|
||||||
|
|||||||
@@ -1474,7 +1474,13 @@ const char *cmd_autoinit_help[] = {
|
|||||||
"explanations line two",
|
"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;
|
jtag_core *jc;
|
||||||
int number_of_devices, dev_nb;
|
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 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
|
// 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).
|
// (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;
|
return JTAG_CORE_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1885,6 +1968,7 @@ static int cmd_open_probe(script_ctx *ctx, char *line)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
g_probe_id = id;
|
||||||
ctx->script_printf(ctx, MSG_INFO_0, "Probe Ok !\n");
|
ctx->script_printf(ctx, MSG_INFO_0, "Probe Ok !\n");
|
||||||
return JTAG_CORE_NO_ERROR;
|
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.
|
// chain looks probe-less until the next jtag_open.
|
||||||
jc->io_functions.drv_DeInit(jc);
|
jc->io_functions.drv_DeInit(jc);
|
||||||
memset(&jc->io_functions, 0, sizeof(jc->io_functions));
|
memset(&jc->io_functions, 0, sizeof(jc->io_functions));
|
||||||
|
g_probe_id = -1;
|
||||||
ctx->script_printf(ctx, MSG_INFO_0, "Probe released.\n");
|
ctx->script_printf(ctx, MSG_INFO_0, "Probe released.\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2909,10 +2994,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\n",
|
" bsdl=%s ir=%d proxy=%s caveats=0x%x maxtck=%dkHz\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->caveats, t->max_tck_khz);
|
||||||
}
|
}
|
||||||
return JTAG_CORE_NO_ERROR;
|
return JTAG_CORE_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user