一、简介
FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3_yt8531sh原理图-CSDN博客
1、以太网帧类型
2、以太网帧格式
前导码(Preamble) : 为了实现底层数据的正确阐述,物理层使用 7 个字节同步码(0 和 1 交替(55-55-55-55-55-55-55))实现数据的同步。
帧起始界定符(SFD, Start Frame Delimiter):使用 1 个字节的 SFD(固定值为 0xd5)来表示一帧的开始,即后面紧跟着传输的就是以太网的帧头。
目的 MAC 地址: 即接收端物理 MAC 地址,占用 6 个字节。 MAC 地址从应用上可分为单播地址、组播地址和广播地址。单播地址:第一个字节的最低位为 0,比如 00-00-00-11-11-11,一般用于标志唯一的设备;组播地址:第一个字节的最低位为 1,比如 01-00-00-11-11-11,一般用于标志同属一组的多个设备;广播地址:所有 48bit 全为 1,即 FF-FF-FF-FF-FF-FF,它用于标志同一网段中的所有设备。
源 MAC 地址:即发送端物理 MAC 地址,占用 6 个字节。
长度/类型: 上图中的长度/类型具有两个意义,当这两个字节的值小于 1536(十六进制0x0600)时,代表该以太网中数据段的长度;如果这两个字节的值大于 1536,则表示该以太网中的数据属于哪个上层协议,例如 0x0800 代表 IP 协议(网际协议) 、 0x0806 代表 ARP 协议(地址解析协议)等。
数据:以太网中的数据段长度最小 46 个字节, 最大 1500 个字节。
3、以太网MAC帧格式
二、以太网报文
目的IP :ff ff ff ff ff ff(广播IP地址) 类型:ARP (0806) 源MAC地址:74 7d 24 92 fb df
单播 广播 组播
单播:MAC 00 XX XX XX XX XX
组播:MAC 01 XX XX XX XX XX
广播:MAC FF FF FF FF FF FF
三、ARP协议
ARP根据IP地址获取MAC地址的一种TCP/IP协议
1、ARP映射
ARP 映射是指将 IP 地址和 MAC 地址映射起来,分为静态映射和动态映射。
静态映射指手动创建一张 ARP 表,把 IP 地址和 MAC 地址关联起来。手动绑定之后,源主机在通信之前,就可以直接从 ARP 表中直接找到 IP 地址对应的 MAC 地址。
动态映射指使用协议来获取相对应的物理地址,之所以用动态这个词是因为这个过程是自动完成的,一般应用程序的用户或系统管理员不必关心。
2、ARP请求
3、ARP应答

