`default_nettype none // IEEE 802.3 Clause-22 management interface. MDC is stopped low between // commands. A read releases MDIO for both turnaround bits and all data bits. module mdio_master #( parameter int unsigned MDC_DIV = 25 ) ( input wire logic clk, input wire logic rst, input wire logic start, input wire logic command_write, input wire logic [4:0] phy_address, input wire logic [4:0] register_address, input wire logic [15:0] write_data, output logic busy, output logic done, output logic [15:0] read_data, output logic read_valid, output logic mdc, input wire logic mdio_i, output logic mdio_o, output logic mdio_oe ); localparam int unsigned DIV_W = (MDC_DIV < 2) ? 1 : $clog2(MDC_DIV); logic [DIV_W-1:0] div_count; logic [5:0] bit_index; logic [63:0] frame_bits; logic [15:0] read_shift; logic read_command_q; initial begin if (MDC_DIV < 2) $error("MDC_DIV must be at least two"); end always_comb begin mdio_o = frame_bits[bit_index]; mdio_oe = busy; if (busy && read_command_q && bit_index <= 6'd17) mdio_oe = 1'b0; end always_ff @(posedge clk) begin if (rst) begin busy <= 1'b0; done <= 1'b0; read_valid <= 1'b0; read_data <= 16'd0; read_shift <= 16'd0; mdc <= 1'b0; div_count <= '0; bit_index <= 6'd63; frame_bits <= 64'hffffffff_6000_0000; read_command_q <= 1'b0; end else begin done <= 1'b0; read_valid <= 1'b0; if (!busy) begin mdc <= 1'b0; div_count <= '0; if (start) begin busy <= 1'b1; bit_index <= 6'd63; read_shift <= 16'd0; read_command_q <= !command_write; frame_bits[63:32] <= 32'hffffffff; frame_bits[31:30] <= 2'b01; frame_bits[29:28] <= command_write ? 2'b01 : 2'b10; frame_bits[27:23] <= phy_address; frame_bits[22:18] <= register_address; frame_bits[17:16] <= 2'b10; frame_bits[15:0] <= write_data; end end else if (div_count == MDC_DIV-1) begin div_count <= '0; if (!mdc) begin // Rising MDC: PHY sources read data; master samples it. mdc <= 1'b1; if (read_command_q && bit_index <= 6'd15) read_shift <= {read_shift[14:0], mdio_i}; end else begin // Falling MDC: move to the next bit or finish the command. mdc <= 1'b0; if (bit_index == 6'd0) begin busy <= 1'b0; done <= 1'b1; if (read_command_q) begin read_data <= read_shift; read_valid <= 1'b1; end end else bit_index <= bit_index - 6'd1; end end else div_count <= div_count + {{(DIV_W-1){1'b0}},1'b1}; end end endmodule `default_nettype wire