From 5d5fbb6ec702155f4e245795802fb91b57a9ebe3 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 6 May 2026 02:15:16 -0300 Subject: [PATCH] fix(hw): intercard_link width-guard fires at elaboration (was simulation-only) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #11 placed the MAST #14 width-contract guard inside `initial $error` blocks. Verilator `--lint-only` does not execute `initial` blocks, so a future violation of LANES * LANE_WIDTH == 128 would have lint-passed silently. Today the contract is satisfied, so the gap was masked. Fix: lift `$error` out of `initial` and place it directly in the generate body (Option A from issue #12). This is an IEEE 1800-2012 elaboration-time construct that Verilator enforces under `--lint-only`. Applied to both sites: - src/intercard_link.sv (production module's internal width-guard) - verif/intercard_link/test_widths.sv (testbench elaboration guard) Verilator 5.048 verification: Default params (LANES=4, LANE_WIDTH=32 → 128): bash verif/intercard_link/run_lint.sh → exit 0, "PASS" banner Violation (LANES=8, LANE_WIDTH=32 → 256, via temp violation TB): verilator --lint-only ... → exit 1 %Warning-USERERROR: src/intercard_link.sv:89:13: intercard_link: INTERCARD_LANES (8) * INTERCARD_LANE_WIDTH (32) = 256, expected 128 (MAST #14 contract). The "OK" branch in test_widths.sv still uses `initial $display` (purely observational under --binary; harmless under --lint-only). closes #12 Authored by Agent 2 (FPGA Hardware). Signed-off-by: Marcos --- src/intercard_link.sv | 13 ++++++++++--- verif/intercard_link/test_widths.sv | 15 +++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/intercard_link.sv b/src/intercard_link.sv index e12d602..047538c 100644 --- a/src/intercard_link.sv +++ b/src/intercard_link.sv @@ -74,15 +74,22 @@ module intercard_link #( // Width-contract sanity: INTERCARD_LANES * INTERCARD_LANE_WIDTH must // equal 128 (INTERCARD_BUS_WIDTH per the MAST #14 contract). Any // override that breaks this contract will fail elaboration. + // + // NOTE on placement: $error sits directly in the generate body (NOT + // wrapped in `initial begin ... end`). Verilator `--lint-only` parses + // and elaborates the design but does NOT run `initial` blocks, so + // wrapping $error in `initial` would mask a contract violation under + // lint-only (issue #12). Generate-body $error is an IEEE 1800-2012 + // elaboration-time construct and is enforced by Verilator at lint time. // ------------------------------------------------------------------ localparam int INTERCARD_BUS_WIDTH = INTERCARD_LANES * INTERCARD_LANE_WIDTH; - initial begin - if (INTERCARD_BUS_WIDTH != 128) begin + generate + if (INTERCARD_BUS_WIDTH != 128) begin : g_width_contract_broken $error("intercard_link: INTERCARD_LANES (%0d) * INTERCARD_LANE_WIDTH (%0d) = %0d, expected 128 (MAST #14 contract).", INTERCARD_LANES, INTERCARD_LANE_WIDTH, INTERCARD_BUS_WIDTH); end - end + endgenerate // ------------------------------------------------------------------ // Stub body: tie outputs to safe defaults so synthesis/elab does not diff --git a/verif/intercard_link/test_widths.sv b/verif/intercard_link/test_widths.sv index fb72886..e207189 100644 --- a/verif/intercard_link/test_widths.sv +++ b/verif/intercard_link/test_widths.sv @@ -56,12 +56,19 @@ module test_widths; localparam int ACTUAL_BUS_WIDTH = 4 /* INTERCARD_LANES */ * 32 /* INTERCARD_LANE_WIDTH */; + // $error sits DIRECTLY in the generate body (no `initial` wrapper). + // Lint-only mode does NOT run `initial` blocks, so wrapping would + // mask contract violations under lint-only (issue #12). The bare + // $error form is an IEEE 1800-2012 elaboration-time construct and + // is enforced at lint time. + // + // The "OK" branch retains `initial $display` because that path is + // observational, not a gate — when the contract holds, lint-only + // exits 0 and the display is a no-op (only meaningful under --binary). generate if (ACTUAL_BUS_WIDTH != EXPECT_BUS_WIDTH) begin : g_width_mismatch - // This $error fires at elaboration. Verilator surfaces it as - // an elaboration-time error and exits non-zero. - initial $error("intercard_link width contract broken: actual=%0d expected=%0d", - ACTUAL_BUS_WIDTH, EXPECT_BUS_WIDTH); + $error("intercard_link width contract broken: actual=%0d expected=%0d", + ACTUAL_BUS_WIDTH, EXPECT_BUS_WIDTH); end else begin : g_width_ok initial $display("[test_widths] INTERCARD_BUS_WIDTH = %0d (matches MAST #14 contract)", ACTUAL_BUS_WIDTH);