/* * mdio.v * * Copyright (C) 2018, 2019 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * function: MDIO HW driver / bit banger * */ `timescale 1ns /10ps module mdio( input rstn, input mdc, // clock // mdio input mdi, output reg mdo, output reg mdo_oe, // mdio_controller interface input rwn, // read / write not input [4:0] phy_addr, input [4:0] reg_addr, input [15:0] di, input ld, // load, start output reg run, output done, // mdio xfer is done // output port to converter output reg [15:0] dout, output we ); reg [5:0] state; reg [15:0] d; // run state machine // run starts when ld is asserted and finishes when done is asserted always @(negedge mdc or negedge rstn) begin if ( !rstn) run <= 1'b0; else if ( ld ) run <= 1'b1; else if ( done ) run <= 1'b0; end // increment state during run always @(negedge mdc or negedge rstn) begin if ( !rstn ) state <= 0; else if ( ld ) state <= 0; else if ( run ) state <= state + 1; end // register data for MDIO TX always @(negedge mdc or negedge rstn) begin if ( !rstn ) d <= 0; else if ( ld ) d <= di; end // done combo logic assign done = ( state == 6'd36 ) ? 1'b1 : 1'b0; // only assert we on mdio reads assign we = done && rwn; // mdo_oe logic always @(*) begin mdo_oe = 1'b0; if ( run && !rwn && state < 6'd36 ) mdo_oe = 1'b1; else if ( run && rwn && state < 6'd14 ) mdo_oe = 1'b1; end // mdo mux always @(*) begin mdo = 1'b0; casez(state) 6'h0: mdo = 1'b0; 6'h1: mdo = 1'b1; 6'h2: if (rwn) mdo = 1'b1; else mdo = 1'b0; 6'h3: if (rwn) mdo = 1'b0; else mdo = 1'b1; 6'h4: mdo = phy_addr[4]; 6'h5: mdo = phy_addr[3]; 6'h6: mdo = phy_addr[2]; 6'h7: mdo = phy_addr[1]; 6'h8: mdo = phy_addr[0]; 6'h9: mdo = reg_addr[4]; 6'ha: mdo = reg_addr[3]; 6'hb: mdo = reg_addr[2]; 6'hc: mdo = reg_addr[1]; 6'hd: mdo = reg_addr[0]; 6'he: mdo = 1'b1; // it's a don't care if we're doing a read 6'hf: mdo = 1'b0; 6'h1?: mdo = rwn ? 1'b1 : d[31-state]; // msbit default: mdo = 1'b1; endcase end /* * capture mdi on rising edge * data out shift register * ( shift into it from mdio to parallel reg ) */ always @(posedge mdc or negedge rstn) begin if ( !rstn ) dout <= 16'h0; else if ( rwn && run && ( state >= 16 && state <= 31 )) dout <= { dout[14:0], mdi }; end endmodule