FPGA模块——以太网MDIO读写
- MDIO接口介绍
- MDIO接口代码
- (1)MDIO接口驱动代码
- (2)使用MDIO驱动的代码
MDIO接口介绍
MDIO是串行管理接口。MAC 和 PHY 芯片有一个配置接口,即 MDIO 接口,可以配置 PHY 芯片的工作模式以及获取 PHY 芯片的若干状态信息。
1.MDIO部分的接口结构
2.千兆以太网在接口上兼容百兆和十兆以太网。
3.YT8511 是一个千兆以太网物理层收发器,支持 1000/100/10Mbps 通信速率,该芯片内部的参数可以通过MDIO接口进行配置。
MDIO接口代码
MDIO接口主要是控制三根接口线,进行驱动和读写。
(1)MDIO接口驱动代码
mdio_dri文件:输入一些读写开始等等控制信号,输出读到的数据和控制芯片的时钟
module mdio_dri #(parameter PHY_ADDR = 5'b00100,//PHY地址parameter CLK_DIV = 6'd10 //分频系数)(input clk , //时钟信号input rst_n , //复位信号,低电平有效input op_exec , //触发开始信号input op_rh_wl , //低电平写,高电平读input [4:0] op_addr , //寄存器地址input [15:0] op_wr_data, //写入寄存器的数据output reg op_done , //读写完成output reg [15:0] op_rd_data, //读出的数据output reg op_rd_ack , //读应答信号 0:应答 1:未应答output reg dri_clk , //驱动时钟output reg eth_mdc , //PHY管理接口的时钟信号inout eth_mdio //PHY管理接口的双向数据信号);//parameter define
localparam st_idle = 6'b00_0001; //空闲状态
localparam st_pre = 6'b00_0010; //发送PRE(前导码)
localparam st_start = 6'b00_0100; //开始状态,发送ST(开始)+OP(操作码)
localparam st_addr = 6'b00_1000; //写地址,发送PHY地址+寄存器地址
localparam st_wr_data = 6'b01_0000; //TA+写数据
localparam st_rd_data = 6'b10_0000; //TA+读数据//reg define
reg [5:0] cur_state ;
reg [5:0] next_state;reg [5:0] clk_cnt ; //分频计数
reg [15:0] wr_data_t ; //缓存写寄存器的数据
reg [4:0] addr_t ; //缓存寄存器地址
reg [6:0] cnt ; //计数器
reg st_done ; //状态开始跳转信号
reg [1:0] op_code ; //操作码 2'b01(写) 2'b10(读)
reg mdio_dir ; //MDIO数据(SDA)方向控制
reg mdio_out ; //MDIO输出信号
reg [15:0] rd_data_t ; //缓存读寄存器数据//wire define
wire mdio_in ; //MDIO数据输入
wire [5:0] clk_divide ; //PHY_CLK的分频系数assign eth_mdio = mdio_dir ? mdio_out : 1'bz; //控制双向io方向
assign mdio_in = eth_mdio; //MDIO数据输入
//将PHY_CLK的分频系数除以2,得到dri_clk的分频系数,方便对MDC和MDIO信号操作
assign clk_divide = CLK_DIV >> 1;//分频得到dri_clk时钟
always @(posedge clk or negedge rst_n) beginif(!rst_n) begindri_clk <= 1'b0;clk_cnt <= 1'b0;endelse if(clk_cnt == clk_divide[5:1] - 1'd1) beginclk_cnt <= 1'b0;dri_clk <= ~dri_clk;endelseclk_cnt <= clk_cnt + 1'b1;
end//产生PHY_MDC时钟
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n)eth_mdc <= 1'b1;else if(cnt[0] == 1'b0)eth_mdc <= 1'b1;else eth_mdc <= 1'b0;
end//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;elsecur_state <= next_state;
end //组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;case(cur_state)st_idle : beginif(op_exec)next_state = st_pre;else next_state = st_idle; end st_pre : beginif(st_done)next_state = st_start;elsenext_state = st_pre;endst_start : beginif(st_done)next_state = st_addr;elsenext_state = st_start;endst_addr : beginif(st_done) beginif(op_code == 2'b01) //MDIO接口写操作 next_state = st_wr_data;elsenext_state = st_rd_data; //MDIO接口读操作 endelsenext_state = st_addr;endst_wr_data : beginif(st_done)next_state = st_idle;elsenext_state = st_wr_data;end st_rd_data : beginif(st_done)next_state = st_idle;elsenext_state = st_rd_data;end default : next_state = st_idle;endcaseend//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n) begincnt <= 5'd0;op_code <= 1'b0;addr_t <= 1'b0;wr_data_t <= 1'b0;rd_data_t <= 1'b0;op_done <= 1'b0;st_done <= 1'b0; op_rd_data <= 1'b0;op_rd_ack <= 1'b1;mdio_dir <= 1'b0;mdio_out <= 1'b1;endelse beginst_done <= 1'b0 ; cnt <= cnt +1'b1 ; case(cur_state)st_idle : beginmdio_out <= 1'b1; mdio_dir <= 1'b0; op_done <= 1'b0; cnt <= 7'b0; if(op_exec) beginop_code <= {op_rh_wl,~op_rh_wl}; //OP_CODE: 2'b01(写) 2'b10(读) addr_t <= op_addr;wr_data_t <= op_wr_data;op_rd_ack <= 1'b1;end end st_pre : begin //发送前导码:32个1bit mdio_dir <= 1'b1; //切换MDIO引脚方向:输出mdio_out <= 1'b1; //MDIO引脚输出高电平if(cnt == 7'd62) st_done <= 1'b1;else if(cnt == 7'd63)cnt <= 7'b0;end st_start : begincase(cnt)7'd1 : mdio_out <= 1'b0; //发送开始信号 2'b017'd3 : mdio_out <= 1'b1; 7'd5 : mdio_out <= op_code[1]; //发送操作码7'd6 : st_done <= 1'b1;7'd7 : beginmdio_out <= op_code[0];cnt <= 7'b0; end default : ;endcaseend st_addr : begincase(cnt)7'd1 : mdio_out <= PHY_ADDR[4]; //发送PHY地址7'd3 : mdio_out <= PHY_ADDR[3];7'd5 : mdio_out <= PHY_ADDR[2];7'd7 : mdio_out <= PHY_ADDR[1]; 7'd9 : mdio_out <= PHY_ADDR[0];7'd11: mdio_out <= addr_t[4]; //发送寄存器地址7'd13: mdio_out <= addr_t[3];7'd15: mdio_out <= addr_t[2];7'd17: mdio_out <= addr_t[1]; 7'd18: st_done <= 1'b1;7'd19: beginmdio_out <= addr_t[0]; cnt <= 7'd0;end default : ;endcase end st_wr_data : begincase(cnt)7'd1 : mdio_out <= 1'b1; //发送TA,写操作(2'b10)7'd3 : mdio_out <= 1'b0;7'd5 : mdio_out <= wr_data_t[15];//发送写寄存器数据7'd7 : mdio_out <= wr_data_t[14];7'd9 : mdio_out <= wr_data_t[13];7'd11: mdio_out <= wr_data_t[12];7'd13: mdio_out <= wr_data_t[11];7'd15: mdio_out <= wr_data_t[10];7'd17: mdio_out <= wr_data_t[9];7'd19: mdio_out <= wr_data_t[8];7'd21: mdio_out <= wr_data_t[7];7'd23: mdio_out <= wr_data_t[6];7'd25: mdio_out <= wr_data_t[5];7'd27: mdio_out <= wr_data_t[4];7'd29: mdio_out <= wr_data_t[3];7'd31: mdio_out <= wr_data_t[2];7'd33: mdio_out <= wr_data_t[1];7'd35: mdio_out <= wr_data_t[0];7'd37: beginmdio_dir <= 1'b0;mdio_out <= 1'b1;end7'd39: st_done <= 1'b1; 7'd40: begincnt <= 7'b0;op_done <= 1'b1; //写操作完成,拉高op_done信号 end default : ;endcase endst_rd_data : begincase(cnt)7'd1 : beginmdio_dir <= 1'b0; //MDIO引脚切换至输入状态mdio_out <= 1'b1;end7'd2 : ; //TA[1]位,该位为高阻状态,不操作 7'd4 : op_rd_ack <= mdio_in; //TA[0]位,0(应答) 1(未应答)7'd6 : rd_data_t[15] <= mdio_in; //接收寄存器数据7'd8 : rd_data_t[14] <= mdio_in;7'd10: rd_data_t[13] <= mdio_in;7'd12: rd_data_t[12] <= mdio_in;7'd14: rd_data_t[11] <= mdio_in;7'd16: rd_data_t[10] <= mdio_in;7'd18: rd_data_t[9] <= mdio_in;7'd20: rd_data_t[8] <= mdio_in;7'd22: rd_data_t[7] <= mdio_in;7'd24: rd_data_t[6] <= mdio_in;7'd26: rd_data_t[5] <= mdio_in;7'd28: rd_data_t[4] <= mdio_in;7'd30: rd_data_t[3] <= mdio_in;7'd32: rd_data_t[2] <= mdio_in;7'd34: rd_data_t[1] <= mdio_in;7'd36: rd_data_t[0] <= mdio_in;7'd39: st_done <= 1'b1;7'd40: beginop_done <= 1'b1; //读操作完成,拉高op_done信号 op_rd_data <= rd_data_t;rd_data_t <= 16'd0;cnt <= 7'd0;enddefault : ;endcase end default : ;endcase end
end endmodule
(2)使用MDIO驱动的代码
mdio_ctrl文件:对寄存器进行读写配置,主要还是读取状态,用于显示
1.基本控制寄存器地址:0x00
代码里面配置为16’h9140 即1001_0001_0100_0000
2.基本状态寄存器地址:0x01
用来读出转态信息
3.特定状态寄存器地址:0x11
module mdio_ctrl(input clk ,input rst_n ,input soft_rst_trig , //软复位触发信号input op_done , //读写完成input [15:0] op_rd_data , //读出的数据input op_rd_ack , //读应答信号 0:应答 1:未应答output reg op_exec , //触发开始信号output reg op_rh_wl , //低电平写,高电平读output reg [4:0] op_addr , //寄存器地址output reg [15:0] op_wr_data , //写入寄存器的数据output [1:0] led //LED灯指示以太网连接状态);//reg define
reg rst_trig_d0;
reg rst_trig_d1;
reg rst_trig_flag; //soft_rst_trig信号触发标志
reg [23:0] timer_cnt; //定时计数器
reg timer_done; //定时完成信号
reg start_next; //开始读下一个寄存器标致
reg read_next; //处于读下一个寄存器的过程
reg link_error; //链路断开或者自协商未完成
reg [2:0] flow_cnt; //流程控制计数器
reg [1:0] speed_status; //连接速率 //wire define
wire pos_rst_trig; //soft_rst_trig信号上升沿//采soft_rst_trig信号上升沿
assign pos_rst_trig = ~rst_trig_d1 & rst_trig_d0;
//未连接或连接失败时led赋值00
// 01:10Mbps 10:100Mbps 11:1000Mbps 00:其他情况
assign led = link_error ? 2'b00: speed_status;
//对soft_rst_trig信号延时打拍
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginrst_trig_d0 <= 1'b0;rst_trig_d1 <= 1'b0;endelse beginrst_trig_d0 <= soft_rst_trig;rst_trig_d1 <= rst_trig_d0;end
end//定时计数
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintimer_cnt <= 1'b0;timer_done <= 1'b0;endelse beginif(timer_cnt == 24'd1_000_000 - 1'b1) begintimer_done <= 1'b1;timer_cnt <= 1'b0;endelse begintimer_done <= 1'b0;timer_cnt <= timer_cnt + 1'b1;endend
end //根据软复位信号对MDIO接口进行软复位,并定时读取以太网的连接状态
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflow_cnt <= 3'd0;rst_trig_flag <= 1'b0;speed_status <= 2'b00;op_exec <= 1'b0; op_rh_wl <= 1'b0; op_addr <= 1'b0; op_wr_data <= 1'b0; start_next <= 1'b0; read_next <= 1'b0; link_error <= 1'b0;endelse beginop_exec <= 1'b0; if(pos_rst_trig) rst_trig_flag <= 1'b1; //拉高软复位触发标志case(flow_cnt)2'd0 : beginif(rst_trig_flag) begin //开始对MDIO接口进行软复位op_exec <= 1'b1; op_rh_wl <= 1'b0; op_addr <= 5'h00; op_wr_data <= 16'h9140; // Bit[15]=1'b1,表示软复位flow_cnt <= 3'd1;endelse if(timer_done) begin //定时完成,获取以太网连接状态op_exec <= 1'b1; op_rh_wl <= 1'b1; op_addr <= 5'h01; flow_cnt <= 3'd2;endelse if(start_next) begin //开始读下一个寄存器,获取以太网通信速度op_exec <= 1'b1; op_rh_wl <= 1'b1; op_addr <= 5'h11;flow_cnt <= 3'd2;start_next <= 1'b0; read_next <= 1'b1; endend 2'd1 : beginif(op_done) begin //MDIO接口软复位完成flow_cnt <= 3'd0;rst_trig_flag <= 1'b0;endend2'd2 : begin if(op_done) begin //MDIO接口读操作完成if(op_rd_ack == 1'b0 && read_next == 1'b0) //读第一个寄存器,接口成功应答,flow_cnt <= 3'd3; //读第下一个寄存器,接口成功应答else if(op_rd_ack == 1'b0 && read_next == 1'b1)begin read_next <= 1'b0;flow_cnt <= 3'd4;endelse beginflow_cnt <= 3'd0;endend end2'd3 : begin flow_cnt <= 3'd0; //链路正常并且自协商完成if(op_rd_data[5] == 1'b1 && op_rd_data[2] == 1'b1)beginstart_next <= 1;link_error <= 0;endelse beginlink_error <= 1'b1; end end3'd4: beginflow_cnt <= 3'd0;if(op_rd_data[15:14] == 2'b10)speed_status <= 2'b11; //1000Mbpselse if(op_rd_data[15:14] == 2'b01) speed_status <= 2'b10; //100Mbps else if(op_rd_data[15:14] == 2'b00) speed_status <= 2'b01; //10Mbpselsespeed_status <= 2'b00; //其他情况 endendcaseend
end endmodule