当前位置:首页 > 编程笔记 > 正文
已解决

以太网实验1.mdio接口读写实验

来自网友在路上 171871提问 提问时间:2023-11-04 21:35:05阅读次数: 71

最佳答案 问答题库718位专家为你答疑解惑

以太网常见硬件组成:

fpga首先发送数据到经过udp层、ip层以及mac层的封装
主要有MAC控制器、PHY芯片、网络变压器和RJ45接头组成,有的系统会有DMA控制。一般的系统中CPU和MAC以及DMA控制器都是集成在一块芯片上的,为了节省空间简化设计,很多时候网口的变压器和RJ45的接头集成在一起。
MAC 及 PHY 工作在 OSI 七层模型的数据链路层和物理层。
mii接口(Media Independent Interface):
MII 接口提供了 MAC 与 PHY 之间、PHY 与 STA(Station Management)之间的互联技术。
MII 接口主要包括四个部分。一是从 MAC 层到 PHY 层的发送数据接口,二是从 PHY 层到 MAC 层的接收数据接口,三是从PHY 层到 MAC 层的状态指示信号,四是 MAC 层和 PHY 层之间传送控制和状态信息的 MDIO 接口。

mdio接口

MDIO为双向信号,用来在MAC和PHY芯片之间的传递控制和状态信息。写寄存器MAC驱动,读寄存器时PHY驱动。
数据传输时先传高位(MSB),后传低位(LSB)。输出采用三态电路设计,MDIO需要接1.5K~10K的上拉电阻。通过MDIO线上是否有上拉电阻,来检测MDIO接口是否连接到PHY芯片上。
MDIO前后有两种协议, 包括Clause22 以及之后的Clause45, Clause 45 兼容Clause 22。
在这里插入图片描述
MDIO: 是 PHY 和 STA 之间的双向信号。 它用于在 PHY 和 STA 之间传输控制信息和状态。 控制信息由 STA 同步地针对 MDC 驱动并且由 PHY 同步地采样。 状态信息由 PHY 针对 MDC 同步驱动并由 STA 同步采样。
MDC: 由站管理实体向 PHY 提供,作为在 MDIO 信号上传送信息的定时参考。 MDC 是一种非周期性的信号,没有最高或最低时间。 无论 TX_CLK 和 RX_CLK 的标称周期如何,MDC 的最小高低时间应为 160 ns,MDC 的最小周期为 400 ns。

MDIO-Clause22帧格式:
依次为空闲标志、前导标志、开始标志、操作码、PHY地址、寄存器地址,翻转标志位和数据。
空闲标志(idle):无MDIO帧发送时,MDIO接口输出高阻(外部有上拉电阻,总线上看到的是高电平)。

前导标志(PRE):每帧发送前,MAC通过MDIO连续发送32个MDC周期的高电平1,同时通过MDC输出32个时钟周期。前导的作用是为PHY建立同步提供时间。如果STA能够确定PHY可以接收管理帧,可以压缩前导的发送。

开启标志(st):长度2Bits,必须为01,标志该数据帧开始。

操作码(op):长度2Bits,10标志为读操作,01标志为写操作。

PHY地址(phyad):长度5Bits,表示所访问的PHY地址,一个MDIO总线最大支持32个PHY。

寄存器地址(regad):长度5Bits,表示所访问的寄存器的地址,共计32个寄存器。IEEE802.3协议中对前16个寄存器进行了定义,其中比较常用的如下表。其余为PHY芯片自定寄存器。

翻转标志位(ta):长度2Bits,固定为10。该标志位为PHY芯片地址传输和数据传输处理预留处理时间,设置2bit TA的目的就是为了防止MDIO总线上产生竞争。

数据(data):长度16Bits,操作符为读操作时,该数据为对于地址PHY的特定寄存器的数值;操作符为写时,该数据为对该寄存器写入的数值。

帧格式Clause45:

MDIO Clause45为了访问更多的寄存器在Clause22基础上做了一些扩展,修改如下:

1)ST由01修改为00

