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

PortDirectionTypeDescription
clk_iin1 bitSystem clock
rst_iin1 bitSynchronous reset, active high
start_iin1 bitRequest one register read
phy_addr_iin5-bit vectorPHY address
reg_addr_iin5-bit vectorClause 22 register address
mdio_in_iin1 bitMDIO wire as read by the host
mdc_oout1 bitManagement clock, idle low
mdio_out_oout1 bitHost drive value for the MDIO wire
mdio_oe_oout1 bitHigh while the host drives the MDIO wire
busy_oout1 bitHigh while a read is in progress
data_oout16-bit vectorLast successful register value, held
valid_oout1 bitSuccessful read complete, one-clock pulse
ta_err_oout1 bitInvalid turnaround detected, one-clock pulse

Behavior

  • A high start_i is accepted only while idle
  • phy_addr_i and reg_addr_i are captured at acceptance
  • Later address changes do not affect the running read
  • Any start_i assertion while busy_o is high is ignored
  • busy_o is high while the accepted read is in progress and low while idle
  • mdc_o uses four clk_i cycles per period: two low cycles followed by two high cycles
  • The host changes mdio_out_o and mdio_oe_o only while mdc_o is low
  • The PHY samples the wire on each rising mdc_o edge
  • The host samples mdio_in_i on rising mdc_o edges during turnaround checking and data capture
  • The host first drives 32 preamble bits, all ones
  • It then drives start field 01, read opcode 10, captured phy_addr_i, and captured reg_addr_i
  • Each address is sent most significant bit first
  • mdio_oe_o stays high from the first preamble bit through the last reg_addr_i bit
  • The host releases the wire for both turnaround slots by setting mdio_oe_o low
  • mdio_oe_o remains 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_err_o pulses once and the read is discarded
  • A discarded read never raises valid_o, never changes data_o, and returns the unit to idle
  • After a valid turnaround, the host captures 16 data bits from mdio_in_i, most significant bit first
  • One clk_i cycle after the sixteenth data bit is captured, data_o updates and valid_o pulses
  • valid_o and ta_err_o each stay high for exactly one clk_i cycle
  • data_o holds its last successful value between reads and throughout later reads
  • While idle, mdc_o is low, mdio_oe_o is low, and both pulse outputs are low
  • rst_i high on any rising clk_i edge cancels all progress and returns the unit to idle
  • Reset drives every output low, including data_o, 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 mdc_o pattern and all rising-edge sampling
  • It checks that mdio_oe_o falls before the first turnaround slot and stays low
  • It checks MSB-first data capture and the one-clock delay before data_o and valid_o update
  • It injects bad turnaround values and checks ta_err_o, unchanged data_o, and suppressed valid_o
  • It checks one-clock pulses and that data_o holds between successful reads
  • It asserts reset during different frame phases and checks that every output clears

Constraints

TIMING: everything is synchronous to clk_i; inputs are sampled on rising edges, and all outputs are registered.

SCOPE: 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_o, mdio_oe_o, and mdio_in_i to the shared pin.

Do not add ports.

Loading editor...

Click Run to execute your code. Output will appear here.