Skip to content

analog: linear.t27 -- linear state-space cell, MNA folded to x' = x + rnd(Dx + Nu), reproduces M0 bit for bit (Closes #10487) - #10511

Merged
gHashTag merged 1 commit into
masterfrom
claude/analog-m1-linear-10487
Oct 18, 2026
Merged

gHashTag merged 1 commit into
masterfrom
claude/analog-m1-linear-10487

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #10487

M1 of #10486. specs/analog/linear.t27 is a linear analog cell for small R / C / L circuits with one voltage source and a fixed step. M0 (specs/fpga/rc_filter_ax7203.t27) is its 1x1 case and is reproduced bit for bit.

What it is

  • Modified nodal analysis with backward Euler is folded at compile time to D = M - I and N, stored as Q.24 integers. One step is x_i' = x_i + rnd(sum_j D_ij x_j + N_i u): exact wide sum, one rounding per state, half away from zero.
  • The step is unsigned only. States, constants and inputs are sign-magnitude u32 words (bit 31 = direction flag); positive and negative products add into two u64 registers (pos_after, neg_after); delta_of does the one subtraction and the one rounding. No i32/i64 anywhere, because t27b verilog refuses signed types. The fns from vin_at to fold take only bool and unsigned words, so the board tops of M1b can reach them.
  • Circuits as element tables: M0's RC (R=1k, C=1n, dt=50 ns), series RLC (R=100, L=10 uH, C=1 nF, dt=5 ns; inductor current stored times ZS = sqrt(L/C) = 100 Ohm so every entry of M stays near 1), 3-node and 5-node RC ladders (R=1k, C=1n, dt=50 ns).
  • Every stored constant is derived in the tests: they stamp A, C/dt, B from the table, solve by Gauss-Jordan elimination with partial pivoting in f64, and assert each integer equals round(value * 2^24). No derived value is within 1e-4 of a rounding tie; |A X - R| < 1e-12.

Measured (all from t27c test-report / t27c seal on this branch)

  • Tests: 14, pass 14, FAIL 0, vacuous passes 0 of 14. tri test specs/analog/linear.t27: 14 pass, 0 fail.
  • M0, bit for bit: RC circuit gives first samples 798915 and 1559786, A = 16649635 (step 100), B = 126610 (step 200) and S = 0x77EDFF0295C3FE9E. M0's numbers are pinned as literals in the test (use of M0 would carry its on_clock/PLL top into every importer).
  • Error vs the f64 backward Euler run of the same method (max over all states and 200 steps, in LSB of 2^-24; asserted from both sides so a better or worse cell must change the stated number): RC 2.65 (bound 3), RLC 8.44 (bound 9), ladder-3 9.45 (bound 10), ladder-5 40.03 (bound 41). The error grows with 1/(1 - rho) of the slowest pole (rho 0.952, 0.975, 0.990, 0.996).
  • Stability, ||M_q^256||_inf < 1: RC 3.8e-6, RLC 2.7e-3, ladder-3 0.098, ladder-5 0.445. The RLC has ||M_q||_inf = 1.045 > 1 and is still stable, which is why the plain norm is not used. Failing on purpose: sign of D flipped (M = 1 + 1/21), M = I, and a coupled pair with eigenvalue 1.4.
  • RLC numerical damping (stated in the header, asserted): backward Euler energy factor 0.95012 per step against the circuit's 0.95123; the det of the lower-right block of the derived M equals it to 1e-12; after the 100 charging steps the ringing amplitude is 0.943 of the true one.
  • Negative controls. In-spec: a conductance on the wrong node (ladder-3 and ladder-5), a capacitor on the wrong node, an inductor with swapped terminals, the series R on the wrong node, a doubled R, a wrong dt each leave at least one stored constant different from the derived one (asserted), and a truncating step ends at 16649624, not 16649635. By hand: a copy of the spec with one table entry changed (LAD3_A[3] 2 -> 1) fails 3 of 14 tests, and t27c seal --save on it refuses with exit 1 ("refusing to seal ...: 3 of 14 test(s) FAIL"). Changing one stored constant by 1 (LAD3_D[1], 696219 -> 696218) fails the same 3 of 14 tests under test-report.
  • Seal: t27c seal specs/analog/linear.t27 --save, then --verify: spec, zig, verilog, c, rust hashes all MATCH. Seal committed as .trinity/seals/analog_AnalogLinear.json.
  • tri types dup and tri types redef: nothing new.

Not covered (stated in the spec header)

No board tops, bitstream, LUT/FF/DSP/Fmax or UART receipt (M1b, #10496). No netlist reader (M2). No nonlinear device (M3). Nothing about the continuous circuit, only backward Euler at this dt. Nothing about power. A capacitor or inductor on the driven node is refused by table_ok. docs/reports/t27b_expectations.json is not edited; the steward adds the ledger row.

Notes for review

  • vin_at, round_back and fold are byte-identical to M0's (t27c dupes --bodies). They cannot come from a use of M0 (see above). A follow-up can make M0 use them from this file: t27b carries only the fns the hardware reaches, so no f64 code would reach the board. fabs64 joins three other private abs helpers in the corpus; none is pub and shareable.
  • Language limit hit: a module-level one-element array const with a lone literal, pub const X : [1]u32 = [798915];, lowers to the Zig array TYPE [798915] and the spec fails to compile; [a | 0] and [798915] ** 1 lower correctly, and so does the same literal in a function body. Worked around with ** 1.
  • t27b was not built in this sandbox, so t27b verilog was not run on the scalar step; the step uses only bool, u32 and u64.

🤖 Generated with Claude Code

… rnd(Dx + Nu), reproduces M0 bit for bit (Closes #10487)

M1 of #10486. specs/analog/linear.t27 is a linear analog cell for small R / C / L
circuits with one voltage source and a fixed step. Modified nodal analysis with
backward Euler is folded at compile time to D = M - I and N, stored as Q.24
integers, and one step is x_i' = x_i + rnd(sum_j D_ij x_j + N_i u) with one
rounding per state, half away from zero.

The step is unsigned only (the fns a board top reaches take bool and u32/u64):
states, constants and inputs are sign-magnitude u32 words with bit 31 as the
direction flag; the positive and the negative products add into two u64
registers, one subtraction and one rounding give the delta. No signed type,
because `t27b verilog` refuses one.

Circuits as element tables: the M0 RC (R=1k, C=1n, dt=50ns), a series RLC
(R=100, L=10uH, C=1nF, dt=5ns, inductor current stored times 100 Ohm), a 3-node
and a 5-node RC ladder.

The tests derive every stored constant: they stamp A, C/dt and B from the
element table, solve by Gauss-Jordan elimination with partial pivoting in f64,
and assert each integer equals round(value * 2^24). Mis-stamped elements (a
conductance, a capacitor, an inductor on the wrong node) change a derived value
and fail; t27c seal refuses the mutant. Stability is ||M_q^256||_inf < 1 per
circuit, with unstable matrices failing the same check. The RC circuit gives
M0's A, B and S = 0x77EDFF0295C3FE9E. Error against the f64 backward Euler run
of the same method is bounded per circuit (3, 9, 10, 41 LSB).

No board tops and no Verilog here; those are M1b (#10496).

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

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 15:48:10 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 M1: linear state-space cell -- any linear R/C/L circuit, fixed step, folded to x' = Mx + Nu

2 participants