2)OP进行了重新定义。00:地址帧 01:写 11:读 10:增量读(Post-read-increment-address)

3)PHYAD域修改名称为PRTAD,端口地址但仍代表PHY地址

4)REGAD修改为DEVAD,Clause45将PHY内部子模块的地址进行细分,这些子模块用DEVAD寻址。子模块内部的寄存器则使用地址帧进行寻址。

mdio时序:
在这里插入图片描述

当MDIO通信出现问题,可依次检查以下方面:

确保MDC工作在合适的频率,MDC以及MDIO有上拉.
PHYAD(PRTAD)没有搞错。
MMD 没有处于复位状态。
适当调整MDC的相位。
有些MMD要求帧与帧之间一定要用高阻态分隔

以太网芯片YT8511支持两种复位方式,一种是硬件复位,另外一种是软件复位。硬件复位时通过ETH_RST_N引脚实现对PHY芯片的复位,当ETH_RST_N引脚持续10ms的低电平时,即可实现对PHY芯片的复位。软件复位通过向寄存器地址0x00的Bit[15]写入1进行复位,并且在完成复位后,该位会自动清零。YT8511共有22位寄存器,有些寄存器是仅可读的,有些寄存器是仅可写的,还有些寄存器是可读可写的,当然在程序设计中我们不需要把这些寄存器全部都用上,一般来说寄存器0x00、0x01、0x1F是3个最常用的寄存器,即基本控制寄存器、基本状态寄存器、PHY控制寄存器。

对基本控制寄存器有个软复位操作,即MAC控制器可以通过MDIO协议向PHY发送软复位请求,在大多数情况下软复位一次后,即可读取到正确的自协商结果。

对基本状态寄存器,用户通过MDIO协议从PHY中读取该寄存器的值,即可从16位的寄存器值中判断目前自协商状态,注意到寄存器的第5位为1时,代表自协商完成,为0时代表自协商未完成;寄存器的第5位为1时,代表网线的物理层连接成功,为0时代表连接失败。

对PHY控制寄存器,通过MDIO协议从PHY中读取该寄存器的值,即可从16位的寄存器值中判断自协商后连接速度,注意到寄存器的第6位为1时,代表千兆速度;寄存器的第5位为1时,代表百兆速度;而寄存器的第4位为1时,代表十兆速

首先每隔一段时间通过MDIO接口从PHY内部寄存器中读取基本状态寄存器(BMSR)和特定状态寄存器(PHYSR)的值,从而获取到自协商完成状态、连接状态和连接速度,将网口的连接速度通过LED灯进行指示;当FPGA检测到TPAD触摸按键按下时,开始通过MDIO接口对PHY进行软复位,在软复位完成后,PHY会重新开始自协商,此时LED灯会定时获取当前网口的连接状态以及连接速度。
mdio驱动模块代码:

