Skip to content

t27o: SPICE front end -- parse, flatten and inventory an ngspice netlist, or refuse with the line (Closes #10509) - #10530

Merged
gHashTag merged 2 commits into
masterfrom
claude/t27o-spice-10509
Oct 18, 2026
Merged

gHashTag merged 2 commits into
masterfrom
claude/t27o-spice-10509

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #10509

M2a of #10486: specs/tools/t27o/spice_convert.t27, the SPICE front end of t27o. tri t27o deck.spice|.cir|.sp reads an ngspice netlist, flattens its subcircuits and prints an inventory, or prints line N: <reason> plus the card and exits 1. Emitting the M1 cell spec from the inventory is M2b.

Result on the acceptance deck (top.spice, downloaded at run time, not committed)

tri t27o top.spice accepts the whole file (exit 0):

end: first .end on line 296, the deck ends with the last of 2, cards after it ignored: 0
cards: r=0 c=31 l=0 v=12 i=0 e=11 x=193 (subcircuit instances 72, mos 57, mim 64)
subckt definitions: 16
mos: sg13_lv_nmos=36 sg13_lv_pmos=21 total=57
mim: cap_cmim=64
tran (control) 0.1n 16u: tstep 1e-10 tstop 16e-6 (line 261)
corner: mos_tt from .../cornerMOSlv.lib (not read)
corner: cap_typ from .../cornerCAP.lib (not read)
temp: 50
recorded, not evaluated: lib=2 include=0 parameters=0 options=1 ic=10 nodeset=1 save=5 global=1 control-commands=6
flattened: instances=1454 r=0 c=38 l=0 v=12 i=0 e=11 mos=1265 mim=128 levels=517 depth=4
flattened, weighted by m: mos=1534 mim=128

Flattened MOS count 1265 (1534 weighted by m); instance names are hierarchical (x3.x1.xm1). An independent Python flattening of the same text gives the same numbers (mos 1265, mim 128, c 38, levels 517, depth 4, weighted 1534).

Two differences from the issue text, both measured: the deck has 16 .subckt definitions (grep -ci '^\.subckt'; the 17th hit of a plain grep is the comment **.subckt top), not 20; and it holds 64 cap_cmim X instances, which the issue does not mention, so cap_cmim is recognised like the two MOS models (counted separately, w/l/m checked, not evaluated). Also read beyond the card list because the deck needs them: .ic, .nodeset, .save, .global (recorded) and the post-processing commands inside .control (counted as ignored; any other control command is refused).

The .end rule

The reader reads the whole file and the deck ends at the LAST .end. In top.spice the first .end is line 296 and every .subckt follows it; ngspice produced the golden .raw from that file, so it saw them. Stopping at the first .end would refuse all 72 instances. Documented in the spec header and tested both ways (definitions after a first .end are read; with a single .end they are ignored and the instance is reported undefined with its line).

Gates

  • t27c test-report specs/tools/t27o/spice_convert.t27: 22 tests, 22 pass, 0 FAIL, 0 vacuous (Zig backend); t27b test (JIT) agrees, 22/22.
  • tri test specs/tools/t27o/spice_convert.t27: exit 0 (the new fail-on-vacuous gate, tri test: refuse vacuous passes -- report.t27 reads the "vacuous passes N of M" line (Closes #10497) #10507).
  • Negative controls run on copies: meg scale 6 -> 3 fails si_suffixes_milli_is_not_mega; stopping at the first .end fails the end-rule test; dropping the . in hierarchical names fails four tests. The suite also holds the refusal side (B source, .measure, braces, unknown element, bad number, undefined subckt, port mismatch, recursion, ...) next to the same decks without the offending card being accepted.
  • t27c seal ... --save / --verify: all hashes MATCH; .trinity/seals/t27o_T27oSpice.json committed. t27c frontier --only: REUSE for this spec and for rust_convert.t27.
  • Generated Rust gen/rust/tools/t27o/spice_convert.rs is byte-identical to t27c gen-rust output and compiles standalone; cargo build --release -p tri builds.
  • Hand-written Rust: 9 added lines in cli/tri/src/main.rs (module declaration + the extension dispatch in cmd_t27o; the extension test itself is is_spice_name in the spec). Cap is 40 per file, 80 per PR. If a gate asks for the owner-approved-foreign label for touching main.rs, that is the owner's to add; I did not add it.

Not in this PR

Node resolution through port maps, .param substitution, .measure/B sources/.func/behavioural models (refused), M2b (emitting the M1 spec), M3 (evaluating MOS). Two skill lines are now stale (tri-pipeline/SKILL.md "Rust is the only front end today", analog-emulation/SKILL.md converter row); the agent sandbox could not write under .claude/skills, so they are left for a follow-up.

🤖 Generated with Claude Code

…ist, or refuse with the line (Closes #10509)

specs/tools/t27o/spice_convert.t27 is the second t27o front end, beside rust_convert.t27, reached by
file extension: `tri t27o deck.spice | deck.cir | deck.sp` prints an inventory (cards by kind, the
subckt list, MOS and cap_cmim counts by model, .tran, .lib corners, TEMP, flattened instances with
ngspice-style hierarchical names, m-weighted device counts) or `line N: <reason>` plus the card, and
exits 1.

The deck ends at the LAST .end card: top.spice has its first .end at line 296 and every .subckt
definition after it, and ngspice produced the golden .raw from that file, so the reader reads the whole
file (rule and evidence in the spec header, tested both ways). The reported refusal is the earliest one.

22 tests, 0 vacuous; sealed; generated Rust under gen/rust/tools/t27o/; 9 lines of hand Rust in
cli/tri/src/main.rs (the module and the extension dispatch).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 17:01:19 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 46
PRs with All Checks Green 4
READY 2
FAILING 46
PENDING 0
NO CHECKS YET 0

These columns do not partition: 2 + 46 + 0 + 0 = 48, and there are 50 open PRs. A PR is being counted twice or not at all.

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fad12492937a != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

… so the SPICE glue does not grow hand-written Rust (Closes #10509)

own-language failed: +9 / -1 hand-written lines in cli/tri/src/main.rs
(owner rule 2026-10-09: hand-written foreign code may not grow). Both
functions document themselves "nothing calls this now" (their callers went
in #5935) and have no caller, test caller included; git_paths and
gate_script keep their other callers. Net hand-written Rust for the PR:
+9 / -38. `cargo build --release -p tri` passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 18:34:16 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 50
PRs with All Checks Green 0
READY 0
FAILING 50
PENDING 0
NO CHECKS YET 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=fad12492937a != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

analog M2a: SPICE front end for t27o -- parse, flatten and inventory an ngspice netlist, or refuse with the line

2 participants