赞
踩
是当今现有局域网采用的最通用的通信协议标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。成本低,通信速率高,抗干扰能力强。
标准以太网:10Mbit/s
快速以太网:100Mbit/s
千兆以太网:1000Mbit/s
.........
以太网和千兆网口其实不完全相同。以太网是一种局域网技术标准,而千兆网口通常指的是支持1Gb/s(即千兆位每秒)速度的网络接口。
以太网是一种通用的局域网技术标准,定义了数据传输的规范和硬件设备之间的通信方式。而千兆网口是一种物理接口标准,用于连接计算机或其他网络设备到网络中,支持更高的数据传输速度。
因此,可以说千兆网口是以太网的一种实现方式,它提供了更快的数据传输速度,但并不等同于以太网。在实际应用中,通常会使用以太网技术,并通过千兆网口进行连接,以实现更快的网络通信速度。
以太网通信离不开连接端口的支持,网络数据连接的端口就是以太网接口。以太网接口类型有RJ45接口,RJ11接口(电话线接口),SC光纤接口等其中RJ45接口是我们现在最常见的网络设备接口(如:电脑网口)
3、6一对是为了向下兼容10/100M
上图为RGMII接口,时钟为双沿2.5MHz、25MHz、250MHz。位宽为4位。(节省引脚)
GMII接口,时钟为单沿1.25MHz、12.5MHz、125MHz。位宽为8位。
MAC侧完成对报文的封包和解包过程
PHY芯片:实现模数转换
ETH_RXC | 接收端时钟 |
ETH_RXCTL | 接收端数据使能(接受报文有效,该引脚拉高) |
ETH_RXD[3:0] | 接收数据 |
ETH_TXC | 发送端时钟 |
ETH_TXCTL | 发送端数据使能(发送报文有效,该引脚拉高) |
ETH_TXD[3:0] | 发送数据 |
ETH_RST_N | 复位脚 |
ETH_MDC | |
ETH_MDIO |
MDIO接口:(默认即可实现功能)
UDP(User Datagram Protocol,用户数据报协议)是一种无连接的网络传输协议,位于OSI模型的传输层。以下是UDP网络协议的简介:
(1)无连接性:UDP是一种无连接的协议,发送端在发送数据之前不需要与接收端建立连接,也不会维护连接状态。
(2)面向数据报:UDP以数据报(Datagram)的形式传输数据,每个数据报都是独立的,互相之间没有关联。
(3)不可靠性:UDP不提供数据可靠性保证,数据报可能会丢失、重复或无序到达。也不会进行数据校验和重传。
(4)高效性:由于没有连接建立和维护的开销,以及简化的功能,UDP比TCP更轻量级,传输效率更高。
(5)适用场景:UDP适用于对实时性要求高、数据传输完整性要求不高的应用场景,如音频、视频流媒体传输、在线游戏等。
总的来说,UDP是一种简单、高效但不可靠的网络传输协议,适用于对实时性要求高、能容忍少量数据丢失的应用场景。
数据通过FPGA进行数据协议的打包和MAC协议处理,通过RGMII总线协议传输将数据传送至PHY芯片,PHY芯片将数据进行处理后发送至RJ45接口进行数据发送。
物理层(RJ45网口接口)、数据链路层(PHY芯片和MAC协议)、网络层(在FPGA中处理的MAC协议)、传输层(在FPGA中处理的数据协议打包,用UDP或者TCP协议就是说的这一层)
自动协商通信速率,或自行配置
BMCI寄存器addr为:0x00
(1)0.15:软件复位,1复位,0工作。结束复位后,无需手动写0,结束复位会自动归0,
(2)0.14:环回,主要用于检测,1使能,0失能。
(3)0.13,0.6:Speed配置,配置不同速率(先失能自协商)
(4)0.12:自协商配置,默认使能,1使能,0失能。
(5)0.11:掉电模式配置,1掉电模式,0正常工作模式。
(6)0.10:隔离配置,RGMII接口隔离,引脚失能,只有MDIO,MDC配置有效
(7)0.9:重新开始自协商配置。
(8)0.8:双工模式配置
(9)0.7:冲突测试
(10)0.5:单向使能:1双方建立连接成功后通信,0直接通信
BMCI寄存器addr为:0x01
(1)1.5:自协商完成
(2)1.2:是否连接OK
BMCI寄存器addr为:0x11
(1)17.15:14 :连接速率(前提link ok,自协商完成)
网线差分模拟信号转换数字信号,FPGA数字信号转换网线差分模拟信号。
22R电阻做阻抗匹配。
一定会响应0x00!!
寄存器地址0x00
(1) 15:软复位,带自清0,默认为0 。0工作,1复位。
(2) 14:内部环回,默认为0。0失能,1使能。
(3) 13:速率配置低位,关闭自协商才可以使用,bit6,13进行配置。
(4) 12:自协商使能,默认支持。1使能,0失能。
(5) 11:掉电模式,1掉电,0正常。
(6) 10:隔离模式,一般为0正常模式
(7) 9:重新自协商,自带置1。
(8) 8:配置双工,关闭自协商才可以使用,1双工,0半双工。
寄存器地址0x01
(1) 13:双工模式检测。 0:半双工, 1:全双工。
(2) 15~14:传输速度等级检测。10:1000M 01:100M 00:10M
(3) 2:连接状态。 1:连接已建立, 0:链接未建立。
(4) 3:自协商能力, 1具有, 0不具备。
(5) 4:远端错误指示位:读取后自动清0 0:远端设备正常 1:远端设备异常
(6) 5:自协商完成标志,软复位后自动清0 1:完成自协商 0:未完成自协商。
(7) 6:前导码模式,1:不检测前导码直接接受 0:检测前导码
(8) 7:单项传输能力:0:当检测到建立有效连接才开始传输 1:直接开始传输。
寄存器地址0x11
MDIO: Management Data Input/Output
也被称为SMI ( Serial Management Interface ) ,即串行管理接口。
MAC和 PHY芯片有一个配置接口,即MDIO接口,可以配置 PHY芯片的工作模式以及获取 PHY芯片的若干状态信息。
管理接口通过MDC和MDIO引脚提供对内部寄存器的访问,如IEEE802.3u第22节所述。MDC信号由MAC提供,是MDIO信号的管理数据时钟参考。MDIO是管理数据的输入/输出,是一个双向信号,与MDC同步运行。MDIO引脚需要一个1.5k欧姆的上拉电阻,以在空闲和周转期间保持MDIO高电平。
前置抑制为RTL8211E/RTL8211EG上电后的默认设置。然而,在操作之间仍然必须至少有一个空闲位。RTL8211E/RTL8211EG可以共享同一条MDIO线。在交换机/路由器应用程序中,每个端口应该在硬件复位序列中分配一个唯一的地址,并且只能通过该唯一的PHY地址进行寻址。有关RTL8211E/RTL8211EG管理寄存器的详细信息,请参见第30页第8节寄存器描述。
写状态下,一直由FPGA输出数据给到PHY芯片;读状态下,在TA位切换方向
(1)Preamble:前导码,32个逻辑1,同时给出时钟。
(2)ST:帧开始。固定01
(3)OP:操作码,指示当前读写
(4)PHYAD:PHY地址
(5)REGAD:寄存器地址
(6)TA:2bit宽度,时间间隔,切换FPGA数据线方向。Z为高阻状态,0为应答。
(7)DATA:数据
上升沿采集数据,下降沿更新数据;MDC的最小周期为80ns对应时钟最大为12.5MHz
PHY芯片不进行配置也可以使用
u_mdio_dri模块:
op_addr:读写地址
op_exec:触发信号↓↓↓↓↓↓↓↓↓↓↓
op_rh_wl:读写标志信号 1读 0写
op_wr_data:具体写入数据
op_done:读写结束后,拉高该信号
op_rd_ack:读应答信号 0应答 1未应答(帧格式里面的AT)
op_rd_data:读出的信号
dri_clk:驱动时钟
当op_exec信号拉高标志一次读写操作开始,根据op_rh_wl状态判断读写操作,同时给出读写操作地址op_addr(如果是写操作要对应给出op_wr_data),读写操作结束后,拉高op_done信号,表示当前读写操作完成,控制模块可以发起下一次操作(如果是写读操作,在op_done为高期间,如果是0表示应答,如果是1表示未应答)(如果读操作,在op_done为高期间,会返回读到的数据)。
- 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) 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
首先对CLK_DIV进行除2,目的是得到2倍的CLK_DIV时钟分频,再利用clk_cnt计数器产生dri_clk时钟。clk_divide[5:1] - 1'd1 取clk_divide的第5到第1位(实际作用除2取整,记满进行信号反转),再减掉1位作为计数器最大值、dri_clk信号跳转标志。(对时钟信号进行二分频)
cnt计数器最低位,实际只在0.1互相切换, 实际是对dri_clk进行二分频。(也就是dri_clk是MDC的两倍,此时eth_mdc是时钟频率四分频)
- //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+读数据
- //(三段式状态机)同步时序描述状态转移
- 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
在 st_idle(空闲状态)模式下:op_exec(触发开始信号)拉高,下一状态进入st_pre,否则保持。
在st_pre(发送PRE(前导码))模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_start,否则保持
在st_start(开始状态,发送ST(开始)+OP(操作码))模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_addr,否则保持
在st_addr(写地址,发送PHY地址+寄存器地址)模式下:如果op_code (操作码)== 2'b01,下一状态进入st_wr_data,否则进入st_rd_data
在st_wr_data(TA+写数据)模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_idle,否则保持
在st_rd_data(TA+读数据)模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_idle,否则保持
- //时序电路描述状态输出
- 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
复位后:cnt(计数器)、op_code(操作码 2'b01(写) 2'b10(读))、addr_t(缓存寄存器地址)、wr_data_t(缓存写寄存器的数据)、rd_data_t(缓存读寄存器数据)、op_done(读写完成)、st_done(状态开始跳转信号)、op_rd_data(读出的数据)、mdio_dir(MDIO数据(SDA)方向控制)清零。op_rd_ack(读应答信号 0:应答 1:未应答)、mdio_out(MDIO输出信号)置1。
mdio_out(MDIO输出信号)置1、mdio_dir(MDIO数据(SDA)方向控制):输入、op_done(读写完成)置0。
如果op_exec(触发信号)拉高,对op_code进行为拼接赋值,op_addr、op_wr_data进行寄存,op_rd_ack(读应答信号 0:应答 1:未应答)置1。
dri_clk下计数63个上升沿,对应MDC时钟31个上升沿,即为发送32个前导码1。记满cnt清零
发送起始信号ST01,发送读写操作码低电平写,op_code[1],op_code[0]低电平写,高电平读。st_done(状态开始跳转信号)置为1。
为什么cnt计数为奇数时改变数据:dri_clk为MDC的2倍,奇数刚好在MDC下降沿改变数据。
依次发送PHY地址、寄存器地址,期间st_done(状态开始跳转信号)置为1
发送TA写指令后,开始发送写入数据。之后将mdio_dir(MDIO数据(SDA)方向控制)置0释放总线进入高阻,mdio_out(MDIO输出信号)置1,op_done(读写完成)拉高标志一次读写造作结束。
发送TA读指令后,TA[1]位无需操作保持高阻状态,由PHY侧控制总线。检测TA[0]位,如有应答,PHY侧会拉低mdio_in引脚,作为应答信号。依次接受16位读出数据寄存到rd_data_t下。之后op_done(读写完成)拉高标志一次读写造作结束,op_rd_data <= rd_data_t;进行输出其他新信号清零。
- 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
对soft_rst_trig 软复位触发信号进行打两拍,抓取soft_rst_trig上升沿信号
如果检测到soft_rst_trig 上升沿,将rst_trig_flag信号拉高
不拉高op_exec原因:触摸按键按下实践不确定,如果在读写期间按下,拉高op_exec没有意义,无法达到复位效果。
0状态:
如果rst_trig_flag信号拉高,进行软复位,op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置0。op_addr(寄存器地址)给出寄存器地址0x00,op_wr_data(写入寄存器的数据)对PHY芯片寄存器给出配置。flow_cnt下一状态进入1状态。
16'h9140 = 2'b1001_0001_0100_0000
回看0x00寄存器,
如果复位没有按下,则会等待计数器记满后,读取当前通信速率。op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置1,op_addr(寄存器地址)给出寄存器地址0x01。flow_cnt下一状态进入2状态。
16'h9140 = 2'b1001_0001_0100_0000
如果计数器没有记满,start_next拉高时,op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置1,op_addr(寄存器地址)给出寄存器地址0x11。flow_cnt下一状态进入2状态。start_next <= 1'b0;read_next <= 1'b1;
1状态:
等待op_done读写结束标志拉高,下一状态为0状态,rst_trig_flag标志位拉低。
2状态:
如果应答信号op_rd_ack为0,read_next(寄存器读取标志)为0,跳转到3状态。
如果read_next(寄存器读取标志)为1,重置read_next为0,下一状态为4状态。
如果没有应答信号op_rd_ack为1时,下一状态进入0状态。
3状态:
下一状态进入0状态。读出0x01寄存器的第5位和第2位,进行判断。符合条件(远端连接成功)start_next拉高。
4状态:
下一状态进入0状态,读取寄存器0x11的15、14位,的出对应的通信速率,赋值给speed_status。
利用三目运算为led进行赋值。
- 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地址
- .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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。