diff --git a/DESIGN.md b/DESIGN.md index 52dc5c6..4de07c7 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -12,8 +12,10 @@ cmake --build build -j - CMake **3.14+** required (uses `FetchContent_MakeAvailable`). - FTXUI is fetched at configure time from GitHub (`v6.1.9`, shallow clone). First configure pays ~20 s for the clone; subsequent ones are cached in `build/_deps/`. -- **System dependencies** (resolved via `find_package`): `libzip` (target `libzip::zip`) and `pugixml` (target `pugixml::pugixml`). Used by the ODS importer. Available on Arch via `pacman -S libzip pugixml`. +- **System dependencies** (resolved via `find_package`): `libzip` (target `libzip::zip`) and `pugixml` (target `pugixml::pugixml`). Used by the ODS importer. Arch: `pacman -S libzip pugixml`; Debian/Ubuntu: `libzip-dev libpugixml-dev`. +- **`libbsdl`** (standalone BSDL parser, LGPL-2.1) is the sibling repo at `../libbsdl`, pulled in via `add_subdirectory` (path overridable with `-DBSDL_DIR=...`) and linked dynamically (`bsdl::bsdl`; an LGPL `.so` is fine from EUPL essim). Powers the BSDL ingest behind `attach-bsdl`. - Sources are collected with `file(GLOB_RECURSE ALL_SOURCES "src/*.cpp")`. **After adding a new `.cpp`, re-run `cmake -S . -B build`** — CMake does not re-glob automatically and link will fail with "undefined reference". +- **Headless / batch**: `essim --batch --source FILE` runs a script and prints its console output to stdout, then exits without the TUI (good for CI / capturing `verify`). Also `--restore FILE` and `--commands-md [FILE]`. `BootDispatch` runs `--restore`/`--source` synchronously before the event loop (`Source` takes its headless drain branch when no screen is attached), so the console buffer is complete by the time `--batch` dumps it (`Tui::DumpOutput`). ## Layout @@ -35,6 +37,8 @@ src/ CheckIdentityCompatible + FillIdentityNCs pin_role.{hpp,cpp} pin_role(kind, name) → PinSpec, pin_layout(kind), FillPartFromLayout(part, kind) + bsdl_model.{hpp,cpp} BsdlModel (libbsdl C-ABI wrapper) + apply_bsdl(Part*, model) + bsdl_check.{hpp,cpp} check_pin_specs / check_jtag_chain → vector nets.{hpp,cpp} find_net / compute_all_nets / net_type_consistent analysis.{hpp,cpp} analyze_system → AnalysisReport (diff pairs, buses, anomalies) persist.{hpp,cpp} save / restore (tab-delimited) @@ -91,7 +95,7 @@ doc/classes.puml -- PlantUML class diagram - Multi-step prompts work via a `std::deque` queue. `Submit()` pops them one by one before falling back to dispatch. Adding a new command = one entry in `RegisterCommands()`; the prompt-flow and inline-flow are both handled automatically. - Tab completion: at the top-level prompt (no `pending`), completes built-in command names. Inside a prompt with `path_completion = true` (e.g. the `filename` step of `load`), completes file paths via `std::filesystem::directory_iterator` (handles `~/`, dirs get a trailing `/`). Logic: 1 match → replace; multiple with progress on the longest common prefix → extend; multiple stuck at LCP → list candidates in the visualisation area. -Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source`, `script-save`, `connect` (alias `plug`), `set-connector-type`, `set-signal-type`, `explore`, `verify`, `analyze`, `dashboard`, `clear`, `help`, `quit`/`exit`. `Esc` cancels an in-progress multi-step prompt. +Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source`, `script-save`, `connect` (alias `plug`), `set-connector-type`, `attach-bsdl`, `set-signal-type`, `explore`, `verify`, `analyze`, `dashboard`, `clear`, `help`, `quit`/`exit`. `Esc` cancels an in-progress multi-step prompt. `set ` declares a session-scoped variable. Subsequent commands expand `$name` and `${name}` in their args (substitution happens in `Finalize` between canonical-form recording and `spec.action(args)` — so `history` and `script-save` keep the **unexpanded** form, while the action sees resolved values). Unknown variables are left literal. `vars` is reset by `new`. Validation: `[A-Za-z_][A-Za-z0-9_]*`. @@ -105,7 +109,7 @@ Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source Pending prompts (from incomplete inline commands) are NOT considered interactive and are filled by subsequent script lines, the way you'd expect. Lines starting with `#` and blank lines are skipped; leading/trailing whitespace is trimmed; `~/` is expanded. -`save` / `restore` (`src/system/persist.{hpp,cpp}`): tab-delimited line format, no extra deps. Tags: `M` (module), `P` (part with `connector_type`), `N` (pin → signal name; empty = NC; optional 4th field carries `nc_origin_tag()`: `U` = ImportedUnconnected, `D` = DroppedSingleton — omitted when the pin has a signal or when origin is `None`), `S` (signal → type override; only emitted for non-default), `C` (connection header with endpoints + `transform_name`), `W` (wire pair within the current connection). The 4th N field is backward-compatible: pre-existing snapshots without it restore with `nc_origin = None`. +`save` / `restore` (`src/system/persist.{hpp,cpp}`): tab-delimited line format, no extra deps. Tags: `M` (module), `P` (part with `connector_type`), `B` (part's attached BSDL `.bsd` path — re-parsed and re-applied on restore; the path is persisted, **not** the derived pin specs), `N` (pin → signal name; empty = NC; optional 4th field carries `nc_origin_tag()`: `U` = ImportedUnconnected, `D` = DroppedSingleton — omitted when the pin has a signal or when origin is `None`), `S` (signal → type override; only emitted for non-default), `C` (connection header with endpoints + `transform_name`), `W` (wire pair within the current connection). The 4th N field is backward-compatible: pre-existing snapshots without it restore with `nc_origin = None`. **Signals** carry a `type` (`SignalType::Power | GndShield | Other`). The `Signal` constructor **defaults to `Other`** — auto-inference no longer happens at construction. Types are set in three ways, in priority order: @@ -118,11 +122,13 @@ Pending prompts (from incomplete inline commands) are NOT considered interactive The explore screen shows the type in the signal detail header. -**Pin spec (expected attributes)**: every Pin carries a `PinSpec spec` (`src/system/pin_spec.hpp`) — the *expected* half of verification, set from a model: `function` (Power/Ground/Signal/Clock/NoConnect/Jtag*), `direction` (In/Out/Bidir/Passive/Power), `pad` (physical package terminal, e.g. a BSDL ball), and `source` (which model wrote it). `set-connector-type` populates it via `pin_role(connector_type, pin_name) → PinSpec`. `Pin::expected_signal_type()` is now a **derived accessor** — `to_signal_type(spec.function)` (Power→Power, Ground→GndShield, else Other) — not a stored field; the *observed* half stays `Pin::signal()` + the net + inference, and `verify` diffs the two. The framework is wired end-to-end; the VPX 3U lookup (`vpx_3u_role`) is still a stub returning Other for every position, so today every pin's `function` resolves to Unknown → `expected_signal_type()` Other (behaviour unchanged from the old field) — fill in the real VITA 46 layout when needed. `direction`/`function`/`pad` are present but not yet model-populated: they are what the BSDL ingest (`libbsdl`) and the planned contention / undriven-net / NC-wired / JTAG-chain checks will consume. +**Pin spec (expected attributes)**: every Pin carries a `PinSpec spec` (`src/system/pin_spec.hpp`) — the *expected* half of verification, set from a model: `function` (Power/Ground/Signal/Clock/NoConnect/Jtag*), `direction` (In/Out/Bidir/Passive/Power), `pad` (physical package terminal, e.g. a BSDL ball), and `source` (which model wrote it). `set-connector-type` populates it via `pin_role(connector_type, pin_name) → PinSpec`. `Pin::expected_signal_type()` is now a **derived accessor** — `to_signal_type(spec.function)` (Power→Power, Ground→GndShield, else Other) — not a stored field; the *observed* half stays `Pin::signal()` + the net + inference, and `verify` diffs the two. The VPX 3U connector lookup (`vpx_3u_role`) is still a stub returning Other, so connector-typed pins resolve to function Unknown until that table is filled. **`direction`/`function`/`pad` are populated from BSDL** via `attach-bsdl` (see below) and consumed by the model-driven checks (`check_pin_specs`: contention / undriven / NC-wired) and the JTAG chain check (`check_jtag_chain`), both run by `verify`. **Connector pin layout (preparation)**: `pin_layout(connector_type)` returns the canonical full pin-name list for a known connector kind, and `FillPartFromLayout(part, kind)` materialises NC pins for any layout position absent from the imported netlist. `set-connector-type` calls it after setting `connector_type` (no-op today since `pin_layout` is a stub returning `{}` for everything — populate alongside `vpx_3u_role`). End-to-end chain in place: `set-connector-type → FillPartFromLayout → pin_role`. -**`verify` (three passes)**: (1) walks all typed pins and reports local mismatches between each pin's `expected_signal_type()` (derived from its `PinSpec`) and the actual signal type; (2) walks all bridged nets reporting Power↔GndShield inconsistencies; (3) prints a single-line orphan summary `N orphan pin(s) at import (X imported NC, Y dropped singleton)`. The orphan pass filters out pins that appear in any `Connection::pin_map` — those are bridged to a real signal on the peer module (typically `FillIdentityNCs`-materialised) and not real NCs at system level. The BFS-reached `(module, signal)` set for any signal is shown live in `explore`'s detail pane when a signal entry is selected. +**BSDL models (`attach-bsdl`)**: `attach-bsdl ` parses a BSDL device through `libbsdl` (wrapped by `BsdlModel`, `src/system/bsdl_model.{hpp,cpp}`), then `apply_bsdl(part, model)` binds each port to a Pin **by port name first, then by physical pad** — so a netlist that names IC pins either by signal or by package ball both bind. Each bound pin gets its `spec` set: `direction` (BSDL in/out/inout/linkage), `function` (TAP role → Jtag\*, `linkage` → Power/Ground/NoConnect by name, else Signal), `pad` (PIN_MAP ball), `source = Bsdl`. The `.bsd` path is stored on `Part::bsdl_path`, persisted via the `B` tag and re-applied on `restore`. Real-world check: an `xcku15p` FPGA in a VPX system binds 1517/1517 ports. + +**`verify` (five passes)**: (1) typed pins — local mismatch between each pin's `expected_signal_type()` (derived from its `PinSpec`) and the actual signal type; (2) bridged nets — Power↔GndShield inconsistencies; (3) orphan summary `N orphan pin(s) at import (X imported NC, Y dropped singleton)` (filters out pins bridged via any `Connection::pin_map` — typically `FillIdentityNCs`-materialised); (4) **model-driven pin checks** (`check_pin_specs`): `DriveContention` (≥2 push-pull `Out` on a net), `UndrivenNet` (a **fully-modelled** net with input(s) but no driver — nets with any Unknown-direction pin are skipped, so un-modelled drivers don't cause false positives), `NcWired` (a no-connect pin on a multi-pin net); (5) **JTAG chain** (`check_jtag_chain`): collects TAP pins by `spec.function`, maps each to its net, emits `JtagTapIncomplete` / `JtagBusUnbridged` (TMS or TCK not common to all TAP devices) / `JtagChainBreak` (dangling TDO/TDI, chain fan-out, or not a single head→tail daisy chain). The BFS-reached `(module, signal)` set for any signal is shown live in `explore`'s detail pane when a signal entry is selected. **`analyze` (post-processing pass)**: `analyze_system(System*) → AnalysisReport` (`src/system/analysis.{hpp,cpp}`) is a stateless read-only pass that detects structural signal groups and anomalies. Per-module (signals are module-scoped): @@ -290,6 +296,8 @@ The analyze screen additionally surfaces two "verify-class" issues, computed the - **pin-role mismatch** — a pin whose `expected_signal_type()` (derived from its `PinSpec`, set by `set-connector-type` via `pin_role(connector_type, pin_name)`) disagrees with the actual signal type. - **net-mix** — a bridged net (BFS over `Connection::pin_map`, ≥ 2 members) where `net_type_consistent(net, &dominant)` returns false. Specifically, the net contains both `Power` and `GndShield` signals. +The `verify` command (not the analyze screen, yet) also emits the **model-driven `AnomalyKind`s** from `bsdl_check.{hpp,cpp}`: `DriveContention` / `UndrivenNet` / `NcWired` (`check_pin_specs`) and `JtagTapIncomplete` / `JtagChainBreak` / `JtagBusUnbridged` (`check_jtag_chain`). They consume the BSDL-populated `PinSpec` plus `compute_all_nets`. Surfacing them in the analyze/dashboard Issues pane is a TODO. + ### Component kind `Part::kind` is inferred at construction (`src/system/component_kind.cpp`) from the leading reference-designator letter(s) of the part name. **Longest-match wins**: diff --git a/README.md b/README.md index c236c5a..e9efe95 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,15 @@ auto-generated reference at [`doc/user/commands.md`](doc/user/commands.md). A worked bring-up script is at [`test/system.essim`](test/system.essim); load it with `source test/system.essim`. +To run a script without the TUI and print its output to stdout (CI-friendly): + +```sh +./build/essim --batch --source bring-up.essim +``` + +Step-by-step walkthroughs for both the batch and TUI workflows are in +[`doc/user/tutorial.md`](doc/user/tutorial.md). + ## Dependencies - **C++17 compiler** and **CMake 3.14+**. @@ -28,6 +37,10 @@ load it with `source test/system.essim`. - Debian/Ubuntu — `sudo apt install libzip-dev libpugixml-dev` - Arch — `sudo pacman -S libzip pugixml` - Fedora — `sudo dnf install libzip-devel pugixml-devel` +- **libbsdl** — the standalone BSDL parser, a sibling repo expected at + `../libbsdl`, pulled in via `add_subdirectory` and linked dynamically. + Override its location with `-DBSDL_DIR=/path/to/libbsdl`. Powers the + `attach-bsdl` command and the pin/JTAG checks. - Fetched automatically at configure time via `FetchContent` (nothing to install): **FTXUI** v6.1.9 and **doctest** v2.4.11. - Optional, only for the `doc` target: **doxygen** and **python3**. diff --git a/doc/user/commands.md b/doc/user/commands.md index 5ed42ca..745c519 100644 --- a/doc/user/commands.md +++ b/doc/user/commands.md @@ -37,7 +37,7 @@ browse modules → parts/signals/connections → details (interactive) --- ### `export` *(interactive)* -export structured data to CSV (kinds: connections; bare form opens the file-picker dialog) +export structured data to CSV / ODS (kinds: connections; bare form opens the file-picker dialog) **Arguments** @@ -88,6 +88,21 @@ tag a part's connector type for transform lookup detect signal groups (diff pairs, buses) and structural anomalies **No arguments.** +--- +### `attach-bsdl` + +attach a BSDL (.bsd) model to a part and populate pin specs + +**Arguments** + +1. `module` +2. `part (name or pattern)` +3. `bsdl file (.bsd path)` + +**Notes** + +- no per-arg prompt: pass all args inline (or run bare for an empty-args path) + --- ### `clear` @@ -238,7 +253,7 @@ execute a file of commands line by line (interactive cmds rejected) --- ### `verify` -check pin roles locally and signal-type consistency across bridged nets +check pin roles, bridged-net consistency, and model-driven pin specs (contention/undriven/NC) **No arguments.** --- diff --git a/doc/user/index.md b/doc/user/index.md index 75185e4..108a35e 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -1,8 +1,9 @@ # essim — user guide -A short, task-oriented introduction to using essim. For an exhaustive -reference of every command, see [`commands.md`](commands.md) (auto- -generated from the binary). For scripting and `$variable` expansion, +A short, task-oriented introduction to using essim. For step-by-step +**batch** and **TUI** walkthroughs, see [`tutorial.md`](tutorial.md). For an +exhaustive reference of every command, see [`commands.md`](commands.md) +(auto-generated from the binary). For scripting and `$variable` expansion, see [`scripting.md`](scripting.md). ## What essim is @@ -91,6 +92,8 @@ fresh. ## Where to look next +- [`tutorial.md`](tutorial.md) — end-to-end batch and TUI walkthroughs, + including BSDL attach + the pin/JTAG checks. - [`commands.md`](commands.md) — exhaustive command reference, regenerated from the binary on every `cmake --build build --target doc`. - [`analysis.md`](analysis.md) — how essim classifies signals diff --git a/doc/user/tutorial.md b/doc/user/tutorial.md new file mode 100644 index 0000000..4111fd0 --- /dev/null +++ b/doc/user/tutorial.md @@ -0,0 +1,225 @@ +# essim — tutorial + +Two end-to-end walkthroughs of the same work. The **batch** one runs everything +from a script with no terminal UI (ideal for CI or quick reproducible checks); +the **TUI** one drives the same operations interactively. + +Both assume essim is built (`cmake -S . -B build && cmake --build build -j` — see +the [README](../../README.md)). For the exhaustive command list see +[`commands.md`](commands.md); for `$variable` expansion see +[`scripting.md`](scripting.md). + +--- + +## 1. Batch mode + +`essim --batch --source FILE` runs an essim script and prints the console output +to stdout, then exits — no TUI. The script is the same language you'd type at the +prompt: one command per line, `#` comments, blank lines ignored, `$variables` +expanded, `~/` paths supported. + +### 1.1 A first script + +Create `bring-up.essim`: + +```text +# Load two cards, tag + wire their VPX connectors, then verify. +new + +set nl /path/to/netlists + +load backplane $nl/backplane.NET altium +load payload1 $nl/payload.qcv mentor + +set-connector-type backplane J20 vpx-3u-bkp-p0 +set-connector-type payload1 P0 vpx-3u-payload-p0 +connect backplane J20 payload1 P0 + +verify +save system.essim +``` + +Run it: + +```sh +./build/essim --batch --source bring-up.essim +``` + +Each command is echoed with its result, e.g.: + +```text +> load backplane /path/to/netlists/backplane.NET altium +loaded 'backplane' from /path/to/netlists/backplane.NET +> connect backplane J20 payload1 P0 +connected: backplane/J20 <-> payload1/P0 via vpx-3u-p0 (59 wires) +> verify +verify: 0 local mismatch(es) over 0 typed pin(s). +verify: 0 inconsistent net(s) over 1 bridged net(s) (... total). +... +``` + +### 1.2 Attaching a BSDL model and checking the JTAG chain + +For complex ICs (FPGAs, big SoCs) you can attach a **BSDL** (`.bsd`) device model +to a part. essim binds each BSDL port to a pin — **by port name first, then by +package ball** — and fills the pin's direction / function / pad. `verify` then +runs model-driven checks on top of the basic ones. + +Add this before `verify`: + +```text +attach-bsdl payload1 U14 /path/to/bsdl/xcku15p_ffve1517.bsd +``` + +`attach-bsdl` reports the binding: + +```text +payload1/U14: attached BSDL 'XCKU15P_FFVE1517' — 1517/1517 ports bound +``` + +and `verify` now also prints the model-driven and JTAG passes, e.g.: + +```text +verify: 0 model-driven pin anomaly(ies). + [jtag-bus-unbridged] TMS is not common to all TAP devices (off-bus: ...) + [jtag-chain-break] JTAG chain is not a single daisy chain: 3 head(s), 3 tail(s) over 3 TAP device(s) +verify: 3 JTAG chain anomaly(ies). +``` + +What the new findings mean: + +| Finding | Meaning | +|---|---| +| `drive-contention` | ≥2 push-pull outputs share a net | +| `undriven-net` | a **fully-modelled** net has input(s) but no driver | +| `nc-wired` | a no-connect pin is wired onto a multi-pin net | +| `jtag-tap-incomplete` | a TAP device is missing TDI/TDO/TMS/TCK | +| `jtag-bus-unbridged` | TMS or TCK isn't common to every TAP device | +| `jtag-chain-break` | the TDO→TDI daisy chain dangles, forks, or isn't a single path | + +`undriven-net` only fires when *every* pin on the net is modelled — otherwise the +driver might sit on a part with no model, so essim stays quiet rather than guess. + +The attached `.bsd` path is stored in the `save` snapshot and re-applied on +`restore` (the path is persisted, not the derived pin data), so the model survives +round-trips. + +### 1.3 Capturing and chaining + +`--batch` exits 0 after the script, so pipe/grep the output like any tool: + +```sh +./build/essim --batch --source bring-up.essim | grep -E '\[jtag-|verify:' +``` + +Layer a script on top of a saved snapshot with `--restore`: + +```sh +./build/essim --batch --restore system.essim --source extra.essim +``` + +--- + +## 2. TUI mode + +Launch with no script: + +```sh +./build/essim +``` + +You land on the **dashboard** — a read-only overview (module / part / signal / +connection counts, health rows, a per-module table). It's the home screen. + +### 2.1 Getting around + +Dashboard single-key shortcuts: + +| Key | Goes to | +|---|---| +| `c` | console (type commands, read output) | +| `p` | plug / connect screen | +| `t` | set-connector-type screen | +| `e` | explore screen | +| `a` | analyze screen | +| `q` | quit | + +From anywhere, `Ctrl-P` opens the **command palette** (fuzzy-find a command, or +jump to a module / signal). `Esc` backs out of a screen or cancels a prompt. + +### 2.2 The console + +Press `c`. This is the textual shell — the same commands as the batch script, +typed one at a time: + +```text +> new +> load backplane ~/netlists/backplane.NET altium +> set-connector-type backplane J20 vpx-3u-bkp-p0 +> verify +``` + +Helpers: + +| Action | How | +|---|---| +| List commands / help on one | `help`, `help ` | +| Tab-complete a command or path | `‹Tab›` | +| Recall previous commands | ↑ / ↓ | +| Scroll output | `PageUp`/`PageDown`, `Home`/`End` | +| Cancel a multi-step prompt | `Esc` | + +Any command works inline (`set-connector-type backplane J20 vpx-3u-bkp-p0`) or +**bare** to open its interactive screen (`set-connector-type` then Enter). + +### 2.3 Interactive screens + +Bare `connect`, `set-connector-type`, `explore`, `analyze` open a full-screen +layout. Conventions: a title bar at the top, `Tab` cycles fields (the active one +flips to reverse video), `Esc` returns to the prompt. + +- **connect** (`p`) — pick module A, filter + pick its part, module B, its part, + then Apply. The transform is chosen from the two `connector_type`s. +- **set-connector-type** (`t`) — pick a module, filter + pick a part, type the + kind, Apply. Materialises any missing layout pins and fills each pin's expected + spec. +- **explore** (`e`) — browse modules → parts / signals → pins. Selecting a signal + shows its net (the members reached across connections); Enter on a signal opens + the power / gnd / other popup. +- **analyze** (`a`) — Issues / Groups / Types panes: pin-role mismatches, + bridged-net inconsistencies, diff pairs, buses, structural anomalies. + +### 2.4 Attaching BSDL in the TUI + +`attach-bsdl` has no dedicated screen — run it inline from the console: + +```text +> attach-bsdl payload1 U14 ~/bsdl/xcku15p_ffve1517.bsd +payload1/U14: attached BSDL 'XCKU15P_FFVE1517' — 1517/1517 ports bound +> verify +``` + +The model-driven and JTAG findings appear in the console output (see §1.2). +(Surfacing them on the analyze screen is planned.) + +### 2.5 Saving your work + +| Command | Writes | +|---|---| +| `save ` | full snapshot (including attached BSDL paths) | +| `script-save ` | replay-ready script of this session | +| `source ` | run a script line by line | + +A common loop: experiment in the console, `script-save` what works, hand-edit it +into a parametrised `$variable` script, then `--batch --source` it for repeatable +runs. + +--- + +## Where to look next + +- [`index.md`](index.md) — the user-guide overview. +- [`commands.md`](commands.md) — every command, regenerated from the binary. +- [`analysis.md`](analysis.md) — signal classification, buses, diff pairs. +- [`scripting.md`](scripting.md) — `set` / `$var`, `source` semantics. +- [`../../DESIGN.md`](../../DESIGN.md) — implementation notes (BSDL ingest, checks).