4、免费ARP(检测IP冲突)
5、以太网ARP数据包
6、ARP协议
硬件类型(Hardware type):硬件地址的类型, 0x0001 表示以太网地址。
协议类型(Protocol type):要映射的协议地址类型, ARP 协议的上层协议为 IP 协议,因此该协议类型为 IP 协议,其值为 0x0800。
硬件地址长度(Hardware size):硬件地址(MAC 地址)的长度,以字节为单位。对于以太网上 IP 地址的 ARP 请求或者应答来说,该值为 6。
协议地址长度(Protocol size): IP 地址的长度,以字节为单位。对于以太网上 IP 地址的 ARP 请求或者应答来说,该值为 4。
OP(Opcode):操作码,用于表示该数据包为 ARP 请求或者 ARP 应答。 1 表示 ARP 请求, 2 表示 ARP应答。
源 MAC 地址:发送端的硬件地址。
源 IP 地址:发送端的协议(IP)地址,如 192.168.1.102。
目的 MAC 地址:接收端的硬件地址,在 ARP 请求时由于不知道接收端 MAC 地址,因此该字段为广播地址, 即 48’hff_ff_ff_ff_ff_ff。
目的 IP 地址:接收端的协议(IP)地址,如 192.168.1.10。
四、RGMII接口
1、接口说明
ETH_TXCTL 和 ETH_RXCTL 控制信号同样采用 DDR 的方式在一个时钟周期内传输两位控制信号,即上升沿发送/接收数据使能(TX_EN/RX_ DV)信号,下降沿发送/接收使能信号与错误信号的异或值(TX_ERR xor TX_EN、 RX_ERR xor RX_DV)。当 RX_DV 为高电平(表示数据有效), RX_ERR 为低电平(表示数据无错误),则异或的结果值为高电平,因此只有当 ETH_RXCTL和 ETH_TXCTL 信号的上下沿同时为高电平时,发送和接收的数据有效且正确。
2、接口时序
(1)接收时序:
RGMII 发送端口正常模式下,需要满足 TXC 的上下边沿与 TXD 和 TX_CTL 信号的中间位置对齐, TXC 的时钟周期为 8ns,单个高电平或者低电平为 4ns, TXC 相对于 TXD 和TX_CTL 延时约 2ns。
(2)发送时序
RGMII 发送端口延时模式下,需要满足 TXC 的上下边沿与 TXD 和 TX_CTL 信号对齐,相位相同。本次实验 RTL8211FD RGMII 发送端口采用延时模式,其模式由硬件上的特殊引脚外接上下拉电阻进行配置。
五、硬件设计
已做上拉处理,芯片已经处于延时模式,代码中无需在额外添加延时
六、xilinx原语
Xilinx原语——FPGA学习笔记4_fpga 源语作用-CSDN博客
IDDR原语:
IDDR #(
.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED" ), //模式选择
.INIT_Q1 (1'b0 ), //设置初值
.INIT_Q2 (1'b0 ), //设置初值
.SRTYPE ("SYNC" ) //复位 同步/异步
)
IDDR_u0
(
.Q1 (w_rec_data[rxd_i] ), // 1-bit output for positive edge of clock
.Q2 (w_rec_data[rxd_i +4] ), // 1-bit output for negative edge of clock
.C (w_rxc_bufio ), //时钟
.CE (1 ), //输入时钟使能
.D (w_rxd_idly[rxd_i] ), //输入管脚
.R (0 ), //复位
.S (0 ) //置位
);
ODDR原语:
ODDR #(
.DDR_CLK_EDGE ("OPPOSITE_EDGE" ), //模式配置
.INIT (1'b0 ), //初值
.SRTYPE ("SYNC" ) //复位 同步/异步
)
ODDR_u
(
.Q (o_txd[txd_i] ), //输出数据
.C (w_txc ), //输入时钟
.CE (1 ), //时钟输入使能
.D1 (w_send_d1[txd_i] ), //数据1 上升沿
.D2 (w_send_d2[txd_i] ), //数据2 下降沿
.R (0 ), //复位
.S (0 ) //置数
);
IDLAY:
七、程序设计
1、RGMII2GMII
(1)RX
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/14 21:19:29
// Design Name:
// Module Name: rgmii_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module rgmii_rx(
input idelay_clk , //200M
//以太网RGMII接口
input rgmii_rxc , //RGMII接收时钟
input rgmii_rx_ctl , //RGMII接收数据控制信号
input [3:0] rgmii_rxd , //RGMII接收数据
//以太网GMII接口
output gmii_rx_clk , //GMII接收时钟
output gmii_rx_dv , //GMII接收数据有效信号
output [7:0] gmii_rxd //GMII接收数据
);
parameter IDELAY_VALUE = 0;
wire rgmii_rxc_bufg ;//全局时钟缓存
wire rgmii_rxc_bufio ;//全局时钟IO缓存
wire rgmii_rx_ctl_delay ;//
wire [1:0] gmii_rxdv_t ;
wire [3:0] rgmii_rxd_delay ;
assign gmii_rx_clk = rgmii_rxc_bufg;
assign gmii_rx_dv = gmii_rxdv_t[0] & gmii_rxdv_t[1] ;
//全局时钟缓存
BUFG BUFG_inst (
.O(rgmii_rxc_bufg), // 1-bit output: Clock output
.I(rgmii_rxc) // 1-bit input: Clock input
);
//全局时钟IO缓存
BUFIO BUFIO_inst (
.O(rgmii_rxc_bufio), // 1-bit output: Clock output (connect to I/O clock loads).
.I(rgmii_rxc) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
);
//输入延时控制
// Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
(* IODELAY_GROUP = "rgmii_rx_delay"*)
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
);
//rgmii_rx_ctl输入延时与双沿采样
(* IODELAY_GROUP = "rgmii_rx_delay" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYE2 #(
.IDELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(IDELAY_VALUE), // Input delay tap setting (0-31)
.REFCLK_FREQUENCY(200.0) // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
)
IDELAYE2_inst (
.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 (5'b0 ), // 5-bit input: Counter value input
.DATAIN (1'b0 ), // 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
);
//rgmii_rx_ctl输入延时与双沿采样
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "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_inst (
.Q1 (gmii_rxdv_t[0] ), // 1-bit output for positive edge of clock
.Q2 (gmii_rxdv_t[1] ), // 1-bit output for negative edge of clock
.C (rgmii_rxc_bufio ), // 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
);
//rgmii_rxd输入延时与双沿采样
genvar i;
generate for ( i = 0 ; i < 4 ; i = i + 1 )
(* IODELAY_GROUP = "rgmii_rx_delay" *)
begin : rxdata_bus
(* IODELAY_GROUP = "rgmii_rx_delay" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYE2 #(
.IDELAY_TYPE ("FIXED" ), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE (IDELAY_VALUE ), // Input delay tap setting (0-31)
.REFCLK_FREQUENCY (200.0 ) // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
)
IDELAYE2_inst (
.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 (5'b0 ), // 5-bit input: Counter value input
.DATAIN (1'b0 ), // 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
);
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "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_inst (
.Q1 (gmii_rxd[i] ), // 1-bit output for positive edge of clock
.Q2 (gmii_rxd[4+i] ), // 1-bit output for negative edge of clock
.C (rgmii_rxc_bufio ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D (rgmii_rxd_delay[i] ), // 1-bit DDR data input
.R (1'b0 ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
end
endgenerate
endmodule
(2)TX
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/15 12:52:31
// Design Name:
// Module Name: rgmii_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module rgmii_tx(
//GMII发送端口
input gmii_tx_clk , //GMII发送时钟
input gmii_tx_en , //GMII输出数据有效信号
input [7:0] gmii_txd , //GMII输出数据
//RGMII发送端口
output rgmii_txc , //RGMII发送数据时钟
output rgmii_tx_ctl , //RGMII输出数据有效信号
output [3:0] rgmii_txd //RGMII输出数据
);
assign rgmii_txc = gmii_tx_clk;
//输出双沿采样寄存器 rgmii_tx_ctl
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT (1'b0 ), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q (rgmii_tx_ctl ), // 1-bit DDR output
.C (gmii_tx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D1 (gmii_tx_en ), // 1-bit data input (positive edge)
.D2 (gmii_tx_en ), // 1-bit data input (negative edge)
.R (1'b0 ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
genvar i ;
generate for ( i = 0 ; i < 4 ; i = i + 1 )
begin : txdata_bus
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT (1'b0 ), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q (rgmii_txd[i] ), // 1-bit DDR output
.C (gmii_tx_clk ), // 1-bit clock input
.CE (1'b1 ), // 1-bit clock enable input
.D1 (gmii_txd[i] ), // 1-bit data input (positive edge)
.D2 (gmii_txd[4+i] ), // 1-bit data input (negative edge)
.R (1'b0 ), // 1-bit reset
.S (1'b0 ) // 1-bit set
);
end
endgenerate
endmodule
2、ARP
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/15 14:41:05
// Design Name:
// Module Name: arp_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module arp_top(
input idelay_clk ,
input rst_n , //复位信号,低电平有效
//GMII接口
input gmii_rx_clk, //GMII接收数据时钟
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
input gmii_tx_clk, //GMII发送数据时钟
output gmii_tx_en , //GMII输出数据有效信号
output [7:0] gmii_txd , //GMII输出数据
//用户接口
output arp_rx_done, //ARP接收完成信号
output arp_rx_type, //ARP接收类型 0:请求 1:应答
output [47:0] src_mac , //接收到目的MAC地址
output [31:0] src_ip , //接收到目的IP地址
input arp_tx_en , //ARP发送使能信号
input arp_tx_type, //ARP发送类型 0:请求 1:应答
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
output tx_done //以太网发送完成信号
);
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
wire crc_en ;
wire crc_clr ;
wire [32:0] crc_data ;
wire [32:0] crc_next ;
//
arp_rx
#(
//开发板MAC地址 00-11-22-33-44-55
.BOARD_MAC(BOARD_MAC),
//开发板IP地址 192.168.1.10
.BOARD_IP(BOARD_IP)
)
u_arp_rx
(
//input
.clk (gmii_rx_clk ) , //时钟信号
.idelay_clk (idelay_clk ) ,
.rst_n (rst_n ) , //复位信号,低电平有效
.gmii_rx_dv (gmii_rx_dv ) , //GMII输入数据有效信号
.gmii_rxd (gmii_rxd ) , //GMII输入数据
//output
.arp_rx_done (arp_rx_done ) , //ARP接收完成信号
.arp_rx_type (arp_rx_type ) , //ARP接收类型 0:请求 1:应答
.src_mac (src_mac ) , //接收到的源MAC地址
.src_ip (src_ip ) //接收到的源IP地址
);
arp_tx #(
.BOARD_MAC (BOARD_MAC ), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)u_arp_tx(
//input
.clk (gmii_tx_clk ) , //时钟信号
.idelay_clk (idelay_clk ) ,
.rst_n (rst_n ) , //复位信号,低电平有效
.arp_tx_en (arp_tx_en ) , //ARP发送使能信号
.arp_tx_type (arp_tx_type ) , //ARP发送类型 0:请求 1:应答
.des_mac (des_mac ) , //发送的目标MAC地址
.des_ip (des_ip ) , //发送的目标IP地址
.crc_data (crc_data ) , //CRC校验数据
.crc_next (crc_next[31:24]) , //CRC下次校验完成数据
//output
.tx_done (tx_done ) , //以太网发送完成信号
.gmii_tx_en (gmii_tx_en ) , //GMII输出数据有效信号
.gmii_txd (gmii_txd ) , //GMII输出数据
.crc_en (crc_en ) , //CRC开始校验使能
.crc_clr (crc_clr ) //CRC数据复位信号
);
crc32_d8 u_crc32_d8(
.clk (gmii_tx_clk ) , //时钟信号
.rst_n (rst_n ) , //复位信号,低电平有效
.data (gmii_txd ) , //输入待校验8位数据
.crc_en (crc_en ) , //crc使能,开始校验标志
.crc_clr (crc_clr ) , //crc数据复位信号
.crc_data (crc_data ) , //CRC校验数据
.crc_next (crc_next ) //CRC下次校验完成数据
);
endmodule
(1)RX
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/15 14:43:57
// Design Name:
// Module Name: arp_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module arp_rx
#(
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55,
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10}
)
(
input clk , //时钟信号
input idelay_clk ,
input rst_n , //复位信号,低电平有效
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
output reg arp_rx_done, //ARP接收完成信号
output reg arp_rx_type, //ARP接收类型 0:请求 1:应答
output reg [47:0] src_mac , //接收到的源MAC地址
output reg [31:0] src_ip //接收到的源IP地址
);
//parameter define
localparam st_idle = 5'b0_0001; //初始状态,等待接收前导码
localparam st_preamble = 5'b0_0010; //接收前导码状态
localparam st_eth_head = 5'b0_0100; //接收以太网帧头
localparam st_arp_data = 5'b0_1000; //接收ARP数据
localparam st_rx_end = 5'b1_0000; //接收结束
localparam ETH_TPYE = 16'h0806; //以太网帧类型 ARP
localparam ALL_MAC = 48'hff_ff_ff_ff_ff_ff;
reg skip_en ;
reg error_en ;
reg [4:0] cur_state ;
reg [4:0] next_state ;
reg [4:0] cnt ;
reg [47:0] des_mac_t ;
reg [47:0] sou_mac_t ;
reg [15:0] eth_type ;
reg [15:0] op_data ;
reg [47:0] src_mac_t ; //源MAC
reg [31:0] src_ip_t ; //源IP
reg [31:0] des_ip_t ; //目标IP
//
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cur_state <= st_idle ;
end
else begin
cur_state <= next_state ;
end
end
//
always @(*) begin
if (!rst_n) begin
next_state <= st_idle;
end
else begin
case (cur_state)
st_idle :begin //等待前导码
if (skip_en) begin
next_state = st_preamble;
end
else begin
next_state = st_idle;
end
end
st_preamble :begin //接收前导码
if (skip_en) begin
next_state = st_eth_head;
end
else if(error_en)begin
next_state = st_rx_end;
end
else begin
next_state = st_preamble;
end
end
st_eth_head :begin //接收以太网帧头
if (skip_en) begin
next_state = st_arp_data;
end
else if(error_en)begin
next_state = st_rx_end;
end
else begin
next_state = st_eth_head;
end
end
st_arp_data :begin //接收ARP数据
if (skip_en) begin
next_state = st_rx_end;
end
else if(error_en)begin
next_state = st_rx_end;
end
else begin
next_state = st_arp_data;
end
end
st_rx_end :begin //接收结束
if (skip_en) begin
next_state = st_idle;
end else begin
next_state = st_rx_end;
end
end
default: next_state = st_idle ;
endcase
end
end
//
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
skip_en <= 1'b0;
cnt <= 6'd0;
error_en <= 1'b0;
des_mac_t <= 48'd0;
sou_mac_t <= 48'd0;
eth_type <= 16'd0;
op_data <= 16'd0;
des_ip_t <= 32'd0;
src_ip_t <= 32'd0;
arp_rx_done <= 1'd0;
src_mac <= 48'd0;
src_ip <= 32'd0;
arp_rx_type <= 1'd0;
src_mac_t <= 48'd0;
end
else begin
skip_en <= 1'b0;
error_en <= 1'b0;
arp_rx_done <= 1'b0;
case (next_state)
st_idle :begin
if ((gmii_rx_dv == 1'b1)&&(gmii_rxd == 8'h55)) begin
skip_en <= 1'b1;
end else begin
skip_en <= 1'b0;
end
end
st_preamble :begin
if (gmii_rx_dv == 1'b1) begin
cnt <= cnt + 1'b1;
if ((cnt< 5'd6)&&(gmii_rxd != 8'h55)) begin
error_en <= 1'b1;
end
else if(cnt == 5'd6)begin//1+6+1
cnt <= 5'd0;
if (gmii_rxd == 8'hd5) begin
skip_en <= 1'b1;
error_en <= 1'b0;
end
else begin
skip_en <= 1'b0;
error_en <= 1'b1;
end
end
end else begin
skip_en <= skip_en ;
error_en <= error_en;
end
end
st_eth_head :begin
if (gmii_rx_dv == 1'b1) begin
cnt <= cnt + 1'b1;
if (cnt < 5'd6) begin
des_mac_t <= {des_mac_t[39:0],gmii_rxd};
end
else if (cnt == 5'd6)begin
if ((des_mac_t != BOARD_MAC ) && ( des_mac_t != ALL_MAC)) begin
error_en <= 1'b1;
end else begin
error_en <= 1'b0;
end
end
else if ((cnt >= 5'd6)&&(cnt < 5'd12))begin
sou_mac_t <= {sou_mac_t[39:0],gmii_rxd};
end
else if (cnt == 5'd12) begin
eth_type[15:8] <= gmii_rxd;
end
else if (cnt == 5'd13)begin
eth_type[7:0] <= gmii_rxd;
cnt <= 5'd0;
if ((eth_type[15:8] != ETH_TPYE[15:8])&&(gmii_rxd != ETH_TPYE[7:0])) begin //eth_type[7:0]尚未同步 所以判断gmii_rxd
skip_en <= 1'b0;
error_en <= 1'b1;
end else begin
skip_en <= 1'b1;
error_en <= 1'b0;
end
end
end
else begin
skip_en <= skip_en ;
error_en <= 1'b1 ;
end
end
st_arp_data :begin
if (gmii_rx_dv == 1'b1) begin
cnt <= cnt + 1'b1;
if (cnt == 5'd6) begin
op_data[15:8] <= gmii_rxd;
end
else if (cnt == 5'd7)begin
op_data[7:0] <= gmii_rxd;
end
else if ((cnt >= 5'd8)&&(cnt < 5'd14)) begin
src_mac_t <= {src_mac_t[36:0],gmii_rxd}; //源MAC
end
else if ((cnt >= 5'd14)&&(cnt < 5'd18))begin
src_ip_t <= {src_ip_t[23:0],gmii_rxd}; //源IP
end
else if ((cnt >= 5'd24)&&(cnt < 5'd28)) begin
des_ip_t <= {des_ip_t[23:0],gmii_rxd}; //目标IP
end
else if (des_ip_t == BOARD_IP) begin
cnt = 5'd0;
if ((op_data == 16'd1)||(op_data == 16'd2)) begin //01 请求 10 应答
skip_en <= 1'b1 ;
arp_rx_done <= 1'b1 ;
src_mac <= src_mac_t ;
src_ip <= src_ip_t ;
src_mac_t <= 48'd0 ;
src_ip_t <= 32'd0 ;
des_mac_t <= 48'd0 ;
des_ip_t <= 32'd0 ;
if (op_data == 16'd1) begin //请求
arp_rx_type <= 1'b0 ; //APR请求
end
else begin
arp_rx_type <= 1'b1 ; //ARP应答
end
end
else begin
error_en <= 1'b1 ;
skip_en <= 1'b0 ;
end
end
else begin
error_en <= error_en ;
end
end
else begin
error_en <= 1'b1 ;
skip_en <= skip_en ;
end
end
st_rx_end :begin
cnt <= 5'd0 ;
error_en <= 1'b0 ;
//传输数据包
if ((gmii_rx_dv == 1'b0)&&(skip_en == 1'b0)) begin
skip_en <= 1'b1;
end
else begin
skip_en <= 1'b0;
end
end
default: ;
endcase
end
end
ila_0 u_ila_0 (
.clk(idelay_clk), // input wire clk
.probe0(gmii_rxd), // input wire [7:0] probe0
.probe1(cnt), // input wire [4:0] probe1
.probe2(next_state), // input wire [4:0] probe1
.probe3(skip_en), // input wire [4:0] probe1
.probe4(error_en) // input wire [4:0] probe1
);
endmodule
(2)TX
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/12/15 20:16:45
// Design Name:
// Module Name: arp_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module arp_tx(
input clk , //时钟信号
input idelay_clk ,
input rst_n , //复位信号,低电平有效
input arp_tx_en , //ARP发送使能信号
input arp_tx_type, //ARP发送类型 0:请求 1:应答
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
input [31:0] crc_data , //CRC校验数据
input [7:0] crc_next , //CRC下次校验完成数据
output reg tx_done , //以太网发送完成信号
output reg gmii_tx_en , //GMII输出数据有效信号
output reg [7:0] gmii_txd , //GMII输出数据
output reg crc_en , //CRC开始校验使能
output reg crc_clr //CRC数据复位信号
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
localparam st_idle = 5'b0_0001; //初始状态,等待开始发送信号
localparam st_preamble = 5'b0_0010; //发送前导码+帧起始界定符
localparam st_eth_head = 5'b0_0100; //发送以太网帧头
localparam st_arp_data = 5'b0_1000; //
localparam st_crc = 5'b1_0000; //发送CRC校验值
localparam ETH_TYPE = 16'h0806 ; //以太网帧类型 ARP协议
localparam HD_TYPE = 16'h0001 ; //硬件类型 以太网
localparam PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
localparam MIN_DATA_NUM = 6'd46 ; //以太网数据最小为46个字节,不足部分填充数据
reg tx_en_d0 ;
reg tx_en_d1 ;
reg tx_en_d2 ;
reg [4:0] cur_state ;
reg [4:0] next_state ;
reg skip_en ;
reg [7:0] preamble [7:0] ;
reg [7:0] eth_head [13:0] ;
reg [7:0] arp_data [27:0] ;
reg [5:0] cnt ;
reg [5:0] data_cnt ;
reg tx_done_t ;
assign pos_tx_en = (!tx_en_d2) & tx_en_d1; //触发信号上升沿
//对arp_tx_en信号延时打拍 抓上升沿
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_en_d0 <= 1'b0;
tx_en_d1 <= 1'b0;
tx_en_d2 <= 1'b0;
end else begin
tx_en_d0 <= arp_tx_en ;
tx_en_d1 <= tx_en_d0 ;
tx_en_d2 <= tx_en_d1 ;
end
end
//
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cur_state <= st_idle ;
end
else begin
cur_state <= next_state ;
end
end
//
always @(*) begin
if (!rst_n) begin
next_state = st_idle ;
end
else begin
case (cur_state)
st_idle :begin
if (skip_en) begin
next_state = st_preamble ;
end
else begin
next_state = st_idle ;
end
end
st_preamble :begin
if (skip_en) begin
next_state = st_eth_head ;
end
else begin
next_state = st_preamble ;
end
end
st_eth_head :begin
if (skip_en) begin
next_state = st_arp_data ;
end else begin
next_state = st_eth_head ;
end
end
st_arp_data :begin
if (skip_en) begin
next_state = st_crc ;
end else begin
next_state = st_arp_data ;
end
end
st_crc :begin
if (skip_en) begin
next_state = st_idle ;
end else begin
next_state = st_crc ;
end
end
default: next_state = st_idle ;
endcase
end
end
//
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
skip_en <= 1'b0;
gmii_tx_en <= 1'b0;
cnt <= 6'b0;
data_cnt <= 6'b0;
gmii_txd <= 8'd0;
crc_en <= 1'b0;
tx_done_t <= 1'b0;
//初始化数组
//前导码 7个8'h55 + 1个8'hd5
preamble[0] <= 8'h55;
preamble[1] <= 8'h55;
preamble[2] <= 8'h55;
preamble[3] <= 8'h55;
preamble[4] <= 8'h55;
preamble[5] <= 8'h55;
preamble[6] <= 8'h55;
preamble[7] <= 8'hd5;
//以太网帧头
eth_head[0] <= DES_MAC[47:40]; //目的MAC地址
eth_head[1] <= DES_MAC[39:32];
eth_head[2] <= DES_MAC[31:24];
eth_head[3] <= DES_MAC[23:16];
eth_head[4] <= DES_MAC[15:8];
eth_head[5] <= DES_MAC[7:0];
eth_head[6] <= BOARD_MAC[47:40]; //源MAC地址
eth_head[7] <= BOARD_MAC[39:32];
eth_head[8] <= BOARD_MAC[31:24];
eth_head[9] <= BOARD_MAC[23:16];
eth_head[10] <= BOARD_MAC[15:8];
eth_head[11] <= BOARD_MAC[7:0];
eth_head[12] <= ETH_TYPE[15:8]; //以太网帧类型
eth_head[13] <= ETH_TYPE[7:0];
//ARP数据
arp_data[0] <= HD_TYPE[15:8]; //硬件类型
arp_data[1] <= HD_TYPE[7:0];
arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
arp_data[3] <= PROTOCOL_TYPE[7:0];
arp_data[4] <= 8'h06; //硬件地址长度,6
arp_data[5] <= 8'h04; //协议地址长度,4
arp_data[6] <= 8'h00; //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
arp_data[7] <= 8'h01;
arp_data[8] <= BOARD_MAC[47:40]; //发送端(源)MAC地址
arp_data[9] <= BOARD_MAC[39:32];
arp_data[10] <= BOARD_MAC[31:24];
arp_data[11] <= BOARD_MAC[23:16];
arp_data[12] <= BOARD_MAC[15:8];
arp_data[13] <= BOARD_MAC[7:0];
arp_data[14] <= BOARD_IP[31:24]; //发送端(源)IP地址
arp_data[15] <= BOARD_IP[23:16];
arp_data[16] <= BOARD_IP[15:8];
arp_data[17] <= BOARD_IP[7:0];
arp_data[18] <= DES_MAC[47:40]; //接收端(目的)MAC地址
arp_data[19] <= DES_MAC[39:32];
arp_data[20] <= DES_MAC[31:24];
arp_data[21] <= DES_MAC[23:16];
arp_data[22] <= DES_MAC[15:8];
arp_data[23] <= DES_MAC[7:0];
arp_data[24] <= DES_IP[31:24]; //接收端(目的)IP地址
arp_data[25] <= DES_IP[23:16];
arp_data[26] <= DES_IP[15:8];
arp_data[27] <= DES_IP[7:0];
end
else begin
skip_en <= 1'b0;
tx_done_t <= 1'b0;
case (next_state)
st_idle :begin
gmii_tx_en <= 1'b0;
if (pos_tx_en) begin
skip_en <= 1'b1;
if ((des_mac != 48'd0)||(des_ip != 32'd0)) begin
eth_head[0] <= des_mac[47:40];
eth_head[1] <= des_mac[39:32];
eth_head[2] <= des_mac[31:24];
eth_head[3] <= des_mac[23:16];
eth_head[4] <= des_mac[15:8];
eth_head[5] <= des_mac[7:0];
arp_data[18] <= des_mac[47:40];
arp_data[19] <= des_mac[39:32];
arp_data[20] <= des_mac[31:24];
arp_data[21] <= des_mac[23:16];
arp_data[22] <= des_mac[15:8];
arp_data[23] <= des_mac[7:0];
arp_data[24] <= des_ip[31:24];
arp_data[25] <= des_ip[23:16];
arp_data[26] <= des_ip[15:8];
arp_data[27] <= des_ip[7:0];
end
end
else begin
skip_en <= 1'b0;
end
if (arp_tx_type == 1'b0) begin
arp_data[7] <= 8'h01; //请求
end
else begin
arp_data[7] <= 8'h02; //应答
end
end
st_preamble :begin //前导码 7个8'h55 + 1个8'hd5
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if (cnt == 6'd7) begin
skip_en <= 1'b1;
cnt <= 6'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
st_eth_head :begin //以太网帧头
gmii_tx_en <= 1'b1;
gmii_txd <= eth_head[cnt];
crc_en <= 1'b1;
if (cnt == 6'd13) begin
skip_en <= 1'b1;
cnt <= 6'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
st_arp_data :begin //ARP数据
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
if (cnt == MIN_DATA_NUM - 1'b1) begin
skip_en <= 1'b1;
cnt <= 6'b0;
data_cnt<= 6'b0;
end else begin
cnt <= cnt + 1;
end
if (data_cnt <= 6'd27) begin
data_cnt <= data_cnt + 1'b1;
gmii_txd <= arp_data[data_cnt];
end
else begin
gmii_txd <= 8'd0;
end
end
st_crc :begin
gmii_tx_en <= 1'b1;
cnt <= cnt + 1'b1;
crc_en <= 1'b0;
if(cnt == 6'd0)
// gmii_txd <= {~crc_next[24], ~crc_next[25], ~crc_next[26],~crc_next[27],
// ~crc_next[28], ~crc_next[29], ~crc_next[30],~crc_next[31]};
gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
else if(cnt == 6'd1)
gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],
~crc_data[19], ~crc_data[20], ~crc_data[21],
~crc_data[22],~crc_data[23]};
else if(cnt == 6'd2) begin
gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],
~crc_data[11],~crc_data[12], ~crc_data[13],
~crc_data[14],~crc_data[15]};
end
else if(cnt == 6'd3) begin
gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
tx_done_t <= 1'b1;
skip_en <= 1'b1;
cnt <= 1'b0;
end
end
default: ;
endcase
end
end
//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_done <= 1'b0;
crc_clr <= 1'b0;
end
else begin
tx_done <= tx_done_t;
crc_clr <= tx_done_t;
end
end
ila_1 u_ila_1 (
.clk(idelay_clk), // input wire clk
.probe0(gmii_txd), // input wire [7:0] probe0
.probe1(cnt), // input wire [4:0] probe1
.probe2(next_state), // input wire [4:0] probe1
.probe3(skip_en), // input wire [4:0] probe1
.probe4(error_en) // input wire [4:0] probe1
);
endmodule
(3)CRC
八、仿真
发送请求包:
发送应答包: