RGMII时序
前面讲到关于关于ARP的理论知识,该章节主要通过FPGA接收以太网数据,并作数据分析。
首先关于以太网RGMII接收时序如下图所示:
图1 RGMII接收时序图
如上图所示,RGMII采用双沿采样,4根数据线,时钟采用125MHz。这里提一下关于GMII时钟同样是125MHz,8根数据线,采用单沿采样。
如上图所示,接收的信号需要将数据端整体偏移,保证数据在时钟沿的中间,这样保证采样时数据处于稳定状态。移动后的数据如下图所示:
图2 数据偏移后的数据
偏移数据采和双沿采集会用到FPGA里的IDELAYE2原语和IDDR原语,下面就FPGA原语做简单介绍。
FPGA驱动
FPGA解析双沿信号一般使用IDDR转换成单沿信号,根据ug471_7Series_SelectIO文档说明,IDDR分别由三种工作模式,分别为OPPOSITE_EDGE mode、SAME_EDGE mode、SAME_EDGE_PIPELINED mode。时序图如下图所示:
图3 OPPOSITE_EDGE mode模式
图4 SAME_EDGE mode模式
图5 SAME_EDGE_PIPELINED mode模式
本设计以太网采用第一种模式,如下图所示为rgmii接收端FPGA代码驱动:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/07/22 16:11:10
// Design Name:
// Module Name: rgmii_2_gmii_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module rgmii_2_gmii_rx(
input idelay_clk ,
input rgmii_rxc , //RGMII接收时钟
(* MARK_DEBUG="true" *)input rgmii_rx_ctl, //RGMII接收数据控制信号
(* MARK_DEBUG="true" *)input [3:0] rgmii_rxd , //RGMII接收数据
output gmii_rx_clk , //GMII接收时钟
(* MARK_DEBUG="true" *)output gmii_rx_dv , //GMII接收数据有效信号
(* MARK_DEBUG="true" *)output [7:0] gmii_rxd //GMII接收数据
);
wire[3:0] rgmii_rxd_delay;
wire rgmii_rx_ctl_delay;
wire rgmii_clk;
wire rgmii_rxc_io;
wire[3:0] rx_data_rise;
wire[3:0] rx_data_neg;
wire rgmii_rx_ctl_delay_rise;
wire rgmii_rx_ctl_delay_neg;
assign gmii_rx_clk = rgmii_clk;
assign gmii_rx_dv = rgmii_rx_ctl_delay_rise & rgmii_rx_ctl_delay_neg;
assign gmii_rxd = {rx_data_neg,rx_data_rise};
IDDR 2 BUFIO BUFIO 在采集源同步 IO 数据时,提供非常小的延时
WORK 2 BUFG
//全局时钟缓存
BUFG BUFG_inst (
.I (rgmii_rxc), // 1-bit input: Clock input
.O (rgmii_clk) // 1-bit output: Clock output
);
//全局时钟IO缓存
BUFIO BUFIO_inst (
.I (rgmii_rxc), // 1-bit input: Clock input
.O (rgmii_rxc_io) // 1-bit output: Clock output
);
IDELAYCTRL IDELAYCTRL_inst (
.RDY(), // 1-bit output: Ready output
.REFCLK(idelay_clk), // 1-bit input: Reference clock input
.RST(1'b0) // 1-bit input: Active high reset input
);
genvar i;
generate
for (i = 0; i < 4; i = i + 1)begin
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(15), // Input delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
)
IDELAYE2_inst0 (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rgmii_rxd_delay[i]), // 1-bit output: Delayed data output
.C(1'b0), // 1-bit input: Clock input
.CE(1'b0), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN(1'b0), // 5-bit input: Counter value input
.DATAIN(), // 1-bit input: Internal delay data input
.IDATAIN(rgmii_rxd[i]), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
.REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
);
end
endgenerate
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(15), // Input delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
)
IDELAYE2_inst1 (
.CNTVALUEOUT(), // 5-bit output: Counter value output
.DATAOUT(rgmii_rx_ctl_delay), // 1-bit output: Delayed data output
.C(1'b0), // 1-bit input: Clock input
.CE(1'b0), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN(1'b0), // 5-bit input: Counter value input
.DATAIN(), // 1-bit input: Internal delay data input
.IDATAIN(rgmii_rx_ctl), // 1-bit input: Data input from the I/O
.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
.LD(1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
.REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
);
//IDDR
genvar j;
generate
for (j = 0; j < 4; j = j + 1)begin
IDDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
)IDDR_inst0 (
.Q1(rx_data_rise[j]), // 1-bit output for positive edge of clock
.Q2(rx_data_neg[j]), // 1-bit output for negative edge of clock
.C(rgmii_rxc_io), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rgmii_rxd[j]), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerate
IDDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
)IDDR_inst1 (
.Q1(rgmii_rx_ctl_delay_rise), // 1-bit output for positive edge of clock
.Q2(rgmii_rx_ctl_delay_neg), // 1-bit output for negative edge of clock
.C(rgmii_rxc_io), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D(rgmii_rx_ctl_delay), // 1-bit DDR data input
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
endmodule
仿真代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/07/22 16:39:53
// Design Name:
// Module Name: eth_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module eth_tb(
);
reg sys_clk;
reg sys_rst_n;
reg eth_clk;
reg eth_rxc;
reg eth_rx_ctl;
reg[3:0] eth_rxd;
reg[7:0] cnt;
always #5 sys_clk = ~sys_clk;
always #4 eth_rxc = ~eth_rxc;
always #2 eth_clk = ~eth_clk;
initial begin
sys_clk = 'd0;
eth_rxc = 'd0;
eth_clk = 'd1;
sys_rst_n = 'd0;
#2002
sys_rst_n = 'd1;
end
always @(posedge eth_clk or negedge sys_rst_n) begin
if(sys_rst_n == 'd0) begin
eth_rx_ctl = 'd0;
eth_rxd <= 'd0;
cnt <= 'd0;
end
else if(cnt <= 'd11)begin
eth_rxd <= eth_rxd + 'd1;
eth_rx_ctl <= 'd1;
cnt <= cnt + 'd1;
end
else if(cnt <= 'd200)begin
eth_rxd <= 'd0;
eth_rx_ctl <= 'd0;
cnt <= cnt + 'd1;
end
else begin
cnt <= 'd0;
end
end
top top_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.eth_rxc(eth_rxc),
.eth_rx_ctl(eth_rx_ctl),
.eth_rxd(eth_rxd),
.eth_txc(eth_txc),
.eth_tx_ctl(),
.eth_txd(),
.eth_rst_n()
);
endmodule
如下图所示为为仿真数据
图6 RGMII接收信号仿真
紫色的信号是原始信号,黄色是经过IDDR转化后的数据,理论上应该是输出0x21、0x43、0x65、0x87......但实际上通过ILA抓波形数据会反过来,所以这里高低位反了了一遍,目前原因不明。
通过CMD命令发送“ping”指令,PC端会向板卡发送ARP数据包,如下图所示为FPGA经过ILA抓的实际波形
图7 ILA抓ARP包1
图8 ILA抓ARP包2
如上图所示为ARP包,包解析如下:
55_55_55_55_55_55_55:前导码;
d5:帧起始界定符,固定值;
ff_ff_ff_ff_ff_ff:目的MAC,代表广播;
d4_93_90_24_23_9a:源端MAC地址,这里通过CMD命令查询MAC地址命令为“ipconfig/ all”,x显示如下:
图9 电脑MAC地址
源端MAC地址和LIA抓到的值一致。
08_06:代表ARP包;
00_01:硬件类型值1;
08_00:协议类型0800;
06_04:硬件地址长度和协议地址长度,ARP固定值;
00_01:代表ARP请求包;
d4_93_90_24_23_9a:源端MAC地址;
00_00_00_00:源IP地址,这里为何为0,wireshark抓包是有源IP信息的。
00_00_00_00_00_00:目的MAC地址,这里为何不是全F?,经过查询,该处对于未知目的MAC,写入的值是全0,Wishare抓取到的目的MAC地址也是全0。
c0_a8_89_0b:目的IP地址;
00_......_00:18个00,数据填充位;
8a_82_da_16:CRC校验位
根据ARP数据包解析,这里有两个地方和前一章节写的有冲突,分别是源端IP地址为全0,目的MAC地址也为全0。
RGMII接收FPGA驱动代码编写完毕,如有疑问可留言。
标签:实战篇,FPGA,rx,b0,RGMII,output,input,bit,rgmii From: https://blog.csdn.net/weixin_51418325/article/details/140637482