framing_top: Drive TSTRB and TKEEP on the receive stream - #6
Open
dngr2 wants to merge 1 commit into
Open
Conversation
The always_comb driving rx_axis_req_o assigned .t.data, .tvalid, .t.last and .t.user, and never .t.strb or .t.keep. Struct members left unassigned in an always_comb infer a latch and carry no defined value. Those bits feed axi_stream_dw_upsizer, which builds the word-level TKEEP by concatenating the per-byte TKEEP it receives, so a consumer was told no bytes were valid in any word of any frame. The received data itself is correct; what is missing is the validity signalling that says how much of the final word belongs to the frame. Every accepted beat of this byte-wide stream carries one real data byte, so both signals simply track TVALID, gated by accept_frame_q like the neighbouring assignments. '1 is used rather than a sized replication so the width follows the stream if FramingDataWidth ever changes. .t.id and .t.dest are also unassigned, but are zero-width in every configuration in this repository, so they are left alone. Adds eth_rx_keep_tb: the same two-instance RGMII loopback as eth_tb, with one instance transmitting into the other, driven procedurally instead of with the randomized class-based drivers so it also runs on an open-source simulator. Against the unfixed RTL its two TKEEP checks fail while the data checks still pass, which is the distinction the bug turns on.
dngr2
force-pushed
the
fix/rx-drive-tstrb-tkeep
branch
from
August 16, 2026 02:47
85f1f02 to
bf5c062
Compare
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.
Found while investigating #5. Separate bug, receive side, and independent of that one — no shared code.
The problem
The
always_combatrtl/framing_top.sv:81-118drives the receive stream:.t.strband.t.keepare never assigned — not in that block, not anywhere else in the module, and there is no default assignment to the struct. Members left unassigned in analways_combinfer a latch and carry no defined value.They are not unused:
eth_top.sv:115takes this output ass_framing_rx_reqand feeds it toaxi_stream_dw_upsizerat line 155, which builds the word-levelTKEEPby concatenating the per-byteTKEEPit receives.The received data is correct. What is missing is the validity signalling — how much of the final word belongs to the frame. A consumer either ignores
TKEEPor reads bits that were never driven.Demonstrated end to end
Two
eth_top_synthinstances cross-connected over RGMII, one transmitting a broadcast frame into the other. Same simulation, same frame, only this change differs:aabbffffffffffffkeep=0x00keep=0xff0008112233445566keep=0x00keep=0xff000000000000bbaakeep=0x00keep=0xffkeep=0x00keep=0xffab83246b...(FCS)keep=0x00keep=0xffPadding and FCS are generated by the hardware, so the frame really did traverse the MAC and the RGMII pins.
The change
Both signals track
TVALID, gated byaccept_frame_qlike the neighbouring assignments, because every accepted beat of this byte-wide stream carries one real data byte.'1rather than a sized replication, so the width follows the stream ifFramingDataWidthever changes..t.idand.t.destare also unassigned. They are zero-width in every configuration here, so there is no hardware to latch and I left them alone rather than introduce zero-width assignments — say the word if you would rather they were driven too.The testbench
target/sim/src/eth_rx_keep_tb.sv, registered inBender.yml. Same loopback topology aseth_tb, but driven procedurally rather than with the randomized class-based drivers, so it runs on an open-source simulator as well as Questa and the checks name exact values.Against the unfixed RTL:
The data checks pass both before and after; only the
TKEEPchecks move. That is the distinction the bug turns on, and it is why the testbench checks both rather than just asserting a frame arrived.What I have not done
I have no Questa licence, so I ran this with Verilator 5.006 rather than through
eth.mk. The testbench uses no classes or randomization and should run unmodified under Questa, but I have not been able to confirm that, and the existingeth_tbis untouched.Building it required
axi_stream,register_interface(including the vendoredprim_subreg) andtech_cells_generic. Worth noting separately:Bender.lockpinsaxi_streamat472751f550e3918215603e21734fe0ece3c66f79, which is not reachable in the public repository — I fetched all refs before saying so — so I resolved against currentmaster. That is also raised in #5.