大家好,欢迎来到IT知识分享网。
简介
MDC/MDIO,英文全称Management Data Clock和Management Data Input/Output,主要被应用于以太网的MAC和PHY层之间,用于MAC层器件通过读写寄存器来实现对PHY层器件的操作与管理。
结构说明
如下图所示为DMIC接线框图:
图1 MDIO接线图
MDIO协议是以太网标准IEEE802.3中专门用于MAC和PYH之间管理的串行接口总线,该接口主要用于MAC控制器对PYH层的状态读取和设置、获取链路状态,控制物理层协商等操作。
MDIO协议类似于IIC接线,采用2根总线进行通信,分别为MDC和MDIO信号线。分主从设备。其中主设备称作STA,从设备称作MDI,一个主设备可以对多个从设备进行命令读写操作。
如下图所示为以太网硬件结构图:
图2 以太网硬件结构图
如下图所示为RJ45 1000M网口说明:
图3 RJ45以太网接口说明
FPGA通过MDIO协议控制PHY芯片内部寄存器,而以太网接口只传输数据,数据内容再封装UDP/ARP等帧协议内容,该部分在以太网协议篇再做详细说明。
协议说明
在IEEE802.3协议中,把MDIO接口数据帧分为两种,一种是Clause22,另一种是Clause45。前者主要用于百兆千兆以太网,后者用于千兆以上的以太网。下面主要介绍Clause22格式。
MDIO 接口的读写通信协议如下图所示:
图4 CLAUSE22 数据帧协议
Preamble:32 位前导码,由 MAC 端发送 32 位逻辑“1”,用于同步 PHY 芯片。
ST(Start of Frame):2 位帧开始信号,用 01 表示。
OP(Operation Code):2 位操作码,读:10 写:01。
PHYAD(PHY Address):5 位 PHY 地址,用于表示与哪个 PHY 芯片通信,因此一个 MAC 上可以连 接多个 PHY 芯片。
REGAD(Register Address):5 位寄存器地址,可以表示共 32 位寄存器。
TA(Turnaround):2 位转向,在读命令中,MDIO 在此时由 MAC 驱动改为 PHY 驱动,在第一个 TA 位,MDIO 引脚为高阻状态,第二个 TA 位,PHY 将 MDIO 引脚拉低,准备发送数据;在写命令中,不需 要 MDIO 方向发生变化,MAC 固定输出 2’b10,随后开始写入数据。
DATA:16 位数据,在读命令中,PHY 芯片将读到的对应 PHYAD 的 REGAD 寄存器的数据写到 DATA 中;在写命令中,PHY 芯片将接收到的 DATA 写入 REGAD 寄存器中。需要注意的是,在 DATA 传 输的过程中,高位在前,低位在后。
IDLE:空闲状态,此时 MDIO 为无源驱动,处于高阻状态,但一般用上拉电阻使其上拉至高电平。
设计说明
本设计使用FPGA作为MAC设备,设计框图如下图所示:
图5 硬件设计连接图
FPGA作为MAC分命令接口和数据接口,命令接口按照标准千兆网Clause22协议控制PHY芯片,PHY芯片寄存器内容这里不做详细介绍,数据接口接收以太网数据帧,帧协议严格按照UDP或者ARP解析。
MDIO协议时序图如下图所示:
图6 MDIO 时序图
信号比较简单,时钟的上升沿采样,下降沿改变数据。
FPGA设计
仿真代码
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2024/07/19 14:21:13 // Design Name: // Module Name: tb_sdio // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module tb_sdio( ); reg sys_clk; reg sys_rst_n; reg touch_key; always #5 sys_clk = ~sys_clk; initial begin sys_clk = 'd0; sys_rst_n = 'd0; touch_key = 'd0; #2000 sys_rst_n = 'd1; #4000 touch_key = 'd1; #4000 touch_key = 'd0; end mdio_rw_test mdio_rw_test_inst( .sys_clk(sys_clk) , .sys_rst_n(sys_rst_n), .eth_mdc() , //PHY管理接口的时钟信号 .eth_mdio (), //PHY管理接口的双向数据信号 .eth_rst_n(), //以太网复位信号 .touch_key(touch_key), //触摸按键 .led() //LED连接速率指示 ); endmodule
FPGA顶层代码
module mdio_rw_test( input sys_clk , input sys_rst_n, //MDIO接口 output eth_mdc , //PHY管理接口的时钟信号 inout eth_mdio , //PHY管理接口的双向数据信号 output eth_rst_n, //以太网复位信号 input touch_key, //触摸按键 output [1:0] led //LED连接速率指示 ); //wire define wire op_exec ; //触发开始信号 wire op_rh_wl ; //低电平写,高电平读 wire [4:0] op_addr ; //寄存器地址 wire [15:0] op_wr_data ; //写入寄存器的数据 wire op_done ; //读写完成 wire [15:0] op_rd_data ; //读出的数据 wire op_rd_ack ; //读应答信号 0:应答 1:未应答 wire dri_clk ; //驱动时钟 //硬件复位 assign eth_rst_n = sys_rst_n; //MDIO接口驱动 mdio_dri #( .PHY_ADDR (5'h04), //PHY地址 3'b100 .CLK_DIV (6'd10) //分频系数 ) u_mdio_dri( .clk (sys_clk), .rst_n (sys_rst_n), .op_exec (op_exec ), .op_rh_wl (op_rh_wl ), .op_addr (op_addr ), .op_wr_data (op_wr_data), .op_done (op_done ), .op_rd_data (op_rd_data), .op_rd_ack (op_rd_ack ), .dri_clk (dri_clk ), .eth_mdc (eth_mdc ), .eth_mdio (eth_mdio ) ); //MDIO接口读写控制 mdio_ctrl u_mdio_ctrl( .clk (dri_clk), .rst_n (sys_rst_n ), .soft_rst_trig (touch_key ), .op_done (op_done ), .op_rd_data (op_rd_data), .op_rd_ack (op_rd_ack ), .op_exec (op_exec ), .op_rh_wl (op_rh_wl ), .op_addr (op_addr ), .op_wr_data (op_wr_data), .led (led ) ); endmodule
mdio_dri 代码
module mdio_dri #( parameter PHY_ADDR = 5'b00001,//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) begin if(!rst_n) begin dri_clk <= 1'b0; clk_cnt <= 1'b0; end else if(clk_cnt == clk_divide[5:1] - 1'd1) begin clk_cnt <= 1'b0; dri_clk <= ~dri_clk; end else clk_cnt <= clk_cnt + 1'b1; end //产生PHY_MDC时钟 always @(posedge dri_clk or negedge rst_n) begin if(!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) begin if(!rst_n) cur_state <= st_idle; else cur_state <= next_state; end //组合逻辑判断状态转移条件 always @(*) begin next_state = st_idle; case(cur_state) st_idle : begin if(op_exec) next_state = st_pre; else next_state = st_idle; end st_pre : begin if(st_done) next_state = st_start; else next_state = st_pre; end st_start : begin if(st_done) next_state = st_addr; else next_state = st_start; end st_addr : begin if(st_done) begin if(op_code == 2'b01) //MDIO接口写操作 next_state = st_wr_data; else next_state = st_rd_data; //MDIO接口读操作 end else next_state = st_addr; end st_wr_data : begin if(st_done) next_state = st_idle; else next_state = st_wr_data; end st_rd_data : begin if(st_done) next_state = st_idle; else next_state = st_rd_data; end default : next_state = st_idle; endcase end //时序电路描述状态输出 always @(posedge dri_clk or negedge rst_n) begin if(!rst_n) begin cnt <= 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; end else begin st_done <= 1'b0 ; cnt <= cnt +1'b1 ; case(cur_state) st_idle : begin mdio_out <= 1'b1; mdio_dir <= 1'b0; op_done <= 1'b0; cnt <= 7'b0; if(op_exec) begin op_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 : begin case(cnt) 7'd1 : mdio_out <= 1'b0; //发送开始信号 2'b01 7'd3 : mdio_out <= 1'b1; 7'd5 : mdio_out <= op_code[1]; //发送操作码 7'd6 : st_done <= 1'b1; 7'd7 : begin mdio_out <= op_code[0]; cnt <= 7'b0; end default : ; endcase end st_addr : begin case(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: begin mdio_out <= addr_t[0]; cnt <= 7'd0; end default : ; endcase end st_wr_data : begin case(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: begin mdio_dir <= 1'b0; mdio_out <= 1'b1; end 7'd39: st_done <= 1'b1; 7'd40: begin cnt <= 7'b0; op_done <= 1'b1; //写操作完成,拉高op_done信号 end default : ; endcase end st_rd_data : begin case(cnt) 7'd1 : begin mdio_dir <= 1'b0; //MDIO引脚切换至输入状态 mdio_out <= 1'b1; end 7'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: begin op_done <= 1'b1; //读操作完成,拉高op_done信号 op_rd_data <= rd_data_t; rd_data_t <= 16'd0; cnt <= 7'd0; end default : ; endcase end default : ; endcase end end endmodule
mdio_ctrl代码
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) begin if(!rst_n) begin rst_trig_d0 <= 1'b0; rst_trig_d1 <= 1'b0; end else begin rst_trig_d0 <= soft_rst_trig; rst_trig_d1 <= rst_trig_d0; end end //定时计数 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin timer_cnt <= 1'b0; timer_done <= 1'b0; end else begin if(timer_cnt == 24'd1_000_000 - 1'b1) begin timer_done <= 1'b1; timer_cnt <= 1'b0; end else begin timer_done <= 1'b0; timer_cnt <= timer_cnt + 1'b1; end end end //根据软复位信号对MDIO接口进行软复位,并定时读取以太网的连接状态 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin flow_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; end else begin op_exec <= 1'b0; if(pos_rst_trig) rst_trig_flag <= 1'b1; //拉高软复位触发标志 case(flow_cnt) 2'd0 : begin if(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; end else if(timer_done) begin //定时完成,获取以太网连接状态 op_exec <= 1'b1; op_rh_wl <= 1'b1; op_addr <= 5'h01; flow_cnt <= 3'd2; end else 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; end end 2'd1 : begin if(op_done) begin //MDIO接口软复位完成 flow_cnt <= 3'd0; rst_trig_flag <= 1'b0; end end 2'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; end else begin flow_cnt <= 3'd0; end end end 2'd3 : begin flow_cnt <= 3'd0; //链路正常并且自协商完成 if(op_rd_data[5] == 1'b1 && op_rd_data[2] == 1'b1)begin start_next <= 1; link_error <= 0; end else begin link_error <= 1'b1; end end 3'd4: begin flow_cnt <= 3'd0; if(op_rd_data[15:14] == 2'b10) speed_status <= 2'b11; //1000Mbps else 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; //10Mbps else speed_status <= 2'b00; //其他情况 end endcase end end endmodule
FPGA仿真结果
仿真结果如下:
图7 FPGA初始化仿真
按键信号touch_key为1时对MDIO进行软复位,软复位动作是对PHY芯片寄存器0写0x9140,bit[15]为1时代表软复位,其他值保留初始状态不变。
然后FPGA会定时读取PHY链接的情况并将链接状态通过LED显示,如下图所示:
图8 FPGA定时读取PHY链接状态时序图
定时读取0x01状态寄存器值,其中寄存器bit[2]代表链接状态。
如有疑问欢迎留言。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/115578.html







