Repository navigation
feat(fpga): eth_rx.t27 -- receive path of the Ethernet job link (E2a) - #8337
Closed
dmitrii-f-t27 wants to merge 3 commits into
Closed
dmitrii-f-t27 wants to merge 3 commits into
dmitrii-f-t27 wants to merge 3 commits into
Conversation
#8333) E2a of #8327. EthRx takes the GMII-style byte stream of one PHY (valid flag plus one byte per 125 MHz clock) and accepts exactly the UDP datagrams addressed to the TRI-NET node on the AX7203: destination MAC 02:27:00:00:00:01 or broadcast, IPv4 without fragments and with a correct header checksum, UDP to port 27028 at 192.168.1.227 or a broadcast address, a UDP length that agrees with the IP length, and an FCS that leaves the CRC-32 residue 0xDEBB20E3. Payload bytes go out one per clock with their index; the sender's MAC, IP and port are latched for the reply; every drop has its own reason code and is counted. The canonical test frame was built independently in Python (struct, the RFC 791 sum, zlib.crc32): IPv4 checksum 0xB620, FCS 30 2B 9B E2, payload sum 2199. Timing: every sum the verdict needs is settled at byte 40, and the counters move one clock after the verdict, so no adder chain follows the last byte. Out of context on xc7a200tfbg484-2 (yosys 0.69, nextpnr-xilinx 0.9.7) the module closes 125 MHz on three seeds: 139.51, 138.99 and 128.80 MHz.
This was referenced Oct 9, 2026
dmitrii-f-t27
enabled auto-merge (squash)
October 9, 2026 20:22
This was referenced Oct 9, 2026
Contributor
…8333) t27b-native-ratchet: specs/fpga/eth_rx.t27 passes t27b and the reference, so it gets a pass row, sorted after eth_crc.t27. duplicate-bodies: crc_step and crc_byte in EthRx are byte-identical to EthBeacon on purpose. `use fpga::eth_beacon::{crc_byte}` typechecks and the 7 tests pass, but gen-verilog emits the call without the function body, so the RTL of EthRx would not elaborate. Each RTL module must carry its own copy until the Verilog backend inlines imported functions; the baseline records the two groups.
…ot wrap (#8333) pay_end and need_len were UDP_POS + udp_len (+ 4) in 16 bits. A 70-byte frame with IP length 0xFFFF and UDP length 0xFFEB passed the IP/UDP length agreement (65535 - 20 = 65515), then need_len wrapped to 17 and the frame was accepted with ok = 1 and no payload. The generated RTL did exactly that; the Zig test runner panicked with an integer overflow on the same sum. - Byte 39 rejects a UDP length above MAX_UDP = 1480 (a 1518-byte frame) as reason 10. - pay_end_of and need_of add only below the bound, so the sums are safe on every clock, whatever udp_len holds then; above it the need is 65535, past POS_CAP. - New test: IP 0xFFFF / UDP 0xFFEB, a padded 2-byte datagram (accepted), UDP 1480 in 1518 bytes (accepted), UDP 1481, IP length 19 with UDP 0xFFFF, and the canonical frame cut to 64 bytes on the wire while it needs 70 (reason 7). test-report 8/8, 0 vacuous; seal re-saved with the tree's t27c 0.5.2, verify MATCH.
2 of 3 tasks
Contributor
PR DashboardGenerated at: 2026-10-09 21:16:27 UTC
Summary
Seal Status
|
4 tasks done
Collaborator
Author
|
Replaced by #8370: the same four files as one commit on current master (ef75683). Two commits here had |
dmitrii-f-t27
added a commit
that referenced
this pull request
Oct 9, 2026
…#8370) * feat(fpga): eth_rx.t27 -- receive path of the Ethernet job link (E2a) Closes #8333 EthRx takes the GMII-style byte stream of one PHY and accepts exactly the UDP datagrams to the TRI-NET node (MAC 02:27:00:00:00:01 or broadcast, IPv4 192.168.1.227/.255/255.255.255.255, port 27028), with a UDP length of at most 1480 that agrees with the IP length, at least 64 bytes and as many as the UDP header needs, and a CRC-32 residue 0xDEBB20E3. Each drop has its own reason. The UDP length is bounded before any sum uses it: 34 + 0xFFEB would wrap to 13 in 16 bits and let a 70-byte frame pass the length check (found in review of #8337; the generated RTL accepted that frame with ok = 1). - test-report 8/8, 0 vacuous; seal from this tree's t27c 0.5.2, verify MATCH. - t27b ledger: one pass row. Duplicate baseline: crc_step and crc_byte are EthBeacon's on purpose, because gen-verilog emits an imported call without its body. Replaces #8337, whose commits lacked the issue keyword L1 TRACEABILITY needs. * ci(elab): eth_rx elaborates clean, so it gets a 0 row Refs #8333 fpga-conformance (tools/check_elab_ratchet.py) read the new EthRx module as NEW with 0 errors and no row, as it did for eth_beacon and rgmii_sniff with #7788. iverilog elaborates the gen-verilog output without an error.
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.
Closes #8333
Part of #8327 (E2: TRI-NET node over UDP on the AX7203). Refs #6655.
Pull Request Checklist
Closes #8333t27c test-report specs/fpga/eth_rx.t27passes 8/8t27c seal specs/fpga/eth_rx.t27 --save, then--verifyall MATCHDescription
specs/fpga/eth_rx.t27(moduleEthRx) is the receive half of the Ethernet job link. It takes the GMII-style byte stream of one PHY, a valid flag plus one byte per 125 MHz clock, and accepts exactly the UDP datagrams addressed to the node:02:27:00:00:00:01(the addressEthBeaconsends from) or broadcast;0xDEBB20E3.Payload bytes come out one per clock with their index while the frame is still clean; the consumer buffers them and uses them only when the frame ends with
ok = 1. The sender's MAC, IP and port are latched for the reply (E2c), so a host needs no ARP entry: it may send to the broadcast address. Each drop has its own reason code (1 MAC, 2 EtherType/version, 3 protocol, 4 destination IP, 5 port, 6 FCS, 7 runt/truncated, 8 IPv4 checksum, 9 fragment, 10 UDP length) and is counted.Turning RGMII nibbles into these bytes on silicon is E2d and is not in this PR.
Changes
specs/fpga/eth_rx.t27-- new spec, 8 tests and 1 invariant..trinity/seals/fpga_EthRx.json-- seal built witht27c 0.5.2from this tree.docs/reports/t27b_expectations.json-- one pass row forspecs/fpga/eth_rx.t27.tools/duplicate_bodies_baseline.txt--crc_stepandcrc_bytecopied fromEthBeaconon purpose:use fpga::eth_beacon::{crc_byte}typechecks and passes the tests, butgen-verilogemits the call without the function body, so the RTL ofEthRxwould not elaborate.Fix in 8819b10: the length sums wrapped (review finding)
pay_endandneed_lenwereUDP_POS + udp_len (+ 4)in 16 bits. A 70-byte frame with IP lengthFF FF, UDP lengthFF EBand a correct IPv4 checksum (B6 54) and FCS passed the length agreement (65535 - 20 = 65515);need_lenwrapped to 17 and the frame was accepted withok = 1and no payload byte. Now byte 39 rejects a UDP length above 1480 as reason 10, andpay_end_of/need_ofadd only below that bound, so the sums are safe on every clock whateverudp_lenholds (above it the need is 65535, pastPOS_CAP).integer overflowatUDP_POS + udp_lenTesting
test-report: 8/8 pass, 0 vacuous. Invariant proved at comptime.seal --verify: all hashes MATCH; tests 8/8 recorded in the seal.struct, the RFC 791 sum andzlib.crc32before the spec: IPv4 checksum 0xB620, FCS30 2B 9B E2, payload sum 2199. A test asserts that the spec's own builder andcrc_bytereproduce exactly these values.gen-verilogoutput (sha2562abc54db..., equal to the seal'sgen_hash_verilog) runs in Icarus Verilog 13.0 against 20 frames built independently in Python (struct, RFC 791,zlib.crc32): every reason code 1..10, a flipped FCS bit, a runt, broadcast MAC and both broadcast IPs, a padded 2-byte datagram, UDP 1480 in a 1518-byte frame, UDP 1481, the two length-overflow frames and a 64-byte frame that needs 70. why, ok, and for accepted frames the payload count and byte sum all match. The testbench and generator are kept outside the repo (hand-written .v/.py need an owner-approved entry intools/policy/foreign-exceptions.txt); hashes: generator482c55df..., testbencha326e94e..., vectors39008e84....gen-verilogoutput, wrapped (not committed) so that all outputs reach one pin, onxc7a200tfbg484-2with yosys 0.69 (synth_xilinx -flatten -abc9 -nowidelut -nosrl) and nextpnr-xilinx 0.9.7: 1004 cells after the fix, post-route 145.01 / 149.50 / 142.53 MHz on seeds 1, 2, 3 against 125 MHz. The first version reached 85.5 MHz; the verdict now uses only comparisons (the IPv4 checksum fold and the required length are settled at byte 40) and the counters move one clock afterdone.Review Notes
t27c icarus-simulatedoes not run tests that callon_clock(Enable of unknown task on_clock); the same happens on master foreth_beacon.t27andrgmii_sniff.t27. The generated RTL is therefore checked with the separate testbench above.Checks that fail on master too (not caused by this PR)
has_atinspecs/tri/t27b/ast_walk.t27(#8258) has no ledger rowtools/dupe_scan.pyon origin/master fails with the same line/Users/playra/in.claude/skills/t27b-loop/SKILL.md(#8332)queen/keyed,math/constants, ...);eth_rx.t27is no longer among themspecs/math/constants.t27discards improved 386 to 349 (#8209) without a re-blessThis PR does not merge while they are red; they need their own fixes on master.
φ² + 1/φ² = 3 | TRINITY