fix(hw): intercard_link width-guard fires at elaboration (was simulation-only)#14
Merged
marcos-mendez merged 1 commit intoMay 6, 2026
Conversation
…ion-only) 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 <m@pop.coop>
Member
Author
Review by Agent RVerdict: APPROVE Pre-review gates (
|
5 tasks
marcos-mendez
added a commit
that referenced
this pull request
May 6, 2026
#13) (#15) PR #11 declared `clk_p`/`clk_n` as unconditionally `output` on the single `intercard_link` module. Per docs/hw/intercard-connector-pinout.md §2.1 §6, the forwarded source-synchronous clock is driven by the upstream card and consumed by the downstream card — so the original single-module surface only modeled the upstream role. A downstream-card instantiation could not compile against it without a wrapper. Apply Option C from issue #13 (least disruptive): - git-rename src/intercard_link.sv → src/intercard_link_upstream.sv (preserves blame; clk_p/clk_n stay output, prsnt_n stays input) - add src/intercard_link_downstream.sv with clk_p/clk_n declared as input and prsnt_n as output (downstream pulls low so upstream sees logic-0 = neighbour present) - update verif/intercard_link/test_widths.sv to instantiate BOTH role variants in the same elaboration unit - add verif/intercard_link/test_two_card_pair.sv: cross-wires upstream → downstream over the connector signal names. Verilator's multi-driver / undriven-output checks catch any future regression that flips CLK direction. Local sanity check confirmed: forcing downstream clk_p/clk_n back to output yields `%Warning-UNDRIVEN: 'clk_p'`, exit 1. - run_lint.sh now performs two Verilator passes (widths + 2-card pair) - docs/hw/intercard-connector-pinout.md §2.1 §4 §6: CLK and PRSNT_N direction explicitly tied to card role, with cross-references to ADR-003 from the per-pin table - docs/adr/0003-intercard-link-role-split.md: rationale, alternatives A/B/C/no-op, consequences, and a downstream-caller table Verilator 5.048 evidence (local): $ bash verif/intercard_link/run_lint.sh [run_lint] verilator Verilator 5.048 2026-04-26 rev v5.048 [run_lint] (1/2) widths: ...test_widths.sv → exit 0 [run_lint] (2/2) two-card pair: ...two_card_pair.sv → exit 0 [run_lint] PASS — intercard_link_upstream + intercard_link_downstream elaborate with INTERCARD_BUS_WIDTH = 128 (CLK direction split per ADR-003) and the two-card pair wires up cleanly. closes #13 refs #11, #14 Authored by Agent 2 (FPGA Hardware). Signed-off-by: Marcos <m@pop.coop> Co-authored-by: Marcos <m@pop.coop>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #12.
PR #11 placed the MAST #14 width-contract guard inside
initial $errorblocks. Verilator
--lint-onlydoes not executeinitialblocks, so afuture violation of
INTERCARD_LANES * INTERCARD_LANE_WIDTH == 128wouldhave lint-passed silently. Today the contract is satisfied, so the gap was
masked.
Option chosen: A (bare
$errorin generate body)The issue offered three options (A: bare
$errorin generate, B: negative-width wire trick, C: switch lint script to
--binary). I tested Option Aagainst Verilator 5.048 first because it's the cleanest IEEE 1800-2012
construct and required no script changes.
Verilator 5.048 accepts
$errordirectly inside a generate-ifbodywithout
--assert, surfaces it as%Warning-USERERROR, and exitsnon-zero. Verified — no need to fall back to B or C.
Applied to BOTH sites the issue identified:
src/intercard_link.sv— production module's internal width-guard(lines ~80-92, formerly
initial begin if (...) $error ... end)verif/intercard_link/test_widths.sv— testbench's elaboration-timeguard (lines ~59-72, formerly
initial $errorin generate)The "OK" branch in
test_widths.svkeepsinitial $displaybecausethat path is purely observational (only meaningful under
--binary,and under
--lint-onlyit's a harmless no-op).Verification (mandatory per issue)
1. Default-parameter case must still PASS
2. Contract violation must FAIL (the crisp proof issue #12 asked for)
I created
verif/intercard_link/test_widths_violation.sv(NOT committed,deleted before commit) instantiating
intercard_link #(.INTERCARD_LANES(8), .INTERCARD_LANE_WIDTH(32))—8 * 32 = 256 != 128, contract broken.Captured violation evidence:
%Warning-USERERROR: src/intercard_link.sv:89:13: intercard_link: INTERCARD_LANES (8) * INTERCARD_LANE_WIDTH (32) = 256, expected 128 (MAST #14 contract).The temp file was deleted before commit; not in the diff.
Out of scope (per issue)
No changes to: connector pinout, SV stub body, ADRs, KiCad files,
run_lint.sh. Only the two files with the broken guard.Test plan
bash verif/intercard_link/run_lint.shexits 0 on default paramsverilator --lint-onlyexits non-zero when contract is violated(LANES=8) — captured above
🛠 Stream 2 / Agent 2 (FPGA Hardware)