module mdio_driver(input clk,input rstn,input en,input wl_rh,input[4:0]addr,input [15:0]wr_data,output reg rd_ack,op_done,dri_clk,eth_mdc,output reg[15:0]rd_data,inout eth_mdio);parameter  PHY_ADDR = 5'b00001,//PHY地址CLK_DIV  = 6'd10 ;   //分频系数localparam st_idle    = 6'b00_0001,  //空闲状态st_pre     = 6'b00_0010,  //发送PRE(前导码)st_start   = 6'b00_0100,  //开始状态,发送ST(开始)+OP(操作码)st_addr    = 6'b00_1000,  //写地址,发送PHY地址+寄存器地址st_wr_data = 6'b01_0000,  //TA+写数据st_rd_data = 6'b10_0000;  //TA+读数据reg[5:0]cstate,nstate,clk_cnt;reg[6:0]cnt;reg[15:0]wr_data_d0,rd_data_d0;reg[1:0]op_code;reg mdio_dir,mdio_out;reg[4:0]addr_d0;wire[5:0]clk_divide;reg done_flag;wire mdio_in;assign mdio_in=eth_mdio,eth_mdio=mdio_dir?mdio_out:1'bz,clk_divide = CLK_DIV >> 2;always @(posedge clk or negedge rstn)beginif(!rstn)begindri_clk='d0;clk_cnt<='d0;endelse if(clk_cnt==clk_divide-'d1)beginclk_cnt<='d0;dri_clk<=~dri_clk;endelseclk_cnt<=clk_cnt+'d1;endalways @(posedge dri_clk or negedge rstn)beginif(!rstn)eth_mdc<='d1;else if(cnt[0]=='d0)eth_mdc<='d1;elseeth_mdc<='d0;endalways @(posedge dri_clk or negedge rstn)beginif(!rstn)cstate<=st_idle;elsecstate<=nstate;endalways @(*)begincase(cstate)st_idle:if(en)nstate=st_pre;elsenstate=st_idle;st_pre:if(done_flag=='d1)nstate=st_start;elsenstate=st_pre ;st_start:if(done_flag=='d1)nstate=st_addr;elsenstate=st_start;st_addr:if(done_flag=='d1)beginif(op_code==2'b01)nstate=st_wr_data;elsenstate=st_rd_data;endelsenstate=st_addr ;st_wr_data:if(done_flag=='d1)nstate=st_idle;elsenstate=st_wr_data;st_rd_data:if(done_flag=='d1)nstate=st_idle;elsenstate=st_rd_data ;default :nstate=st_idle;endcaseendalways @(posedge dri_clk or negedge rstn)beginif(!rstn)begincnt<='d0;op_code<='d0;mdio_dir<='d0;mdio_out<='d1;addr_d0<='d0;rd_data<='d0;wr_data_d0<='d0;rd_data_d0<='d0;rd_ack<='d1;op_done<='d0;done_flag<='d0;endelsebegindone_flag<='d0;cnt<=cnt+'d1;case(cstate)st_idle:beginmdio_out<='d1;mdio_dir<='d0;op_done<='d0;cnt<='d0;if(en)beginop_code<={wl_rh,~wl_rh};addr_d0<=addr;wr_data_d0<=wr_data;rd_ack<='d1;endendst_pre:beginmdio_dir<='d1;mdio_out<='d1;if(cnt=='d62)done_flag<='d1;else if(cnt=='d63)cnt<='d0;endst_start:begincase(cnt)7'd1:mdio_out<='d0;7'd3:mdio_out<='d1;7'd5:mdio_out<=op_code[1];7'd6:done_flag<='d1;7'd7:beginmdio_out<=op_code[0];cnt<='d0;enddefault:;endcaseendst_addr:begincase(cnt)7'd1:mdio_out<=PHY_ADDR[4];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_d0[4];7'd13:mdio_out<=addr_d0[3];7'd15:mdio_out<=addr_d0[2];7'd17:mdio_out<=addr_d0[1];7'd18:done_flag<='d1;7'd19:beginmdio_out<=addr_d0[0];cnt<='d0;enddefault:;endcaseendst_wr_data:begincase(cnt)7'd1:mdio_out<='d1;7'd3:mdio_out<='d0;//op_code 107'd5:mdio_out<=wr_data_d0[15];7'd7:mdio_out<=wr_data_d0[14];7'd9:mdio_out<=wr_data_d0[13];7'd11:mdio_out<=wr_data_d0[12];7'd13:mdio_out<=wr_data_d0[11];7'd15:mdio_out<=wr_data_d0[10];7'd17:mdio_out<=wr_data_d0[9];7'd19:mdio_out<=wr_data_d0[8];7'd21:mdio_out<=wr_data_d0[7];7'd23:mdio_out<=wr_data_d0[6];7'd25:mdio_out<=wr_data_d0[5];7'd27:mdio_out<=wr_data_d0[4];7'd29:mdio_out<=wr_data_d0[3];7'd31:mdio_out<=wr_data_d0[2];7'd33:mdio_out<=wr_data_d0[1];7'd35:mdio_out<=wr_data_d0[0];7'd37:beginmdio_dir<='d0;mdio_out<='d1;end7'd39:done_flag<='d1;7'd40:begincnt<='d0;op_done<='d1;enddefault:;endcaseendst_rd_data:begincase(cnt)7'd1:beginmdio_dir<='d0;mdio_out<='d1;//op_code 01end7'd4:rd_ack<=mdio_in;7'd6:rd_data_d0[15]<=mdio_in;7'd8:rd_data_d0[14]<=mdio_in;7'd10:rd_data_d0[13]<=mdio_in;7'd12:rd_data_d0[12]<=mdio_in;7'd14:rd_data_d0[11]<=mdio_in;7'd16:rd_data_d0[10]<=mdio_in;7'd18:rd_data_d0[9]<=mdio_in;7'd20:rd_data_d0[8]<=mdio_in;7'd22:rd_data_d0[7]<=mdio_in;7'd24:rd_data_d0[6]<=mdio_in;7'd26:rd_data_d0[5]<=mdio_in;7'd28:rd_data_d0[4]<=mdio_in;7'd30:rd_data_d0[3]<=mdio_in;7'd32:rd_data_d0[2]<=mdio_in;7'd34:rd_data_d0[1]<=mdio_in;7'd36:rd_data_d0[0]<=mdio_in;7'd39:done_flag<='d1;7'd40:beginop_done<='d1;rd_data<=rd_data_d0;rd_data_d0<='d0;cnt<='d0;enddefault:;endcaseenddefault:;endcaseendend
endmodule

