Skip to content

fix(gen-verilog): size struct and uN entry ports by one rule (#5904) - #5955

Merged
gHashTag merged 2 commits into
masterfrom
claude/verilog-struct-ports
Oct 4, 2026
Merged

gHashTag merged 2 commits into
masterfrom
claude/verilog-struct-ports

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 4, 2026

Copy link
Copy Markdown
Owner

Summary

t27c gen-verilog turned an on_comb parameter into a module port only for a short list of primitive types, so a spec whose datapath takes a struct (or an odd-width uN) got a module with no data ports and synthesized to zero logic. This PR gives the port decision one owner and widens it to every type whose width can be derived exactly.

  • One rule. VerilogCodegen::entry_port_width decides whether a parameter becomes a port and how wide it is. The entry-points census (t27c entry-points) now calls it instead of keeping its own is_sized_primitive list, so the census and the generator cannot disagree.
  • Struct ports. A struct whose fields are all strictly sized is a port of the summed field width (no _ => 32 default). The width is checked against packed_width_in on the same struct map, so the port is exactly as wide as the input the generated function declares. Nested structs, slices and f32/f64 stay refused.
  • uN / iN for 1 <= N <= 128 are N bits in ports and in type_to_width; type_is_signed accepts iN.
  • Refusals are visible. A refused entry point is reported on stderr as t27c gen-verilog: ENTRY POINT REFUSED -- <param>: <type>. The in-file comment and the exit status are unchanged.
  • Parameterless on_comb is called with the W530 1'b0 placeholder; on_comb() failed iverilog. This also fixes d_full_path.t27 and d_static_one.t27, which already failed iverilog on master.

Acceptance (from #5904)

check before after
ternary_mac + on_comb(acc: i32, a: i8, w: TernaryWeight): NO DATA PORTS 1 0
same: input wire [7:0] w ports 0 1
same: yosys synth cells 0 222
led_off_test.t27: ENTRY POINT REFUSED 1 0 (iverilog rc 0)
slice-parameter spec: refusal on stderr, exit status silent, 0 1 line, 0
is_sized_primitive in bootstrap/src (code) 3 (definition + 2 calls) 0 (one doc comment names the removed list)

Corpus effect (1174 specs, release t27c)

  • gen-verilog output changed for 8 specs; stderr and exit status identical for every spec.
    • Odd widths: igla/training/low_bit_ternary (i2 -> signed [1:0]), numeric/formats (u5, u4), numeric/gf4 (u4), tri/crypto/base64 (u6 locals), tri/utils/utf8 (u21).
    • led_off_test no longer refused (u1); d_full_path and d_static_one call on_comb(1'b0).
    • No corpus on_comb takes a struct today, so the struct path is covered by the new tests.
  • Corpus specs refused: 1 -> 0.
  • t27c entry-points: FORCED_ROOT 3 -> 7, FORCED_ROOT_WIDE 33 -> 29, FORCED_SCALAR 22 -> 25, FORCED_WIDE 55 -> 52; named-type blockers 105 -> 84; HAS_ENTRY 95, NOPARSE 12, NO_CANDIDATE 469 unchanged.

Seals

Tests

  • cargo test -p t27c: 2797 passed, 2 failed, vs 2789 passed on the base. The 2 failures are the known baseline failures and fail identically without this change.
  • New: bootstrap/tests/verilog_struct_entry_ports.rs (6 tests) and 2 unit tests in entry_points.rs.

Not in this PR

  • When an on_clock handler's parameters are refused but the module still has output ports, no refusal comment is written into the file (stderr still reports it).

Closes #5904. Part of #5905.

🤖 Generated with Claude Code

gHashTag and others added 2 commits October 4, 2026 14:43
An on_comb parameter becomes a module port only when its width can be
derived exactly, and the port is as wide as the input the generated
function declares for it (FR-001). One definition decides this --
VerilogCodegen::entry_port_width -- and the entry-points census calls it
instead of keeping a private is_sized_primitive list.

- Struct ports: a struct whose fields are all strictly sized is a port of
  the summed field width (no `_ => 32` default). The width is checked
  against packed_width_in on the same struct map, so port == function
  input. Nested structs, slices and f32/f64 stay refused.
- uN / iN for 1 <= N <= 128 are N bits in ports and type_to_width;
  type_is_signed accepts iN.
- A refusal is reported on stderr as
  `t27c gen-verilog: ENTRY POINT REFUSED -- <param>: <type>`; the in-file
  comment and the exit status are unchanged.
- A parameterless on_comb is called with W530's 1'b0 placeholder
  (`on_comb()` failed iverilog). Reachable now that `-> u1` is sized;
  also fixes d_full_path.t27 and d_static_one.t27, which already failed
  iverilog on master.

Measurements (release t27c, 1174 specs):

- ternary_mac + on_comb(acc: i32, a: i8, w: TernaryWeight):
  NO DATA PORTS 0, `input wire [7:0] w` 1, yosys 222 cells.
- led_off_test.t27: ENTRY POINT REFUSED 0, `function [31:0] on_comb` 0;
  iverilog rc 0, yosys 0 cells (result tied to 1).
- slice spec: stderr refusal lines 1, exit 0.
- Corpus gen-verilog diff vs master: 8 specs, stderr and exit codes
  identical for every spec.
  - Odd widths: igla/training/low_bit_ternary (i2 -> signed [1:0]),
    numeric/formats (u5, u4 parameters), numeric/gf4 (u4),
    tri/crypto/base64 (u6 locals), tri/utils/utf8 (u21).
  - led_off_test no longer refused (u1); d_full_path and d_static_one
    call on_comb(1'b0).
  - No corpus on_comb takes a struct. Corpus specs refused: 1 -> 0.
- t27c entry-points: FORCED_ROOT 3 -> 7, FORCED_ROOT_WIDE 33 -> 29,
  FORCED_SCALAR 22 -> 25, FORCED_WIDE 55 -> 52; named-type blockers
  105 -> 84; HAS_ENTRY 95, NOPARSE 12, NO_CANDIDATE 469 unchanged.
- Seals: 9 files for the 5 odd-width specs resealed with
  `t27c seal <spec> --save`; check_seal_coverage.py OK (1314 hold).
- FROZEN_HASH resealed with `t27c frozen-digest`.
- cargo test -p t27c: 2797 passed, 2 failed (the 2 known baseline
  failures), vs baseline 2789 passed; +6 tests in
  tests/verilog_struct_entry_ports.rs, +2 entry_points unit tests.

gen/verilog/ is a stale snapshot (last regenerated at #1399), so it was
not regenerated here.

Closes #5904

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #5904

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

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-04 08:05:11 UTC

Summary

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

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

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=46f5599e58f6 != 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 fb83b54 into master Oct 4, 2026
38 of 40 checks passed
dmitrii-f-t27 pushed a commit to dmitrii-f-t27/t27 that referenced this pull request Oct 4, 2026
gHashTag#5955 merged; bring the branch onto master. compiler.rs is unchanged by
master, so FROZEN_HASH still matches.

Closes gHashTag#5963

Co-Authored-By: Claude Opus 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.

gen-verilog refuses an on_comb over a struct or u1 although its own functions lower both -- ternary_mac stays at 0 cells

1 participant