Skip to content

fpga(ax7203): cpphdl RC low-pass as a t27 spec, measured on the board (Closes #10276) - #10278

Merged
gHashTag merged 1 commit into
masterfrom
spec/fpga-rc-filter-ax7203
Oct 18, 2026
Merged

gHashTag merged 1 commit into
masterfrom
spec/fpga-rc-filter-ax7203

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Closes #10276

The RC low-pass from mirekez/cpphdl (analog/examples, rc_filter: R=1k, C=1n, tau=1 us, dt=50 ns, backward Euler) as one t27 spec. The spec is both the reference and the design: its tests run the 200-step stimulus in Q8.24 fixed point, and t27b verilog turns the same module into the AX7203 bitstream that runs that stimulus and reports over UART.

What is in the PR

  • specs/fpga/rc_filter_ax7203.t27 (module FpgaRcFilter).
    • The step: v' = (v + u/20) * 20/21 with 20/21 as shift-adds (G21 = 798915) and round-half-up (HALF_LSB = 2^23).
    • The machine takes 6 phases per step, so 200 steps take 1200 clocks at 100 MHz. The clock comes from a PLLE2_ADV with the LiteDRAM settings feeding a BUFG.
    • Reset is synchronous: the button and the PLL's LOCKED pass two flops before they start or clear the machine.
    • Receipt over UART at 115200: T27 RC A=<v at 5 us> B=<v at 10 us> S=<fold of all 200> C=<clocks> L=<line> PASS|FAIL.
    • Pins are pin attributes in the spec, with no hand Verilog or XDC. The output pins are pub var registers, so they show the state one clock later.
  • .trinity/seals/fpga_FpgaRcFilter.json: sealed by t27c on the Railway lab, 10/10 tests.
  • docs/reports/t27b_expectations.json: specs/fpga/rc_filter_ax7203.t27 is listed as pass. So are three specs master added without a row (specs/t27mcp/dashboard.t27, specs/t27mcp/telegram.t27, specs/tools/t27o/ledger.t27, UNLISTED drift).

Measured

Lab (Railway):

  • t27c test-report: 10/10, 0 vacuous.
  • t27c parse-complete: nothing discarded.
  • t27b test --blockers: lowering succeeds and stops at "JIT runs only on arm64", because the lab is x86. The arm64 JIT run is left to t27b-native-ratchet.

Error against f64 backward Euler over the same 200 steps: at most 2.65 LSB of Q8.24, which is under 3 LSB.

  • At 5 us: 0xFE0DA3 = 0.992396 V. cpphdl reports 0.992396; the analytic value is 0.993262.

Synthesis and place-and-route (yosys + nextpnr-xilinx, xc7a200tfbg484-2):

  • 520 LUT + 435 INV, 601 FF, 117 CARRY4, 40 MUXF7, 18 MUXF8.
  • 0 DSP, 0 BRAM, 1 PLLE2_ADV, 1 BUFG, 5 OBUF.
  • Fmax 120.74 MHz against the 100 MHz constraint.
  • Latency: 1200 clocks = 12 us of wall time for 10 us of circuit time.

Board (ALINX AX7203):

  • Flashed with openFPGALoader -c digilent_hs2 --busdev-num 1:3; bitstream sha256 0c7335628fb6b94aec6cfa9cce85a8a16d0fb01f72a349960f77bdf2d0aec632.
  • UART read on the CP2102N: 1336 of 1336 consecutive lines read A=FE0DA3 B=1EE92 S=77EDFF0295C3FE9E C=4B0 PASS, the values the tests assert.

Negative control (not committed): the same spec with HALF_LSB = 0 (truncation).

  • 5 of 10 tests fail and t27c seal refuses.
  • Its bitstream (sha256 5acec6af32749e885add58f99268cf8dda249623d59722bb9845ef5f57a4ee7c) printed A=FE0D98 B=1EE9D S=39837FB9327ADF13 FAIL on 1282 of 1282 lines. Those are exactly the values the mutated tests predict.

Found on the way

  • The clock.
    • A PLLE2_BASE at MULT 5 / DIV 10 never locked on this board.
    • Clocking the design straight from the 200 MHz input reached only 108 MHz Fmax, and the board printed garbage hex.
    • Hence the PLLE2_ADV at 100 MHz.
  • The reset. An unsynchronized async reset gave C=1199 (one clock lost) in a mutant. The two-flop synchronous start fixed it.
  • t27b. When a pub const net reads a var, t27b rejects the module with ExprIdentifier (undeclared), and it reports the error at a later fn's line. Making the outputs pub var registers, which verilog_plan.t27 treats as output regs, lowers cleanly.

Known red, not from this PR

specs/fpga/ddr3_head_bandwidth.t27 is listed as pass on master but is blocked under t27b (run 39105096338), so t27b-native-ratchet stays red until that spec's owner fixes it. This PR does not touch that spec or its row.

Not claimed

  • Power: not measured.
  • Other circuits, or other R, C or dt values: each needs its own coefficient.
  • SPICE or Verilog-A input: the circuit was transcribed from cpphdl's C++ by hand into the spec.

🤖 Generated with Claude Code

… 200 samples bit-exact, receipt PASS

specs/fpga/rc_filter_ax7203.t27 is the rc_filter example from mirekez/cpphdl
(analog/examples): R=1k, C=1n, dt=50 ns, backward Euler, in Q8.24 with a
1/21 coefficient made of shift-adds. The same spec is the software reference
(10 tests, 0 vacuous) and, through t27b verilog, the AX7203 design: 520 LUT,
601 FF, 117 CARRY4, 0 DSP, 0 BRAM, Fmax 120.74 MHz at 100 MHz.

Board: 1336/1336 UART lines A=FE0DA3 B=1EE92 S=77EDFF0295C3FE9E PASS.
Lab mutant HALF_LSB=0: 5 tests fail, the seal refuses, the board prints the
predicted FE0D98 1EE9D 39837FB9327ADF13 FAIL on 1282/1282 lines.

Ledger: the spec as pass, plus specs/t27mcp/dashboard.t27,
specs/t27mcp/telegram.t27 and specs/tools/t27o/ledger.t27, which master's
t27b-native-ratchet (run 39105096338) reports UNLISTED, t27b pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) October 18, 2026 07:21
@github-actions

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-18 07:21:32 UTC

