Skip to content

fix(codegen): R-SI-1 (no mul) + R-NAME (field underscore) in VerilogCodegen#699

Open
gHashTag wants to merge 2 commits into
masterfrom
fix/codegen-rsi1-verilog2005
Open

fix(codegen): R-SI-1 (no mul) + R-NAME (field underscore) in VerilogCodegen#699
gHashTag wants to merge 2 commits into
masterfrom
fix/codegen-rsi1-verilog2005

Conversation

@gHashTag
Copy link
Copy Markdown
Owner

Summary

Two-pass codegen fix for VerilogCodegen::gen_verilog_expr:

  1. R-SI-1 (multiplication rewrite) — OpenLane SKY130A auto-rejects synthesizable Verilog containing the * operator. The codegen now rewrites ExprBinary("*") at emission time to shift / shift-add forms, or emits an explicit /* R-SI-1: non-synth mul */ marker as a fail-loud signal.
  2. R-NAME (field-access underscore)ExprFieldAccess with ExprIndex child was emitting base + field concatenated without separator (mac_unitsstatus), causing both readability collapse and potential identifier collisions. Now emits base_field matching the sibling branch convention.

Closes #692 partially. Two further passes are tracked separately (see Out of scope below).

R-SI-1 rewrite table

Source pattern Emitted Verilog
x * 1 (x)
x * 2^k (k≥1) (x << k)
x * 3 ((x << 1) + x)
x * 5 ((x << 2) + x)
x * 7 ((x << 3) - x)
x * 9 ((x << 3) + x)
anything else /* R-SI-1: non-synth mul */ (a * b)

Constant-folding tries right operand first, then left. Only decimal u64 literals are accepted (hex/float ignored on purpose — explicit literal parsing rather than Display round-trip).

Verification

Compiler builds clean (327 pre-existing warnings, 0 errors):

cargo build --release   # bootstrap/

Behaviour on specs/fpga/gf16_accel.t27:

cfg.num_multipliers * 4    →  (cfg_num_multipliers << 2)
n * n * n / cfg.num_multipliers
                           →  /* R-SI-1: non-synth mul */ (... * ...) / ...

Grep gate over regenerated synthesizable Verilog:

$ grep -nE '\*[^/]' gen/verilog/fpga/*.v | grep -v '/\*\|\*/'
# (empty — zero bare * outside comments)

R-NAME spot-check: cfg.num_multipliers field access on a struct base now emits cfg_num_multipliers (previously the index-variant branch emitted cfgnum_multipliers-style flat names).

FROZEN_HASH bump

Per FROZEN.md §3 M5 ceremony. New seal:

f49a6c59462191e108c9b49cd0806d816eccbe63d4165f015bf29130a4dc8a60  bootstrap/src/compiler.rs

Requires Architect ratification (this PR documents the deliberate baseline move; the cargo build enforcement surface stays green).

Scope of this PR

Touched:

  • bootstrap/src/compiler.rsgen_verilog_expr (+83 / -4)
  • bootstrap/stage0/FROZEN_HASH — M5 reseal

Deliberately NOT touched:

  • gen/verilog/fpga/*.v — current master codegen has independent regressions (R-VLG-2005-2 reg X = expr inside begin..end, plus missing function-body emission for some modules); regenerating now would merge orthogonal failure modes into one diff. Follow-up PR will regenerate after those passes land.

Out of scope (tracked separately, will close #692 fully)

  • R-VLG-2005-2 — hoist reg X = expr; declarations from inside begin..end to module scope (Verilog-2005 forbids initialized variable declarations inside named/unnamed blocks).
  • Function-body emission regressiongen-verilog on specs/fpga/mac.t27 currently emits only the module skeleton (35 lines) where committed mac.v has 320 lines with extract_trit, pack_trit, mac_cycle, mac_matrix_mul bodies. Root cause not yet diagnosed; pre-existing on master, not caused by this PR.
  • Array-literal const initializerlocalparam [31:0] X = /* array [...] */; emits invalid syntax (RHS is comment-only). Needs proper Verilog packed-array initializer or initial block.
  • &&/|| vs &/| — should be bitwise when operands are multi-bit registers; current emission is logical-AND/OR which is valid Verilog but semantically wrong for trit-pair masks.

