Practice
Dock RMII Receiver
At Kingfisher Dock, Pump 3 watches its network port through the two-bit RMII pins between the PHY chip and the FPGA.
The PHY delivers valid frames, but the monitor packs each group of two bits from the wrong starting point. Every field shifts out of place.
On a salt-streaked service laptop, the log fills with packet types that do not exist.
Your task: build the RMII receiver for the dock. Hunt for the real start-of-frame delimiter, 0xD5, before packing payload bytes.
Interface
| Port | Direction | Type | Description |
|---|---|---|---|
ref_clk_i | in | 1 bit | 50 MHz RMII reference clock and the only DUT clock |
rst_i | in | 1 bit | Synchronous reset (active high) |
crs_dv_i | in | 1 bit | High while the PHY delivers a frame |
rx_er_i | in | 1 bit | PHY damage flag for the current dibit |
rxd_i | in | 2-bit vector | One receive dibit per clock |
data_o | out | 8-bit vector | Latest payload byte, held |
valid_o | out | 1 bit | Payload byte ready (one-clock pulse) |
frame_start_o | out | 1 bit | First payload byte ready (one-clock pulse) |
frame_done_o | out | 1 bit | Recognized frame ended (one-clock pulse) |
bad_o | out | 1 bit | Frame damage detected (one-clock pulse) |
Behavior
- The receiver supports only 100 Mb/s RMII
- While
crs_dv_iis high, one dibit is sampled on each risingref_clk_iedge - The PHY changes its outputs after falling
ref_clk_iedges - The receiver samples
crs_dv_i,rx_er_i, andrxd_ionly on rising edges - In this bench,
crs_dv_istays high for the whole frame - Each byte contains four dibits, with the least significant dibit arriving first
- The first dibit fills bits 1 through 0, and the last dibit fills bits 7 through 6
- A wire frame contains seven
0x55preamble bytes, one0xD5SFD byte, then payload bytes - Preamble and SFD bytes are never emitted on
data_o - While
crs_dv_iis high, the receiver hunts through the dibit stream for the SFD pattern - Payload byte packing begins only after the SFD
- A burst that ends without an SFD produces no output pulses
- After such a burst, the next burst re-arms and hunts from a clean state
data_oupdates andvalid_opulses one clock after a payload byte's fourth dibit is sampledframe_start_opulses withvalid_ofor the first payload byte onlydata_oholds its latest value betweenvalid_opulses- One clock after
crs_dv_iis sampled low,frame_done_opulses - If the recognized frame ends mid-byte, the partial byte is discarded
- For a mid-byte ending,
bad_opulses together withframe_done_o - The first sampled high
rx_er_iafter the SFD makesbad_opulse on the next clock. During the preamble hunt the flag is ignored - Further high
rx_er_isamples in that frame do not create another receive-error pulse - A receive error does not stop byte delivery, and the frame still ends normally
rst_ihigh at a rising edge clears all outputs, state, partial data, and recorded errors- After reset, the receiver is idle and hunting for an SFD
What the bench checks
- The testbench drives PHY pins after falling edges and checks every output cycle by cycle against a reference model
- It sends dibit patterns that expose shifted, reversed, or misaligned packing, four dibits per byte with the least significant dibit first
- It checks that preamble and SFD bytes never appear on
data_o - It ends a burst without an SFD and checks that no output pulses at all, then that the next burst is received cleanly
- It cuts a frame mid-byte and checks that
bad_ojoinsframe_done_oand the partial byte is discarded - It samples
rx_er_ihigh twice in one frame and checks a single delayedbad_opulse while bytes keep flowing - It replays SFD-shaped noise while the carrier is down and checks that the receiver ignores it
- It resets mid-frame and checks that all state clears with no
frame_done_opulse
Constraints
TIMING: everything is synchronous to
ref_clk_i, the 50 MHz RMII reference clock. Inputs are sampled on rising edges, and all outputs are registered.
SCOPE: this covers 100 Mb/s RMII with simplified carrier behavior. FCS bytes emerge as ordinary payload, with no CRC checking. Other PHY pins and speeds are out of scope.
Do not add ports.
Click Run to execute your code. Output will appear here.