probes: add probe-config profiles loaded from probes.yaml
- new modules/probes/ parses probes.yaml (libyaml): a defaults: map applied on every jtag_open + named profiles: selected with `jtag_open <idx> <profile>` (jtag_profiles lists them); each value is pushed into the script envvar store the driver reads at open time - ships a flashpro profile (ADBUS4 high-Z) that lets the IGLOO2 kit's embedded FlashPro (FT4232H, port 0) detect the chain - CLAUDE.md: decision entry for probes.yaml + a design note on the probe / JTAG-link / device config strategy (driver-neutral link layer) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
66
CLAUDE.md
66
CLAUDE.md
@@ -116,6 +116,21 @@ probe" if the libs are absent), it costs nothing to build in:
|
|||||||
disable with `-DBS_ENABLE_DIGILENT=OFF`. Adept Runtime is only needed
|
disable with `-DBS_ENABLE_DIGILENT=OFF`. Adept Runtime is only needed
|
||||||
at runtime to actually drive such a probe.
|
at runtime to actually drive such a probe.
|
||||||
|
|
||||||
|
### Probe config profiles (probes.yaml)
|
||||||
|
|
||||||
|
Probe wiring/electrical settings live in `probes.yaml` (parsed by
|
||||||
|
`modules/probes/`, libyaml), layered on top of the built-in
|
||||||
|
`config.script` defaults: a `defaults:` map applied on every `jtag_open`
|
||||||
|
(so opening without a profile is deterministic) plus named `profiles:`
|
||||||
|
selected with `jtag_open <idx> <profile>` (`jtag_profiles` lists them).
|
||||||
|
Each key/value is pushed into the script envvar store the driver reads at
|
||||||
|
open time. The mechanism is driver-agnostic (any `set`-able probe var),
|
||||||
|
but today only the FTDI driver (and minimally the Linux-GPIO one) reads
|
||||||
|
config envvars, so profiles mostly tune FTDI. Motivating case: the
|
||||||
|
embedded FlashPro on Microsemi kits (FT4232H ch.A) needs ADBUS4 high-Z —
|
||||||
|
the `flashpro` profile sets `PROBE_FTDI_SET_PIN_DIR_ADBUS4: 0`. See the
|
||||||
|
config-strategy design note below for where this is headed.
|
||||||
|
|
||||||
### Xilinx caveats
|
### Xilinx caveats
|
||||||
|
|
||||||
On 7-Series / UltraScale / UltraScale+, `CCLK` is not a regular I/O
|
On 7-Series / UltraScale / UltraScale+, `CCLK` is not a regular I/O
|
||||||
@@ -127,6 +142,57 @@ The FPGA must be in a configurable state before loading the proxy.
|
|||||||
Issue `JPROGRAM` first to reset, then `CFG_IN` + shift the bitstream,
|
Issue `JPROGRAM` first to reset, then `CFG_IN` + shift the bitstream,
|
||||||
then `JSTART` and check `DONE`.
|
then `JSTART` and check `DONE`.
|
||||||
|
|
||||||
|
## Probe / JTAG-link / device config strategy (design note)
|
||||||
|
|
||||||
|
Partly built, partly planned. Guiding idea: **separate three concerns
|
||||||
|
that are conflated today, and express the shared JTAG-link settings in
|
||||||
|
driver-neutral terms that get resolved per session.**
|
||||||
|
|
||||||
|
### The three concerns
|
||||||
|
|
||||||
|
| Layer | What it owns | Where it lives | Applied |
|
||||||
|
|-------|-------------|----------------|---------|
|
||||||
|
| **Probe (sonde)** | driver + interface, pin map, buffer-enable, TRST/SRST pins, level-shift, *max TCK the adapter supports* | `probes.yaml` (done) | at `jtag_open` |
|
||||||
|
| **JTAG link** | TCK freq, RTCK, reset behaviour, chain layout — **driver-neutral names**, resolved to effective values | *missing today* | open, then refined after detect |
|
||||||
|
| **Device** | IDCODE/BSDL/IR/proxy/caveats, programming method, *max TCK the part/board tolerates* | `fpga_registry.yaml` (done) | after IDCODE match |
|
||||||
|
|
||||||
|
### The smell that motivated this
|
||||||
|
|
||||||
|
Frequency has no single home. Only the FTDI driver reads
|
||||||
|
`PROBE_FTDI_TCK_FREQ_KHZ`; our Digilent driver **hardcodes 4 MHz**
|
||||||
|
(`digilent_jtag_drv.c`); J-Link/LPT read nothing. And the `PROBE_FTDI_*`
|
||||||
|
namespace mixes probe *wiring* (pin map, ADBUS4) with *link* properties
|
||||||
|
(freq, RTCK, TRST timing). Frequency isn't an FTDI fact — it's a link
|
||||||
|
fact bounded by both the probe and the board/device.
|
||||||
|
|
||||||
|
### Target model
|
||||||
|
|
||||||
|
- **Driver-neutral link vars** (`tck_khz`, `rtck`, `reset`, …) set once
|
||||||
|
(by user / probe defaults / device). Each driver consumes them: our
|
||||||
|
Digilent driver reads them directly; the Viveris FTDI driver is fed
|
||||||
|
`PROBE_FTDI_TCK_FREQ_KHZ` via a thin translation shim at open (no
|
||||||
|
Viveris edit). J-Link/LPT can stay on their defaults.
|
||||||
|
- **Resolution**: effective `tck = min(user request, probe max, device/
|
||||||
|
board max)`. A small session step computes it at open, and re-applies
|
||||||
|
after `jtag_autoinit` once the device (hence its cap / programming
|
||||||
|
method) is known — using the `jtag_close`→reopen seam if a re-init is
|
||||||
|
needed. This also dissolves the chicken-and-egg: a conservative link
|
||||||
|
default gets you to detection, then the device refines it.
|
||||||
|
- `probes.yaml` gains an optional `max_tck_khz`; `fpga_registry.yaml`
|
||||||
|
gains optional `max_tck_khz` + a `prog` method tag (`proxy_spi`/`svf`).
|
||||||
|
|
||||||
|
### Phasing
|
||||||
|
|
||||||
|
- **A** — one canonical `tck_khz` honoured by FTDI (shim) + Digilent
|
||||||
|
(read it): kills the immediate smell, one knob for the two real probes.
|
||||||
|
- **B** — device `max_tck_khz` + resolution after `jtag_autoinit`.
|
||||||
|
- **C** — generalise the other link settings (reset/RTCK) and wire the
|
||||||
|
`prog` method tag into backend dispatch (ties into 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
|
||||||
|
layer** in the middle.
|
||||||
|
|
||||||
## Programming backends: beyond Xilinx external flash (design note)
|
## Programming backends: beyond Xilinx external flash (design note)
|
||||||
|
|
||||||
Not yet implemented — captured so the design is ready. Guiding vision:
|
Not yet implemented — captured so the design is ready. Guiding vision:
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ the full walkthrough (probe → proxy → flash) lives in
|
|||||||
| Category | Commands |
|
| Category | Commands |
|
||||||
|----------|----------|
|
|----------|----------|
|
||||||
| Script control | `set`, `print`, `print_env_var`, `if`, `goto`, `call`, `return`, `rand`, `init_array`, `system`, `pause` |
|
| Script control | `set`, `print`, `print_env_var`, `if`, `goto`, `call`, `return`, `rand`, `init_array`, `system`, `pause` |
|
||||||
| Probe / chain | `jtag_probes`, `jtag_open`, `jtag_close`, `jtag_scan`, `jtag_autoinit`, `jtag_ndev`, `jtag_devices` |
|
| Probe / chain | `jtag_probes`, `jtag_open`, `jtag_close`, `jtag_profiles`, `jtag_scan`, `jtag_autoinit`, `jtag_ndev`, `jtag_devices` |
|
||||||
| BSDL / pins | `jtag_bsdl`, `jtag_pins`, `jtag_mode`, `jtag_pin_dir`, `jtag_pin_set`, `jtag_pin_get`, `jtag_push_pop` |
|
| BSDL / pins | `jtag_bsdl`, `jtag_pins`, `jtag_mode`, `jtag_pin_dir`, `jtag_pin_set`, `jtag_pin_get`, `jtag_push_pop` |
|
||||||
| I²C / MDIO / SPI over BS pins (EXTEST) | `jtag_i2c_scl`, `jtag_i2c_sda`, `jtag_i2c_rd`, `jtag_i2c_wr`, `jtag_mdio_mdc`, `jtag_mdio_io`, `jtag_mdio_rd`, `jtag_mdio_wr`, `jtag_spi_cs/mosi/miso/clk`, `jtag_spi_xfer` |
|
| I²C / MDIO / SPI over BS pins (EXTEST) | `jtag_i2c_scl`, `jtag_i2c_sda`, `jtag_i2c_rd`, `jtag_i2c_wr`, `jtag_mdio_mdc`, `jtag_mdio_io`, `jtag_mdio_rd`, `jtag_mdio_wr`, `jtag_spi_cs/mosi/miso/clk`, `jtag_spi_xfer` |
|
||||||
| FPGA registry | `fpga_list`, `fpga_info` |
|
| FPGA registry | `fpga_list`, `fpga_info` |
|
||||||
@@ -121,6 +121,10 @@ Use `help <command>` for per-command help.
|
|||||||
|
|
||||||
- **FTDI** MPSSE (FT2232D/H, FT4232H, …) — see the `PROBE_FTDI_*` block
|
- **FTDI** MPSSE (FT2232D/H, FT4232H, …) — see the `PROBE_FTDI_*` block
|
||||||
in `modules/config/config.script` for pin mapping and TCK frequency.
|
in `modules/config/config.script` for pin mapping and TCK frequency.
|
||||||
|
Boards that wire the FT4232H differently (e.g. the embedded FlashPro on
|
||||||
|
Microsemi eval kits, which needs ADBUS4 left high-Z) are handled by a
|
||||||
|
**probe profile** in `probes.yaml`: `jtag_profiles` lists them,
|
||||||
|
`jtag_open <idx> <profile>` applies one (e.g. `jtag_open 0 flashpro`).
|
||||||
- **SEGGER J-Link**
|
- **SEGGER J-Link**
|
||||||
- **Linux GPIO** (sysfs; deprecated on recent kernels, libgpiod migration TBD)
|
- **Linux GPIO** (sysfs; deprecated on recent kernels, libgpiod migration TBD)
|
||||||
- **Digilent JTAG-SMT2 / SMT2-NC** — built in by default on Linux
|
- **Digilent JTAG-SMT2 / SMT2-NC** — built in by default on Linux
|
||||||
@@ -159,6 +163,7 @@ modules/
|
|||||||
├── os_interface/ Portable fs/network wrappers
|
├── os_interface/ Portable fs/network wrappers
|
||||||
└── natsort/ Natural-order pin-name sorting
|
└── natsort/ Natural-order pin-name sorting
|
||||||
fpga_registry.yaml FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats)
|
fpga_registry.yaml FPGA registry (IDCODE → BSDL, IR opcodes, proxy, caveats)
|
||||||
|
probes.yaml Probe-config profiles (defaults + per-probe overrides)
|
||||||
bsdl_files/ BSDL files for target FPGAs
|
bsdl_files/ BSDL files for target FPGAs
|
||||||
bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq)
|
bscan_proxies/ BSCAN proxy bitstreams (MIT, from quartiq)
|
||||||
scripts/ Example scripts
|
scripts/ Example scripts
|
||||||
|
|||||||
12
modules/probes/CMakeLists.txt
Normal file
12
modules/probes/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE ALL_SOURCES "*.c")
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||||
|
|
||||||
|
add_library(probes ${ALL_SOURCES})
|
||||||
|
|
||||||
|
# probes.yaml is parsed at runtime via libyaml (same as the fpga module).
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(YAML REQUIRED IMPORTED_TARGET yaml-0.1)
|
||||||
|
target_link_libraries(probes PUBLIC PkgConfig::YAML)
|
||||||
213
modules/probes/probes.c
Normal file
213
modules/probes/probes.c
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <yaml.h>
|
||||||
|
|
||||||
|
#include "probes.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* probes.yaml parser. Structure:
|
||||||
|
*
|
||||||
|
* defaults:
|
||||||
|
* VAR: value
|
||||||
|
* ...
|
||||||
|
* profiles:
|
||||||
|
* name:
|
||||||
|
* VAR: value
|
||||||
|
* other: {}
|
||||||
|
*
|
||||||
|
* Parsed once and cached. See probes.h for semantics.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DEFAULT_PROBES_FILE "probes.yaml"
|
||||||
|
|
||||||
|
typedef struct { char *key; char *val; } kv;
|
||||||
|
typedef struct { char *name; kv *vars; int nvars; } profile;
|
||||||
|
|
||||||
|
static kv *g_def = NULL; static int g_ndef = 0;
|
||||||
|
static profile *g_prof = NULL; static int g_nprof = 0;
|
||||||
|
static int g_loaded = 0;
|
||||||
|
static char g_source[1024] = "";
|
||||||
|
|
||||||
|
static char *xstrdup(const char *s)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
size_t n;
|
||||||
|
if (!s) return NULL;
|
||||||
|
n = strlen(s) + 1;
|
||||||
|
p = (char *)malloc(n);
|
||||||
|
if (p) memcpy(p, s, n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_kv(kv **arr, int *n, const char *k, const char *v)
|
||||||
|
{
|
||||||
|
kv *t = (kv *)realloc(*arr, (*n + 1) * sizeof(kv));
|
||||||
|
if (!t) { fprintf(stderr, "probes: out of memory\n"); return; }
|
||||||
|
*arr = t;
|
||||||
|
t[*n].key = xstrdup(k);
|
||||||
|
t[*n].val = xstrdup(v);
|
||||||
|
(*n)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static profile *add_profile(const char *name)
|
||||||
|
{
|
||||||
|
profile *t = (profile *)realloc(g_prof, (g_nprof + 1) * sizeof(profile));
|
||||||
|
if (!t) { fprintf(stderr, "probes: out of memory\n"); return NULL; }
|
||||||
|
g_prof = t;
|
||||||
|
g_prof[g_nprof].name = xstrdup(name);
|
||||||
|
g_prof[g_nprof].vars = NULL;
|
||||||
|
g_prof[g_nprof].nvars = 0;
|
||||||
|
return &g_prof[g_nprof++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mapping context at each nesting depth. */
|
||||||
|
enum { C_NONE = 0, C_ROOT, C_DEFAULTS, C_PROFILES, C_PROFBODY };
|
||||||
|
|
||||||
|
#define MAX_DEPTH 16
|
||||||
|
|
||||||
|
static int load(const char *path)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
yaml_parser_t parser;
|
||||||
|
yaml_event_t ev;
|
||||||
|
int depth = 0, done = 0, rc = 0;
|
||||||
|
int ctx[MAX_DEPTH];
|
||||||
|
char *key[MAX_DEPTH];
|
||||||
|
profile *cur = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
f = fopen(path, "rb");
|
||||||
|
if (!f) return -1;
|
||||||
|
if (!yaml_parser_initialize(&parser)) { fclose(f); return -1; }
|
||||||
|
yaml_parser_set_input_file(&parser, f);
|
||||||
|
|
||||||
|
memset(ctx, 0, sizeof(ctx));
|
||||||
|
memset(key, 0, sizeof(key));
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
if (!yaml_parser_parse(&parser, &ev)) {
|
||||||
|
fprintf(stderr, "probes: YAML parse error in %s: %s (line %lu)\n",
|
||||||
|
path, parser.problem ? parser.problem : "?",
|
||||||
|
(unsigned long)parser.problem_mark.line + 1);
|
||||||
|
rc = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ev.type) {
|
||||||
|
case YAML_MAPPING_START_EVENT: {
|
||||||
|
int newc = C_NONE;
|
||||||
|
if (depth == 0) {
|
||||||
|
newc = C_ROOT;
|
||||||
|
} else {
|
||||||
|
int c = ctx[depth];
|
||||||
|
const char *k = key[depth];
|
||||||
|
if (c == C_ROOT) {
|
||||||
|
if (k && !strcmp(k, "defaults")) newc = C_DEFAULTS;
|
||||||
|
else if (k && !strcmp(k, "profiles")) newc = C_PROFILES;
|
||||||
|
} else if (c == C_PROFILES) {
|
||||||
|
cur = k ? add_profile(k) : NULL;
|
||||||
|
newc = C_PROFBODY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (depth < MAX_DEPTH - 1) depth++;
|
||||||
|
ctx[depth] = newc;
|
||||||
|
free(key[depth]);
|
||||||
|
key[depth] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case YAML_MAPPING_END_EVENT:
|
||||||
|
free(key[depth]);
|
||||||
|
key[depth] = NULL;
|
||||||
|
if (depth > 0) depth--;
|
||||||
|
break;
|
||||||
|
case YAML_SCALAR_EVENT: {
|
||||||
|
const char *v = (const char *)ev.data.scalar.value;
|
||||||
|
int c = ctx[depth];
|
||||||
|
if (c == C_DEFAULTS) {
|
||||||
|
if (!key[depth]) { key[depth] = xstrdup(v); }
|
||||||
|
else { add_kv(&g_def, &g_ndef, key[depth], v); free(key[depth]); key[depth] = NULL; }
|
||||||
|
} else if (c == C_PROFBODY) {
|
||||||
|
if (!key[depth]) { key[depth] = xstrdup(v); }
|
||||||
|
else { if (cur) add_kv(&cur->vars, &cur->nvars, key[depth], v); free(key[depth]); key[depth] = NULL; }
|
||||||
|
} else {
|
||||||
|
/* C_ROOT / C_PROFILES: the scalar is a key/name whose
|
||||||
|
* value is the next mapping. */
|
||||||
|
free(key[depth]);
|
||||||
|
key[depth] = xstrdup(v);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case YAML_STREAM_END_EVENT:
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
yaml_event_delete(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEPTH; i++) free(key[i]);
|
||||||
|
yaml_parser_delete(&parser);
|
||||||
|
fclose(f);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_loaded(void)
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
|
||||||
|
if (g_loaded) return;
|
||||||
|
g_loaded = 1;
|
||||||
|
|
||||||
|
path = getenv("BS_PROBES");
|
||||||
|
if (!path || !*path) path = DEFAULT_PROBES_FILE;
|
||||||
|
|
||||||
|
if (load(path) == 0 && (g_ndef > 0 || g_nprof > 0)) {
|
||||||
|
strncpy(g_source, path, sizeof(g_source) - 1);
|
||||||
|
g_source[sizeof(g_source) - 1] = '\0';
|
||||||
|
}
|
||||||
|
/* Silent when absent: probe profiles are optional. */
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_apply_defaults(probe_set_fn set, void *user)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
ensure_loaded();
|
||||||
|
for (i = 0; i < g_ndef; i++) set(user, g_def[i].key, g_def[i].val);
|
||||||
|
return g_ndef;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_apply_profile(const char *name, probe_set_fn set, void *user)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
ensure_loaded();
|
||||||
|
for (i = 0; i < g_nprof; i++) {
|
||||||
|
if (!strcmp(g_prof[i].name, name)) {
|
||||||
|
for (j = 0; j < g_prof[i].nvars; j++)
|
||||||
|
set(user, g_prof[i].vars[j].key, g_prof[i].vars[j].val);
|
||||||
|
return g_prof[i].nvars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int probe_profile_count(void)
|
||||||
|
{
|
||||||
|
ensure_loaded();
|
||||||
|
return g_nprof;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *probe_profile_name(int index)
|
||||||
|
{
|
||||||
|
ensure_loaded();
|
||||||
|
return (index >= 0 && index < g_nprof) ? g_prof[index].name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *probe_config_source(void)
|
||||||
|
{
|
||||||
|
ensure_loaded();
|
||||||
|
return g_source[0] ? g_source : NULL;
|
||||||
|
}
|
||||||
40
modules/probes/probes.h
Normal file
40
modules/probes/probes.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef _PROBES_H
|
||||||
|
#define _PROBES_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Probe configuration profiles, loaded at runtime from probes.yaml.
|
||||||
|
*
|
||||||
|
* The built-in config.script (and an optional CWD config.script) set the
|
||||||
|
* baseline probe variables. probes.yaml layers on top of that:
|
||||||
|
* - a `defaults:` map applied on every jtag_open (so opening without a
|
||||||
|
* profile restores a known baseline), then
|
||||||
|
* - a named `profiles:` map whose vars override the defaults for one
|
||||||
|
* probe (e.g. an embedded FlashPro that needs ADBUS4 high-Z).
|
||||||
|
*
|
||||||
|
* Each key/value is pushed into the script envvar store via the caller's
|
||||||
|
* callback — the same vars config.script sets and the driver reads at
|
||||||
|
* open time (jtagcore_getEnvVarValue). This keeps the parser decoupled
|
||||||
|
* from the script context.
|
||||||
|
*
|
||||||
|
* File lookup: $BS_PROBES, else "probes.yaml" relative to the current
|
||||||
|
* directory (same convention as fpga_registry.yaml). Absence is silent —
|
||||||
|
* profiles are optional.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Callback used to set one envvar. `user` is opaque to this module. */
|
||||||
|
typedef void (*probe_set_fn)(void *user, const char *key, const char *value);
|
||||||
|
|
||||||
|
/* Apply the defaults: map. Returns the number of vars applied (0 if none
|
||||||
|
* or no file). */
|
||||||
|
int probe_apply_defaults(probe_set_fn set, void *user);
|
||||||
|
|
||||||
|
/* Apply the named profile's vars on top of the defaults. Returns the
|
||||||
|
* number of vars applied, or -1 if no such profile exists. */
|
||||||
|
int probe_apply_profile(const char *name, probe_set_fn set, void *user);
|
||||||
|
|
||||||
|
/* Listing / diagnostics. */
|
||||||
|
int probe_profile_count(void);
|
||||||
|
const char *probe_profile_name(int index);
|
||||||
|
const char *probe_config_source(void); /* path loaded, or NULL */
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "bsdl_parser/bsdl_loader.h"
|
#include "bsdl_parser/bsdl_loader.h"
|
||||||
#include "os_interface/os_interface.h"
|
#include "os_interface/os_interface.h"
|
||||||
#include "fpga/fpga.h"
|
#include "fpga/fpga.h"
|
||||||
|
#include "probes/probes.h"
|
||||||
#include "bscan_spi/bscan_spi.h"
|
#include "bscan_spi/bscan_spi.h"
|
||||||
#include "spi_flash/spi_flash.h"
|
#include "spi_flash/spi_flash.h"
|
||||||
|
|
||||||
@@ -1802,10 +1803,21 @@ static int cmd_print_probes_list(script_ctx *ctx, char *line)
|
|||||||
return JTAG_CORE_NO_ERROR;
|
return JTAG_CORE_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Push one probes.yaml key/value into the script envvar store — the same
|
||||||
|
* store config.script writes and the driver reads at open time. */
|
||||||
|
static void probe_envvar_set(void *user, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
script_ctx *ctx = (script_ctx *)user;
|
||||||
|
setEnvVarDat((envvar_entry *)ctx->env, (char *)key, (char *)value);
|
||||||
|
}
|
||||||
|
|
||||||
const char *cmd_open_probe_help[] = {
|
const char *cmd_open_probe_help[] = {
|
||||||
"<probe>(int)", // Arguments "1<arg>(type) ..."
|
"<probe>(int) [profile](str)", // Arguments "1<arg>(type) ..."
|
||||||
"Open a probe by its index from jtag_probes (0, 1, 2, ...).",
|
"Open a probe by its index from jtag_probes (0, 1, 2, ...).",
|
||||||
"A raw 0x-prefixed probe id (as printed after the index) also works.",
|
"A raw 0x-prefixed probe id (as printed after the index) also works.",
|
||||||
|
"Optional [profile] applies a probe-config profile from probes.yaml",
|
||||||
|
"(e.g. 'flashpro'); jtag_profiles lists them. The profile's variables",
|
||||||
|
"override the probes.yaml defaults for this open.",
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
static int cmd_open_probe(script_ctx *ctx, char *line)
|
static int cmd_open_probe(script_ctx *ctx, char *line)
|
||||||
@@ -1813,6 +1825,7 @@ static int cmd_open_probe(script_ctx *ctx, char *line)
|
|||||||
int ret;
|
int ret;
|
||||||
int id;
|
int id;
|
||||||
char probe_id[64];
|
char probe_id[64];
|
||||||
|
char profile[64];
|
||||||
jtag_core *jc;
|
jtag_core *jc;
|
||||||
|
|
||||||
jc = (jtag_core *)ctx->app_ctx;
|
jc = (jtag_core *)ctx->app_ctx;
|
||||||
@@ -1836,6 +1849,20 @@ static int cmd_open_probe(script_ctx *ctx, char *line)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the probes.yaml defaults, then the optional named profile,
|
||||||
|
// into the envvar store the driver reads when it initialises.
|
||||||
|
probe_apply_defaults(probe_envvar_set, ctx);
|
||||||
|
if (get_param(ctx, line, 2, profile) > 0)
|
||||||
|
{
|
||||||
|
if (probe_apply_profile(profile, probe_envvar_set, ctx) < 0)
|
||||||
|
{
|
||||||
|
ctx->script_printf(ctx, MSG_ERROR,
|
||||||
|
"Unknown probe profile '%s'. Run jtag_profiles to list them.\n", profile);
|
||||||
|
return JTAG_CORE_BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0, "Applied probe profile '%s'.\n", profile);
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -1889,6 +1916,32 @@ static int cmd_close_probe(script_ctx *ctx, char *line)
|
|||||||
return JTAG_CORE_NO_ERROR;
|
return JTAG_CORE_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *cmd_profiles_help[] = {
|
||||||
|
"",
|
||||||
|
"List the probe-config profiles defined in probes.yaml.",
|
||||||
|
"Apply one when opening: jtag_open <index> <profile>.",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
static int cmd_profiles(script_ctx *ctx, char *line)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
const char *src;
|
||||||
|
|
||||||
|
(void)line;
|
||||||
|
n = probe_profile_count();
|
||||||
|
src = probe_config_source();
|
||||||
|
if (!src)
|
||||||
|
{
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0,
|
||||||
|
"No probes.yaml loaded; using built-in / config.script defaults.\n");
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
ctx->script_printf(ctx, MSG_INFO_0, "%d probe profile(s) in %s:\n", n, src);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
ctx->script_printf(ctx, MSG_NONE, " %s\n", probe_profile_name(i));
|
||||||
|
return JTAG_CORE_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
const char *cmd_load_bsdl_help[] = {
|
const char *cmd_load_bsdl_help[] = {
|
||||||
"<file>(string) <device>(int)",
|
"<file>(string) <device>(int)",
|
||||||
"Load/attach a bsdl file to a device into the chain.",
|
"Load/attach a bsdl file to a device into the chain.",
|
||||||
@@ -3368,6 +3421,7 @@ cmd_list script_commands_list[] =
|
|||||||
{"jtag_probes", cmd_print_probes_list, cmd_print_probes_list_help},
|
{"jtag_probes", cmd_print_probes_list, cmd_print_probes_list_help},
|
||||||
{"jtag_open", cmd_open_probe, cmd_open_probe_help},
|
{"jtag_open", cmd_open_probe, cmd_open_probe_help},
|
||||||
{"jtag_close", cmd_close_probe, cmd_close_probe_help},
|
{"jtag_close", cmd_close_probe, cmd_close_probe_help},
|
||||||
|
{"jtag_profiles", cmd_profiles, cmd_profiles_help},
|
||||||
{"jtag_autoinit", cmd_autoinit, cmd_autoinit_help},
|
{"jtag_autoinit", cmd_autoinit, cmd_autoinit_help},
|
||||||
{"jtag_scan", cmd_init_and_scan, cmd_init_and_scan_help},
|
{"jtag_scan", cmd_init_and_scan, cmd_init_and_scan_help},
|
||||||
{"jtag_ndev", cmd_print_nb_dev, cmd_print_nb_dev_help},
|
{"jtag_ndev", cmd_print_nb_dev, cmd_print_nb_dev_help},
|
||||||
|
|||||||
27
probes.yaml
Normal file
27
probes.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# bs_explorer probe-config profiles
|
||||||
|
#
|
||||||
|
# Loaded at runtime by modules/probes/, layered on top of the built-in
|
||||||
|
# config.script defaults. Looked up CWD-relative (run from the repo
|
||||||
|
# root), or via $BS_PROBES.
|
||||||
|
#
|
||||||
|
# defaults: applied on every `jtag_open` (restores a known baseline
|
||||||
|
# so opening without a profile is deterministic).
|
||||||
|
# profiles: named override sets; select with `jtag_open <idx> <name>`.
|
||||||
|
#
|
||||||
|
# Each key is a probe variable (see modules/config/config.script for the
|
||||||
|
# full list); each value is what `set <KEY> <value>` would assign.
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
# Baseline for a standalone FT2232H JTAG probe: drive ADBUS4 as the
|
||||||
|
# "JTAG buffer enable" output (matches the built-in default).
|
||||||
|
PROBE_FTDI_SET_PIN_DIR_ADBUS4: 1
|
||||||
|
|
||||||
|
profiles:
|
||||||
|
# Embedded FlashPro on Microsemi eval kits (FT4232H, JTAG on channel A
|
||||||
|
# = probe index 0). Its ADBUS4 must be left high-Z or the chain stays
|
||||||
|
# silent. Usage: jtag_open 0 flashpro
|
||||||
|
flashpro:
|
||||||
|
PROBE_FTDI_SET_PIN_DIR_ADBUS4: 0
|
||||||
|
|
||||||
|
# Plain FT2232H probe: nothing to override beyond the defaults.
|
||||||
|
ft2232h: {}
|
||||||
Reference in New Issue
Block a user