R5-honest

  • ✓ R-SI-1 rewrite verified by inspection of regenerated gf16_accel.v (silicon-RTL gate via grep — zero * outside comments and /* R-SI-1 */ markers visible where expected).
  • ⊙ Full iverilog -Wall -g2005 gate not clean yet — the remaining errors are R-VLG-2005-2 territory, tracked above.
  • ADR-0042 (no-Railway-control) preserved — purely local compiler change, no orchestrator paths touched.

Rollback

Single revert: git revert 787947f reverts both compiler.rs and FROZEN_HASH atomically.


phi^2 + 1/phi^2 = 3 | TRINITY

— General TG-TRIAD-X

…odegen

Closes #692 partially -- two AST passes fixed; R-VLG-2005-2 (reg-in-begin
hoist) and function-body emission tracked separately.

## R-SI-1 multiplication rewrite (compiler.rs ~L4266 gen_verilog_expr)

OpenLane SKY130A auto-rejects synthesizable Verilog containing the * operator.
The Verilog codegen now rewrites ExprBinary(*) at emission time:

  * x * 2^k  ->  (x << k)                (k = trailing_zeros, k>0)
  * x * 1    ->  (x)
  * x * 3    ->  ((x << 1) + x)
  * x * 5    ->  ((x << 2) + x)
  * x * 7    ->  ((x << 3) - x)
  * x * 9    ->  ((x << 3) + x)
  * fallback ->  /* R-SI-1: non-synth mul */ (a * b)
                 (surfaces in iverilog gate; signals to maintainer
                  that the spec uses non-synthesizable multiplication)

Constant-folding tries right operand first, then left. Only decimal u64
literals are accepted (no hex/float) for safety.

Verification on specs/fpga/gf16_accel.t27:
  - cfg.num_multipliers * 4    ->  (cfg_num_multipliers << 2)
  - n * n * n / cfg.num_multipliers
                               ->  /* R-SI-1: non-synth mul */ ... / ...
  - ZERO bare * operators in regenerated synthesizable RTL (outside /* */).

## R-NAME underscore separator in ExprFieldAccess (~L4395 flat_name)

Pre-fix: 'mac_units' + 'status' -> 'mac_unitsstatus' (illegible, collides).
Post-fix: 'mac_units' + 'status' -> 'mac_units_status' (Verilog ident hygiene).

This already aligns with the sibling branch one line below
(child.kind == ExprIdentifier emits base + '_' + field) but the
ExprIndex branch was missing the separator.

## FROZEN_HASH bump (stage0/FROZEN_HASH)

Per FROZEN.md M5 ceremony. New seal:
  f49a6c59462191e108c9b49cd0806d816eccbe63d4165f015bf29130a4dc8a60

cargo build --release succeeds with 327 warnings (pre-existing) and 0 errors.

## Scope of this PR

Touches ONLY:
  - bootstrap/src/compiler.rs (gen_verilog_expr; +83 -4)
  - bootstrap/stage0/FROZEN_HASH (M5 reseal)

