Skip to content

fix(t27b): a port named after a t27 keyword is spelled with a trailing underscore (Closes #10061) - #10069

Merged
gHashTag merged 1 commit into
masterfrom
claude/keen-sinoussi-e73ac5
Oct 17, 2026
Merged

gHashTag merged 1 commit into
masterfrom
claude/keen-sinoussi-e73ac5

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Closes #10061

What

A t27b hardware module can now declare a Verilog port whose name is a t27 keyword. Spell it with one trailing _, and t27b verilog writes the keyword:

  • const in_ : u8 = undefined; gives input wire [7:0] in,, and every read is written in;
  • for_ gives the escaped \for , because for is also a Verilog keyword and is escaped as before;
  • data_, in__, _ and xin_ are unchanged. Only a t27 keyword followed by exactly one _ loses the _.

The rule is T27_KEYWORDS + name_end in specs/tri/t27b/verilog_plan.t27. The 29 keywords are the words the bootstrap lexer reserves. The writer is put_name in specs/tri/t27b/verilog_emit.t27, which already writes every Verilog name, so a declaration and its reads cannot disagree. Only .t27 is hand-written.

Why

The t27 lexer reserves in, so const in : u8 = undefined; is a parse error (Expected identifier after 'const', got KwIn). CVDP 8x3_priority_encoder_0001 requires input [7:0] in. In the Build-A-Chip-Bench proxy run, that alone cost arm B the task: the spec never reached Verilog.

The rename is injective, because no t27 name can be a keyword, so in_ cannot collide with a name in. A raw-identifier syntax (@"in") would need a t27c lexer/parser change in Rust, which is out of bounds. The CPU backends are untouched and still see in_.

Files

  • specs/tri/t27b/verilog_plan.t27: rule text in the header and in the Names section; T27_KEYWORDS, t27_keyword, name_end; 2 new tests.
  • specs/tri/t27b/verilog_emit.t27: put_name reads name_end; new asserts in numbers_ranges_literals_and_names_are_spelled; new test an_input_spelled_in_underscore_is_the_port_in.
  • gen/rust/tri/t27b/verilog_{emit,plan}.rs: output of t27c gen-rust. Before regenerating, gen-rust on the pristine specs at a18d7ee reproduced the committed files byte for byte.
  • .trinity/seals/t27b_T27b{VerilogPlan,VerilogEmit,AstShape,TsPlan,EmitKit}.json: t27c seal --save. AstShape, TsPlan and EmitKit (t27b: emit_kit -- one home for the writers' shared helpers (Refs #9994) #10065) record verilog_plan's hash in their closure; none of them has a committed gen/rust file that changes. TsPlan's closure entry was already stale on master (it still had the pre-fix(t27b): fold a constant rst_n/en tie-off out of the clocked process (Closes #10046) #10057 hash); this reseal fixes that too. seal --verify does not compare closure hashes, so it reported MATCH for all three before the reseal.

Lab receipts (Railway fs-lab, lane at a18d7ee (master, includes #10057 and #10065) + this diff, zig 0.16.0)

Spec tests (t27c test-report)

  • verilog_plan: 16/16, 0 vacuous. The new tests run 6 and 6 runtime asserts.
  • verilog_emit: 8/8, 0 vacuous. The new test runs 11 asserts; numbers_ranges_literals_and_names_are_spelled runs 12.

Mutants, to show the tests are not vacuous

  • name_end never strips (the old behavior): a_t27_keyword_with_one_trailing_underscore_names_the_keyword FAILS (plan 15/16). an_input_spelled_in_underscore_is_the_port_in and numbers_ranges_literals_and_names_are_spelled FAIL (emit 6/8).
  • name_end strips any trailing _, with no keyword check: the negative control a_name_that_is_no_keyword_keeps_every_underscore FAILS (plan 15/16), and the same two emit tests FAIL (emit 6/8).

8x3_priority_encoder_0001, arm B: the bench's .t27 with in spelled in_ (two-line edit), emitted with master t27b and with the rebuilt t27b, run through the same gate driver:

master t27b: input  wire [7:0] in_,   assign out = enc(in_);
this PR:     input  wire [7:0] in,    assign out = enc(in);

before: RESULT B 8x3_priority_encoder_0001 G1=FAIL [TESTS=1 PASS=0 FAIL=1] G2=PASS G3=PASS G4=PASS drc=0 hold_ws=0.635229 setup_ws=1.41336 area_um2=97.888
after:  RESULT B 8x3_priority_encoder_0001 G1=PASS [TESTS=1 PASS=1 FAIL=0] G2=PASS G3=PASS G4=PASS drc=0 hold_ws=0.635229 setup_ws=1.41336 area_um2=97.888

The spec's own emitted test bench (Icarus, -DT27B_TESTBENCH) passes: 4 tests, 0 failed, 11 runtime asserts. The diff of the emitted .v is the two in_ -> in lines and nothing else.

Controls: master t27b vs this t27b, stdout + stderr + exit code compared

set result
the bench's other arm-B specs: GP, clock_divider, complex_multiplier, convolutional_encoder, sync_pos_neg_edge_detector, moving_average, palindrome_detect, perfect_squares_generator, reverse_bits; plus the original in spec (parse error) byte-identical, all 10
every .t27 in the repo, plus the 10 bench specs (2483 files; 2333 emit, 150 refused) byte-identical, 0 differ

Wider checks

  • t27b crate tests (release, on the lab): 140 passed, 0 failed.
  • t27c seal --verify: MATCH for verilog_plan, verilog_emit, ast_shape, ts_plan and emit_kit after the reseal.

🤖 Generated with Claude Code

@gHashTag
gHashTag enabled auto-merge (squash) October 17, 2026 18:12
…g underscore (Closes #10061)

The t27 lexer reserves `in`, `for`, `test`, `module`, `var`, ..., so a
hardware module could not declare the Verilog port `in` that CVDP
8x3_priority_encoder_0001 requires: `const in : u8 = undefined;` is a parse
error (KwIn), and the spec never reached Verilog.

verilog_plan.t27 records the rule: a name that is a t27 keyword followed by
exactly one `_` (`in_`) is written in Verilog as the keyword (`in`). It holds
the keyword list (T27_KEYWORDS, the 29 words the bootstrap lexer reserves)
and name_end, which drops that `_` and nothing else; `data_`, `in__`, `_`
and `xin_` are unchanged. put_name in verilog_emit.t27 reads name_end, so the
declaration and every read agree, and a stem that is also a Verilog keyword
(`for_`) is escaped as before (`\for `). The rename is injective, since no t27
name can be a keyword. A raw-identifier syntax would need a lexer change in
t27c (Rust), which is out of bounds.

Tests: a_t27_keyword_with_one_trailing_underscore_names_the_keyword and
an_input_spelled_in_underscore_is_the_port_in; negative control
a_name_that_is_no_keyword_keeps_every_underscore, plus `data_` asserts in the
emit fixture. gen/rust regenerated with t27c gen-rust; VerilogPlan and
VerilogEmit resealed, and AstShape, TsPlan and EmitKit, whose seal closures record
verilog_plan's hash. All on the Railway lab.

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

Copy link
Copy Markdown
Contributor

PR Dashboard

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

Summary

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

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

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=e769ef021713 != 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 67f9288 into master Oct 17, 2026
27 of 33 checks passed
dmitrii-f-t27 added a commit to dmitrii-f-t27/t27 that referenced this pull request Oct 17, 2026
specs/tri/t27b/verilog_plan.t27: the two new header rules sat at the same place. Both are
kept: this branch's PIN DECLARATION (gHashTag#8889) and master's NAME THAT IS A T27 KEYWORD
(gHashTag#10061). The conflicts in the seals and in gen/rust/tri/t27b/verilog_{emit,plan}.rs are
derived: the Rust is regenerated with this tree's t27c gen-rust, and both seals are
re-saved.

Checks with this tree: seals of verilog_emit, verilog_plan, eth_top_ax7203 and
xc7_primitives verify; t27c test-report and t27b test 9/9, 17/17, 2/2; t27b verilog
writes the board top with its 22 pin attributes, synthesizable part unchanged.
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.

t27b verilog: a port cannot be named after a t27 keyword (in, for, ...)

1 participant