verify: cocotb datapath suite + 13 RTL fixes across gmii_cdc / eth_mac_rx / mii_tx_saf - #2
Merged
Merged
Conversation
Backend-agnostic cocotb layer beside the existing Icarus TBs. Reusable lib: AXIS master with random bubbles + dropped-tlast, MII monitor with FCS check, boundary-weighted random frame generator, and a store-and-forward reference model + scoreboard. Pilot covers mii_tx_saf boundaries, merges, and oversized runs with seed-logged random cases. Mutation-checked: reintroducing the oversize wedge makes it fail. Wired into build_and_test.py as PHASE 2 (skips if cocotb absent). Uses cocotb's language-neutral sources= so the same Python retargets GHDL for the VHDL port.
The sim workflow already installs iverilog (cocotb's backend) and runs build_and_test.py, but without cocotb the new cocotb phase skipped silently. Add a pip install "cocotb>=2.0" step so the directed+randomized suite actually executes on push/PR.
Second module on the framework. New RX lib: GMII driver (FCS-corrupt + rx_er), backpressuring AXIS sink, and an eth_mac_rx reference model for filtering, terror, delivery-vs-drop, and stats. Directed + seed-logged random. Mutation-checked. run.py drives both suites.
Suite drives contiguous GMII frames on the sys clock and checks the paced media output byte-for-byte across 1G/100M/10M, plus a small-frame burst that stresses the committed-frame counter. It found two real bugs, now fixed: - paced-TX phantom frame: read pointer parked on the EOF word, so the next 100M/10M frame emitted a stale EOF byte and orphaned the real frame. Close-out now advances past EOF in the paced modes only. - committed-counter wrap: 4-bit counter aliased at 16 backed-up frames, wedging the media side. Widened to ADDR_WIDTH+1 bits (same class as mii_tx_saf). Both are mutation-checked. Docs updated.
RX tests: byte-exact delivery, rx_er alignment, slow-clock burst probe. Found and fixed two bugs: a multi-frame byte-drop (readout re-ran its cold-start align at each EOF, dropping byte 0 of later frames) and an rx_frames_pending 4-bit counter wrap. Both mutation-checked.
All mutation-checked: - eth_mac_rx: multicast hash admit used mac_chk[0], not mac_chk[40] (the I/G bit), so wrong frames passed under MCAST_HASH_FILTER=1. New mcast suite. - gmii_cdc: gmii_tx_er_in was dropped; added a per-byte error lane re-driving gmii_tx_er_out. - gmii_cdc: paced last byte held 1 cycle instead of a full interval.
The paced last-byte-hold fix makes the final byte occupy a full pace interval, so 10M tx_en spans N*K = 8*100 = 800 cycles, not the old (N-1)*K+1 = 701. The legacy TB hard-coded 701 (the bug); update it to ~800, matching the module's own design comment. The 100M TB already tolerated this (range covers 320).
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
Adds a directed + randomized cocotb verification suite for the MAC datapath, wired into CI and
build_and_test.pyas its own phase. The suite is language-neutral (sources passed viasources=), so it retargets to a VHDL port later without rewriting the testbenches. Building it out found — and this PR fixes — six real RTL bugs across three modules, every one mutation-proven by the suite that caught it.What's covered
mii_tx_safeth_mac_rxgmii_cdcBugs fixed (all mutation-checked)
gmii_cdcTX (paced store-and-forward):ADDR_WIDTH+1(15) bits.gmii_cdcRX (unpaced readout):rx_frames_pendingwrap — same class as above; a 4-bit counter aliased at 16 buffered frames and stalled the readout under a slow sys drain. Widened toADDR_WIDTH+1(13) bits.Found by a follow-up module audit (all three mutation-checked):
eth_mac_rxmulticast-hash filter gated on the wrong bit — the hash-admit term testedmac_chk[0](LSB of the last dst octet) instead ofmac_chk[40](the I/G bit), so underMCAST_HASH_FILTER=1a genuine group address with an even last octet was rejected and a unicast with an odd last octet + colliding bucket was admitted. Neweth_mac_rx_mcastbuild/suite.gmii_cdcTX error input dropped —gmii_tx_er_inwas never captured andgmii_tx_er_outwas hard-wired 0. Added a per-byte error lane (9→10-bit FIFO word) that re-drivesgmii_tx_er_out, symmetric with the RXrx_erpath.gmii_cdcpaced last byte held 1 cycle — at 100M/10M the final byte occupied a single media cycle instead of the full pace interval; now it closes out at the nextpace_tick, so the last byte gets its fullperiod.Robustness hardening (
eth_mac_rx_robustsuite, all mutation-checked):terror'd but always starts and terminates cleanly, so it can't merge into the next frame.terrorinstead of as a clean frame with a garbage FCS.byte_cntsaturates at 0x3FFF — a > 16383-byte frame can no longer wrap the counter and inject a phantom SOF that corrupts the following frame.rx_eron a preamble/SFD byte is now reported (terror+stat_err_align); it was only sampled inS_DATA.mii_tx_safelaboration guard ($finishifMAX_FRAME >= FIFO_DEPTH) — this caught a real latent bug:eth_mac_sysfixed the MII SAF FIFO at 4096 whileMAX_FRAME=9018, so a 4096–9018 byte MII TX frame would hit the permanent wedge the oversize cap exists to prevent.eth_mac_sysnow derives that FIFO width fromMAX_FRAME($clog2), so it holds one whole frame and a standard (1518) build stays small — jumbo cost is paid only when jumbo is built.(The two
mii_tx_saffixes the suite also re-validates — committed-counter wrap and the oversize/uncommitted-frame wedge — landed earlier; it now guards them as regressions. Also updatedtb_gmii_cdc_10m, whose pacing assertion hard-coded the pre-fix 1-cycle last byte.)Verification
mii_tx_saf5/5,eth_mac_rx5/5,eth_mac_rx_robust4/4,eth_mac_rx_mcast3/3,gmii_cdc10/10; plus the full legacy Icarus phase.iverilog -Wallclean on the changed RTL.sim.yml, withcocotb>=2.0installed) and locally viapython sim/cocotb/run.py.Notes