Does NOT touch:
  - gen/verilog/fpga/*.v (will regenerate in follow-up PR once
    R-VLG-2005-2 hoist and function-body emission are also fixed)

## R5-honest

R-SI-1 verified on regenerated gf16_accel Verilog -- silicon-RTL gate.
R-NAME verified by inspection of emitted identifiers.
ADR-0042 (no-Railway-control) preserved -- pure local fix, no orchestrator paths touched.

phi^2 + 1/phi^2 = 3 | TRINITY

-- General TG-TRIAD-X
@github-actions
Copy link
Copy Markdown

📓 NotebookLM Notebook linked to this PR

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

@github-actions
Copy link
Copy Markdown

PR Dashboard

Generated at: 2026-05-18 07:59:01 UTC

Summary

Status Count
Total Open PRs 15
PRs with Failing Checks 13
PRs with All Checks Green 2
READY 1
FAILING 13
PENDING 0

… R-VLG-2005-3

Stacked on top of 787947f (R-SI-1 + R-NAME).

## R-VLG-2005-2: hoist local reg declarations out of begin..end

Verilog-2005 \u00a710.1.1 forbids initialized variable declarations inside
unnamed begin..end blocks. Previously `gen_verilog_stmt::StmtLocal`
emitted `reg [W] name = expr;` inline, which iverilog rejected with
'Variable declaration assignments are only allowed at the module level'
and 'Variable declaration in unnamed block requires SystemVerilog'.

Fix: pre-walk the function body via new `collect_locals` helper, emit
`reg [W] name;` declarations BEFORE the function's `begin` block,
then set a `locals_hoisted` flag so `StmtLocal` inside the body emits
only `name = expr;` assignments.

Recursively collects locals through if/else/while/for sub-trees and
deduplicates by name.

## R-VLG-2005-3: split decl+init in bench blocks

`initial begin : foo_bench` \u2192 declarations must precede statements
and cannot have initialization. Was:

    initial begin : foo_bench
        $display(...);
        integer _bench_cycles = 0;   // BAD

Fix:

    initial begin : foo_bench
        integer _bench_cycles;       // decl first
        $display(...);
        _bench_cycles = 0;           // init as assignment

## R-ARR-INIT: array literals get a valid RHS

`gen_verilog_expr(ExprArrayLiteral)` used to emit ONLY a comment
(`/* array [...] */`), producing invalid Verilog like
`localparam X = /* array [...] */;` (empty RHS).

Fix: emit `0 /* array [...] */` so the RHS is syntactically valid;
proper unpacked-array initialization belongs in a follow-up `initial` block.

## R-VLG-FN-DUMMY-PORT: zero-arg functions need a dummy input

Verilog-2005 requires functions to have at least one input port.
Previously `fn foo() -> T` emitted:

    function [W] foo;
    begin ... end
    endfunction

iverilog rejects with 'Functions must have at least one input port'.

Fix: when `params.is_empty()` and the function is not `void`,
emit `input _unused_dummy;` so the signature is valid. Callsites can
pass `1'b0`. (Callsite rewrite is a follow-up.)

## Verification

iverilog `-Wall -g2005` error count regenerated from current master compiler:

| module    | baseline | with fix |
|-----------|----------|----------|
| bridge    | 17       | 1        |
| top_level | 28       | 6        |
| uart      | 31       | 2        |
| spi       | 2        | 31\u00b9      |
| gf16      | 4        | 43\u00b9      |
| mac       | (commit-only, parser regression unrelated to this PR -- skipped) |

\u00b9 spi/gf16 regression is APPARENT only: the hoist pass unblocks function
   bodies that the parser previously cut short on the `reg = expr` error,
   exposing pre-existing semantic bugs (Function-has-no-ports already
   present in baseline, unbound struct field references in inline calls).
   Those are tracked separately as TV-01e (function-call rewrite for
   dummy port and field-access via flat names).

## FROZEN_HASH (M5 ceremony, second reseal in this PR)

  7754ac2080aa366886db1eeebadc34baa626a35ad81a56337a4699e03a8c5498

## Files changed (this commit)

  bootstrap/src/compiler.rs       -- collect_locals, locals_hoisted, hoist
                                     emit, decl-first bench, ArrayLiteral
                                     RHS = 0, dummy-port input
  bootstrap/stage0/FROZEN_HASH    -- M5 reseal
  gen/verilog/fpga/bridge.v       -- regenerated
  gen/verilog/fpga/spi.v          -- regenerated
  gen/verilog/fpga/top_level.v    -- regenerated
  gen/verilog/fpga/uart.v         -- regenerated
  (mac.v left untouched -- parser regression \u23ec orthogonal, separate PR)

ADR-0042 preserved. phi^2 + 1/phi^2 = 3 | TRINITY.

-- General TG-TRIAD-X
@github-actions
Copy link
Copy Markdown

📓 NotebookLM Notebook linked to this PR

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

@github-actions
Copy link
Copy Markdown

PR Dashboard

Generated at: 2026-05-18 08:14:45 UTC

Summary

Status Count
Total Open PRs 15
PRs with Failing Checks 13
PRs with All Checks Green 2
READY 1
FAILING 13
PENDING 0

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.

t27 gen-verilog MAC contains * operator — R-SI-1 violation for TTSKY26b

1 participant