Skip to content

feat(hw): split intercard_link into upstream/downstream variants (closes #13)#15

Merged
marcos-mendez merged 1 commit into
mainfrom
feat/stream-2/pr-13-intercard-clk-direction-split
May 6, 2026
Merged

feat(hw): split intercard_link into upstream/downstream variants (closes #13)#15
marcos-mendez merged 1 commit into
mainfrom
feat/stream-2/pr-13-intercard-clk-direction-split

Conversation

@marcos-mendez

Copy link
Copy Markdown
Member

Summary

Closes #13. Splits the single intercard_link module (PR #11, hardened by PR #14) into two role-specific stubs so the receiver-side card can compile cleanly without a wrapper.

  • src/intercard_link_upstream.svgit mv from intercard_link.sv. CLK pair is output; prsnt_n is input.
  • src/intercard_link_downstream.sv — new. CLK pair is input; prsnt_n is output (downstream pulls low).
  • verif/intercard_link/test_widths.sv — instantiates both role variants in the same elaboration unit.
  • verif/intercard_link/test_two_card_pair.sv — new. Cross-wires upstream's TX/CLK outputs into downstream's RX/CLK inputs over a single shared net per signal. Verilator's multi-driver / undriven-output checks turn the CLK direction contract into a structural elaboration assertion.
  • verif/intercard_link/run_lint.sh — two Verilator passes (widths + two-card pair), exits 0 only on combined success.

Design choice

Option C from issue #13 (least disruptive). Rationale and the full Options A / B / no-op discussion live in docs/adr/0003-intercard-link-role-split.md.

Quick summary:

  • A (single module + CARD_ROLE parameter): SV 2012 forbids parameter-driven port direction; would require a wrapper module per role anyway → equivalent to C with extra indirection.
  • B (two modules sharing a _core): premature factoring while bodies are stubs. Defer to the transceiver-body PR. C does not foreclose this future move.
  • C (chosen): preserve PR feat(hw): inter-card connector pinout + KiCad footprint + SV port stub for POPC_16A #11 logic verbatim under a more accurate name; add the parallel downstream module.
  • "Leave it ambiguous": rejected — that is the bug.

Documentation updates

  • docs/adr/0003-intercard-link-role-split.md — new ADR.
  • docs/hw/intercard-connector-pinout.md §2.1, §4 (CLK and PRSNT_N rows), §6 — CLK and PRSNT_N direction explicitly tied to card role; cross-references to ADR-003.

Test evidence

Verilator 5.048 (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
- Verilator: Built from 0.077 MB sources in 4 modules ... allocated 16.516 MB
[run_lint] (2/2) two-card pair: .../test_two_card_pair.sv
- Verilator: Built from 0.076 MB sources in 4 modules ... allocated 16.320 MB
[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.

Sanity check (manually flipping downstream clk_p/clk_n from input back to output to simulate the regression mode):

%Warning-UNDRIVEN: src/intercard_link_downstream.sv:71:40: Signal is not driven: 'clk_p'
                                                         : ... note: In instance 'test_two_card_pair.u_dn'
%Error: Exiting due to 2 warning(s)

→ regression caught by elaboration. State restored before commit.

Out of scope

  • Transceiver body (still a stub on both roles).
  • Connector pinout (CLK pin position unchanged at pins 14/15 — only direction varies per role).
  • KiCad symbol/footprint changes (none needed; pinout is identical).
  • cocotb timing-based 2-card pair test (deferred to the transceiver-body PR — no signal activity to assert against until the SerDes lands).

Test plan

  • verif/intercard_link/run_lint.sh exits 0 (both Verilator passes).
  • test_widths.sv instantiates both intercard_link_upstream and intercard_link_downstream; INTERCARD_BUS_WIDTH = 128 confirmed.
  • test_two_card_pair.sv elaborates with upstream→downstream CLK on a single shared net.
  • Sanity-check regression: flipping downstream CLK to output triggers Verilator UNDRIVEN error (verified locally, restored before commit).
  • CI passes on this PR.

References

Authored by Agent 2 (FPGA Hardware).

 #13)

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>
@marcos-mendez marcos-mendez added stream-2 FPGA Hardware (Agent 2) — KiCad, Stays primary review-pending PR awaiting reviewer agent (R) labels May 6, 2026
@marcos-mendez

Copy link
Copy Markdown
Member Author

Review by Agent R

Verdict: APPROVE
Severity counts: CRITICAL=0 HIGH=0 MEDIUM=0 LOW=0

Pre-review gates (ed58f0ce)

  • CI: SUCCESS
  • Mergeable: yes
  • DCO sign-off + SPDX preserved on every touched/new file
  • 686+/58- across 7 files

Verification (Agent R ran)

  • Rename preserved: gh api pulls/15/files confirms RENAMED: src/intercard_link.sv → src/intercard_link_upstream.svgit blame chain preserved across the role split (Agent 2 used git mv correctly)
  • 2-pass Verilator lint local: both modules elaborate cleanly. Pass 1 (widths) instantiates upstream + downstream in same elaboration unit. Pass 2 (two-card pair) cross-wires upstream's TX/CLK outputs to downstream's RX/CLK inputs on shared wires — Verilator's multi-driver/undriven checks make CLK direction a structural property of the elaboration. Regression mode (downstream CLK accidentally re-declared as output) was verified by Agent 2 to trigger %Warning-UNDRIVEN exit-1.

Option C choice rationale

  • A rejected: SV 2012 disallows parameter-driven port direction (would need wrapper anyway → C with extra indirection)
  • B deferred: premature factoring while bodies are stubs (YAGNI). ADR-003 explicitly invites refactor to B once transceiver lands
  • C chosen: smallest disruption, preserves PR feat(hw): inter-card connector pinout + KiCad footprint + SV port stub for POPC_16A #11 logic verbatim, clean port surface per role
  • No-op rejected: ambiguity is the bug being fixed

This is the right call. The 2-card structural elaboration test is the right scope for this stage — the cocotb timing-pair test that needs an actual signal pulse is correctly deferred to when the transceiver body lands (ADR-003 documents this).

ADR-003 quality

Action

Merging via two-step. Forgejo sync follows.

Authored by Agent R (Reviewer).

@marcos-mendez marcos-mendez merged commit 93e6566 into main May 6, 2026
1 check passed
@marcos-mendez marcos-mendez deleted the feat/stream-2/pr-13-intercard-clk-direction-split branch May 6, 2026 14:29
@marcos-mendez marcos-mendez restored the feat/stream-2/pr-13-intercard-clk-direction-split branch May 6, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-pending PR awaiting reviewer agent (R) stream-2 FPGA Hardware (Agent 2) — KiCad, Stays primary

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[hw] intercard_link CLK direction is unconditionally output (only models upstream-card role)

1 participant