一、以太网简介
是当今现有局域网采用的最通用的通信协议标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。成本低,通信速率高,抗干扰能力强。
1、分类
标准以太网:10Mbit/s
快速以太网:100Mbit/s
千兆以太网:1000Mbit/s
.........
以太网和千兆网口其实不完全相同。以太网是一种局域网技术标准,而千兆网口通常指的是支持1Gb/s(即千兆位每秒)速度的网络接口。
以太网是一种通用的局域网技术标准,定义了数据传输的规范和硬件设备之间的通信方式。而千兆网口是一种物理接口标准,用于连接计算机或其他网络设备到网络中,支持更高的数据传输速度。
因此,可以说千兆网口是以太网的一种实现方式,它提供了更快的数据传输速度,但并不等同于以太网。在实际应用中,通常会使用以太网技术,并通过千兆网口进行连接,以实现更快的网络通信速度。
2、以太网接口
以太网通信离不开连接端口的支持,网络数据连接的端口就是以太网接口。以太网接口类型有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接口:(默认即可实现功能)
3、UDP网络协议简介
UDP(User Datagram Protocol,用户数据报协议)是一种无连接的网络传输协议,位于OSI模型的传输层。以下是UDP网络协议的简介:
(1)无连接性:UDP是一种无连接的协议,发送端在发送数据之前不需要与接收端建立连接,也不会维护连接状态。
(2)面向数据报:UDP以数据报(Datagram)的形式传输数据,每个数据报都是独立的,互相之间没有关联。
(3)不可靠性:UDP不提供数据可靠性保证,数据报可能会丢失、重复或无序到达。也不会进行数据校验和重传。
(4)高效性:由于没有连接建立和维护的开销,以及简化的功能,UDP比TCP更轻量级,传输效率更高。
(5)适用场景:UDP适用于对实时性要求高、数据传输完整性要求不高的应用场景,如音频、视频流媒体传输、在线游戏等。
总的来说,UDP是一种简单、高效但不可靠的网络传输协议,适用于对实时性要求高、能容忍少量数据丢失的应用场景。
4、数据链路
数据通过FPGA进行数据协议的打包和MAC协议处理,通过RGMII总线协议传输将数据传送至PHY芯片,PHY芯片将数据进行处理后发送至RJ45接口进行数据发送。
物理层(RJ45网口接口)、数据链路层(PHY芯片和MAC协议)、网络层(在FPGA中处理的MAC协议)、传输层(在FPGA中处理的数据协议打包,用UDP或者TCP协议就是说的这一层)
二、以太网PHY芯片简介RTL8211
1、PHY芯片:物理层芯片(物理接口收发器)
自动协商通信速率,或自行配置
2、BMCI寄存器配置(RTL8211为例)
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直接通信
3、BMSR寄存器配置
BMCI寄存器addr为:0x01
(1)1.5:自协商完成
(2)1.2:是否连接OK
4、PHYSR寄存器配置
BMCI寄存器addr为:0x11
(1)17.15:14 :连接速率(前提link ok,自协商完成)
三、以太网PHY芯片简介YT8531
1、PHY芯片
网线差分模拟信号转换数字信号,FPGA数字信号转换网线差分模拟信号。
2、引脚复用
3、硬件原理图(正点原子达芬奇开发板)
22R电阻做阻抗匹配。
4、PHY地址接口
一定会响应0x00!!
5、基本控制寄存器配置
寄存器地址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半双工。
6、基本状态寄存器配置
寄存器地址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:直接开始传输。
7、特定状态寄存器
寄存器地址0x11
四、MDIO接口时序
MDIO: Management Data Input/Output
也被称为SMI ( Serial Management Interface ) ,即串行管理接口。
MAC和 PHY芯片有一个配置接口,即MDIO接口,可以配置 PHY芯片的工作模式以及获取 PHY芯片的若干状态信息。
1、MDIO接口连接图
2、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:数据
3、PHY读写传输时序
上升沿采集数据,下降沿更新数据;MDC的最小周期为80ns对应时钟最大为12.5MHz
五、MDIO读写测试实验
PHY芯片不进行配置也可以使用
1、系统框图
2、模块原理图
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为高期间,会返回读到的数据)。
3、状态机
4、用户接口时序
5、程序设计
(1)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) 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
①控制双向io方向
②分频得到dri_clk时钟
首先对CLK_DIV进行除2,目的是得到2倍的CLK_DIV时钟分频,再利用clk_cnt计数器产生dri_clk时钟。clk_divide[5:1] - 1'd1 取clk_divide的第5到第1位(实际作用除2取整,记满进行信号反转),再减掉1位作为计数器最大值、dri_clk信号跳转标志。(对时钟信号进行二分频)
③产生PHY_MDC时钟
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;进行输出其他新信号清零。
(2)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
① 对soft_rst_trig信号延时打两拍
对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进行赋值。文章来源:https://www.toymoban.com/news/detail-842838.html
(3)mdio_rw_test顶层
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
六、下载验证
文章来源地址https://www.toymoban.com/news/detail-842838.html
到了这里,关于FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!