Summary

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

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

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=0f8c66b9ed0e != 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).

@gHashTag
gHashTag merged commit 6dd449e into master Oct 18, 2026
30 of 33 checks passed
gHashTag added a commit that referenced this pull request Oct 18, 2026
… tests, comptime_float PLL params (Closes #10294) (#10296)

#10278 merged at its first commit, before its own reds were fixed:

- fpga-lint: t27c gen-verilog emits a module-level f64 fn as
  `function real` and an f64 param as `input real`; yosys read_verilog
  -sv refuses both (TOK_REAL). The f64 backward-Euler references now run
  inline in the tests, compared as squares, and PLLE2_ADV takes
  comptime_float. Same bounds as before; the clocked machine is unchanged.
- duplicate-bodies: hex_digit blessed (the copy in ddr3_head_bandwidth
  is deliberate, t27b bitstream specs are self-contained).
- elab ratchet: rc_filter_ax7203 8 recorded as a KNOWN GAP (t27c has no
  lowering for pin attributes or primitive instances; t27b builds the
  bitstream), plus 0 rows for three modules master added without one.

Lab: 10/10 tests, 0 vacuous; seal spec_hash 3728871b. t27b top.v logic
same as the board-verified build. Board: bitstream e619e966, 2130 of
2130 consecutive lines PASS.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Oct 18, 2026
…c again (Closes #10313) (#10314)

master's t27b-native-ratchet reads `specs/fpga/rc_filter_ax7203.t27:
ledger pass, now blocked: type comptime_float`. #10278 ledgered the spec
as t27b pass; #10296 then typed PLLE2_ADV's CLKIN1_PERIOD and
REF_JITTER1 as comptime_float so t27c gen-verilog would not emit
`input real`, and t27b rejects a comptime_float parameter. #10302 made
gen-verilog skip t27b bitstream specs, so that reason is gone: the two
parameters go back to f64 and the doc comment says why.

Measured on the Railway lab at master 9da2c5f, t27b and t27c built
there:
- t27b on master's spec: "unsupported construct type comptime_float at
  line 247"; on this spec: accepted, and `t27b build --elf` writes the
  object (the x86 lab has no t27b JIT; CI's arm64 runs the tests).
- `t27b verilog`: same 608 lines as master's; only the five
  `// ... (line N)` comments move by 2. The PLL instance still reads
  CLKIN1_PERIOD(5.0), REF_JITTER1(0.01), so the board RTL does not
  change.
- reference (t27c test-report, zig 0.16.0): 10 of 10 pass, 0 vacuous.
- seal regenerated with `t27c seal --save`: spec_hash 8fba8a41.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag added a commit that referenced this pull request Oct 18, 2026
…e-reading pins become registers (Closes #10327) (#10328)

master's t27b-native-ratchet reads `specs/fpga/ddr3_head_bandwidth.t27:
ledger pass, now blocked: ExprIdentifier(undeclared)`. The module-level
nets uart_tx, led_2 and led_3 read vars (st, verdict, hb), which t27b's
test path cannot do. As in rc_filter (#10278) they become pub var
registers set at the top of on_clock; on_clock takes the core's UART
output as core_uart_tx. The pinout, core instance, traffic, checksum,
divider and receipt are unchanged.

Measured on the Railway lab at master 7912660:
- t27b on master's spec: unsupported construct ExprIdentifier(undeclared)
  for st, verdict, hb; on this spec: no blocker (x86 stops at the JIT
  message, CI's arm64 runs the tests).
- t27b verilog: the module differs from master's only in uart_tx, led_2
  and led_3 (output reg, assigned in the clocked process).
- reference (t27c test-report, zig 0.16.0): 12 of 12 pass, 0 vacuous.
- seal regenerated with `t27c seal --save`: spec_hash 59242e4b.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
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.

fpga(ax7203): cpphdl RC low-pass as a t27 spec, measured on the board

1 participant