Practice
Plant PHY Register
Dunbar Water Works depends on an Ethernet link to watch pumps, filters, and alarms. Its controller reads the PHY chip's status registers through MDIO, a two-wire management port.
The host owns the shared data wire while it sends a request. It must then let go for both turnaround slots, so the PHY can answer.
The shipped reader kept driving through turnaround. It fought the PHY on every read, and the green link lamp stayed dark.
The control room's wall screen lost its remote view of the plant.
Your task: build mdio_reader. Release the wire on time, check the PHY's turnaround response, and trust the data only when that check passes.
Interface
| Port | Direction | Type | Description |
|---|---|---|---|
clk | in | 1 bit | System clock |
rst | in | 1 bit | Synchronous reset, active high |
start | in | 1 bit | Request one register read |
phy_addr | in | 5-bit vector | PHY address |
reg_addr | in | 5-bit vector | Clause 22 register address |
mdio_in | in | 1 bit | MDIO wire as read by the host |
mdc | out | 1 bit | Management clock, idle low |
mdio_out | out | 1 bit | Host drive value for the MDIO wire |
mdio_oe | out | 1 bit | High while the host drives the MDIO wire |
busy | out | 1 bit | High while a read is in progress |
data | out | 16-bit vector | Last successful register value, held |
valid | out | 1 bit | Successful read complete, one-clock pulse |
ta_err | out | 1 bit | Invalid turnaround detected, one-clock pulse |
Behavior
- A high
startis accepted only while idle phy_addrandreg_addrare captured at acceptance- Later address changes do not affect the running read
- Any
startassertion whilebusyis high is ignored busyis high while the accepted read is in progress and low while idlemdcuses fourclkcycles per period: two low cycles followed by two high cycles- The host changes
mdio_outandmdio_oeonly whilemdcis low - The PHY samples the wire on each rising
mdcedge - The host samples
mdio_inon risingmdcedges during turnaround checking and data capture - The host first drives 32 preamble bits, all ones
- It then drives start field
01, read opcode10, capturedphy_addr, and capturedreg_addr - Each address is sent most significant bit first
mdio_oestays high from the first preamble bit through the lastreg_addrbit- The host releases the wire for both turnaround slots by setting
mdio_oelow mdio_oeremains low throughout both turnaround slots and all 16 data bits- A healthy PHY drives the second turnaround slot to zero
- If that sampled bit is not zero,
ta_errpulses once and the read is discarded - A discarded read never raises
valid, never changesdata, and returns the unit to idle - After a valid turnaround, the host captures 16 data bits from
mdio_in, most significant bit first - One
clkcycle after the sixteenth data bit is captured,dataupdates andvalidpulses validandta_erreach stay high for exactly oneclkcycledataholds its last successful value between reads and throughout later reads- While idle,
mdcis low,mdio_oeis low, and both pulse outputs are low rsthigh on any risingclkedge cancels all progress and returns the unit to idle- Reset drives every output low, including
data, and no cancelled read resumes afterward
What the bench checks
- The testbench checks idle acceptance, captured addresses, and ignored requests while busy
- It checks all 64 frame bits, including preamble, fields, addresses, turnaround, and data
- It checks the two-low, two-high
mdcpattern and all rising-edge sampling - It checks that
mdio_oefalls before the first turnaround slot and stays low - It checks MSB-first data capture and the one-clock delay before
dataandvalidupdate - It injects bad turnaround values and checks
ta_err, unchangeddata, and suppressedvalid - It checks one-clock pulses and that
dataholds between successful reads - It asserts reset during different frame phases and checks that every output clears
Constraints
everything is synchronous to clk; inputs are sampled on rising edges, and all outputs are registered.
Clause 22 reads are the whole task. Writes, Clause 45, preamble suppression, and the physical bidirectional pin are out of scope. A board wrapper joins mdio_out, mdio_oe, and mdio_in to the shared pin.
Do not add ports.
Click Run to execute your code. Output will appear here.