在st_idle状态中,如果en信号为高(或者说en = 1),则状态会转换到st_pre。否则,状态保持为st_idle。
在st_pre状态中,如果done_flag信号为’d1’(可能表示某个特定操作完成),则状态会转换到st_start。否则,状态保持为st_pre。
类似地,在接下来的每个状态中,都有特定的条件用于转换到下一个状态或保持当前状态。
在所有分支之后,有一个默认分支,如果cstate的值没有匹配任何已知的状态,那么状态将转换到st_idle。
于eth_mdc需要在输入时钟的基础上进行分频,为了方便操作,这里先对输入的时钟进行分频,得到一个dri_clk时钟,作为MDIO驱动模块和MDIO控制模块的操作时钟。eth_mdc在dri_clk的基础上进行2分频,由于输入的参数CLK_DIV为eth_mdc相对于输入时钟的分频系数,因此为了得到dri_clk的分频系数,需要将CLK_DIV除以2,如代码中第72行所示。
根据分频系数clk_divide,得到dri_clk的时钟。当cnt一直累加时,eth_mdc的时钟相当于对dri_clk进行2分频。当开始对MDIO接口进行读写操作时,cnt累加,此时才会产生eth_mdc时钟;当读写操作结束后eth_mdc将一直处于高电平。

在st_wr_data状态下,数据是在eth_mdc的下降沿写入,而在st_rd_data状态,数据在erth_mdc的上升沿读出。值得一提是,在st_rd_data状态下,程序中根据TA的第二位,判断PHY芯片有没有应答,如果没有应答,则说明读取数据失败。
mdio控制模块代码:

module ctrl_mdio(input clk,input rstn,input soft_trig,op_done,input[15:0]rd_data,input rd_ack,output reg[4:0]addr,output reg en,wl_rh,wr_data,output [1:0]led);reg rst_trig_d0,rst_trig_d1,rst_trig_flag;reg time_done,st_next,rd_next,error;reg[1:0]speed_status;reg[2:0]flow_cnt;reg[23:0]time_cnt;wire rst_trig_p;assign rst_trig_p=~rst_trig_d1&rst_trig_d0,led=error?2'd0:speed_status;always @(posedge clk or negedge rstn)beginif(!rstn)beginrst_trig_d0<='d0;rst_trig_d1<='d0;endelsebeginrst_trig_d0<=soft_trig;rst_trig_d1<=rst_trig_d0;endendalways @(posedge clk or negedge rstn)beginif(!rstn)begintime_cnt<='d0;time_done<='d0;endelsebeginif(time_cnt=='d1_000_000-'d1)begintime_done<='d1;time_cnt<='d0;endelsebegintime_cnt<=time_cnt+'d1;time_done<='d0;endendendalways @(posedge clk or negedge rstn)beginif(!rstn)beginflow_cnt<='d0;rst_trig_flag<='d0;speed_status<='d0;en<='d0;wl_rh<='d0;addr<='d0;wr_data<='d0;st_next<='d0;rd_next<='d0;error<='d0;endelsebeginen<='d0;if(rst_trig_p=='d1)rst_trig_flag<='d1;case(flow_cnt)3'd0:beginif(rst_trig_flag=='d1)beginen<='d1;wl_rh<='d0;addr<=5'h0;wr_data<=16'h9140;flow_cnt<=3'd1;endelse if(time_done)beginen<='d1;wl_rh<='d1;addr<=5'd1;flow_cnt<=3'd2;endelse if(st_next)beginen<='d1;wl_rh<='d1;addr<=5'h11;flow_cnt<=3'd2;st_next<='d0;rd_next<='d1;endelse;end3'd1:if(op_done)beginflow_cnt<=3'd0;rst_trig_flag<='d0;endelse;3'd2:if(op_done)beginif(rd_ack=='d0&&rd_next=='d0)flow_cnt<=3'd3;else if(rd_ack=='d0&&rd_next=='d1)beginrd_next<='d0;flow_cnt<=3'd4;endelseflow_cnt<=3'd0;end3'd3:beginflow_cnt<=3'd0;if(rd_data[5]==1&&rd_data[2]==1)beginst_next<='d1;error<='d0;endelseerror<='d1;end3'd4:beginflow_cnt<=3'd0;case(rd_data[15:14])2'd2:speed_status<=2'b11;2'd1:speed_status<=2'b10;2'd0:speed_status<=2'b01;default :speed_status<='d0;endcaseenddefault:;endcaseendend
endmodule

根据软复位信号对MDIO接口进行软复位,并定时读取以太网的连接状态。根据状态寄存器的值,为连接速率状态位speed_status赋值。
顶层模块:


module mdio_rw_top(input          clk  ,input          rstn,//MDIO接口output         eth_mdc  , //PHY管理接口的时钟信号inout          eth_mdio , //PHY管理接口的双向数据信号output         eth_rstn, //以太网复位信号input          touch_key, //触摸按键output  [1:0]  led        //LED连接速率指示);//wire define
wire          en    ; //触发开始信号
wire          wl_rh   ;  //低电平写,高电平读
wire  [4:0]   addr    ;  //寄存器地址
wire  [15:0]  wr_data ;  //写入寄存器的数据
wire          op_done    ;  //读写完成
wire  [15:0]  rd_data ;  //读出的数据
wire          rd_ack  ;  //读应答信号 0:应答 1:未应答
wire          dri_clk    ;  //驱动时钟//硬件复位
assign eth_rstn = rstn;//MDIO接口驱动
mdio_driver #(.PHY_ADDR    (5'h04),    //PHY地址 3'b100.CLK_DIV     (6'd10)     //分频系数)u_mdio_dri(.clk        (clk),.rstn      (rstn),.en    (en),.wl_rh   (wl_rh  ),   .addr    (addr   ),   .wr_data (wr_data),   .op_done    (op_done   ),   .rd_data (rd_data),   .rd_ack  (rd_ack ),   .dri_clk    (dri_clk   ),  .eth_mdc    (eth_mdc   ),   .eth_mdio   (eth_mdio  )   
);       //MDIO接口读写控制    
ctrl_mdio  u_mdio_ctrl(.clk           (dri_clk),  .rstn         (rstn ),  .soft_trig (touch_key ),.op_done       (op_done   ),  .rd_data    (rd_data),  .rd_ack     (rd_ack ),  .en       (en   ),  .wl_rh      (wl_rh  ),  .addr       (addr   ),  .wr_data    (wr_data),  .led           (led       )
);      endmodule

上板运行:

QQ视频20231104163807

查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"以太网实验1.mdio接口读写实验":http://eshow365.cn/6-32149-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!