一、实验任务
本节的实验任务是使用FPGA开发板上的以太网接口,和上位机实现 ARP请求和应答的功能。当上位机发送ARP请求时,开发板返回ARP应答数据。当按下开发板的触摸按键时,开发板发送ARP请求,此时上位机返回应答数据。
二、ARP简介
(一下简介来自正点原子达芬奇之FPGA开发指南)
ARP(Address Resolution Protocol),即地址解析协议,是根据 IP 地址(逻辑地址)获取 MAC 地址的一种 TCP/IP 协议。在以太网通信中,数据是以“帧”的格式进行传输的,帧格式里面包含目的主机的 MAC地址。源主机的应用程序知道目的主机的 IP 地址,却不知道目的主机的 MAC 地址。而目的主机的 MAC地址直接被网卡接收和解析,当解析到目的 MAC 地址非本地 MAC 地址时,则直接丢弃该包数据,因此在通信前需要先获得目的的 MAC 地址,而 ARP 协议正是实现了此功能。
ARP 协议的基本功能是通过目的设备的 IP 地址,查询目的设备的 MAC 地址,以保证通信的顺利进行。MAC 地址在网络中表示网卡的 ID,每个网卡都需要并有且仅有一个 MAC 地址。在获取到目的 MAC 地址之后,将目的 MAC 地址更新至 ARP 缓存表中,称为 ARP 映射,下次通信时,可以直接从 ARP 缓存表中获取,而不用重新通过 ARP 获取 MAC 地址。但一般 ARP 缓存表会有过期时间,过期后需要重新通过 ARP协议进行获取。
ARP 映射是指将 IP 地址和 MAC 地址映射起来,分为静态映射和动态映射
1、以太网帧格式
以太网技术的正式标准是 IEEE 802.3,它规定了以太网传输数据的帧结构,我们可以把以太网 MAC 层理解成高速公路, 我们必须遵循它的规则才能在上面通行, 以太网 MAC 层帧格式如图
前导码(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 个字节。最大值 1500 称为以太网的最大传输单元(MTU, Maximum Transmission Unit),之所以限制最大传输单元是因为在多个计算机的数据帧排队等待传输时,如果某个数据帧太大的话,那么其它数据帧等待的时间就会加长,导致体验变差,这就像一个十字路口的红绿灯,你可以让绿灯持续亮一小时,但是等红灯的人一定不愿意的。另外还要考虑网络 I/O控制器缓存区资源以及网络最大的承载能力等因素, 因此最大传输单元是由各种综合因素决定的。为了避免增加额外的配置, 通常以太网的有效数据字段小于 1500 个字节。
帧检验序列(FCS, Frame Check Sequence) : 为了确保数据的正确传输, 在数据的尾部加入了 4 个字节的循环冗余校验码(CRC 校验) 来检测数据是否传输错误。 CRC 数据校验从以太网帧头开始即不包含前导码和帧起始界定符。 通用的 CRC 标准有 CRC-8、 CRC-16、 CRC-32、 CRC-CCIT,其中在网络通信系统中应用最广泛的是 CRC-32 标准。
在这里还有一个要注意的地方就是以太网相邻两帧之间的时间间隔, 即帧间隙(IFG, Interpacket Gap)。帧间隙的时间就是网络设备和组件在接收一帧之后,需要短暂的时间来恢复并为接收下一帧做准备的时间, IFG 的最小值是 96 bit time,即在媒介中发送 96 位原始数据所需要的时间,在不同媒介中 IFG 的最小值是不一样的。 不管 10M/100M/1000M 的以太网,两帧之间最少要有 96bit time, IFG 的最少间隔时间计算方法如下:
10Mbit/s 最小时间为: 96*100ns = 9600ns;
100Mbit/s 最小时间为: 96*10ns = 960ns;
1000Mbit/s 最小时间为: 96*1ns = 96ns。
2、ARP 协议
(1)ARP 数据包格式如下图
![](https://img-blog.csdnimg.cn/direct/172ca42e078f44eeae52de75c7eed918.png)
硬件类型(Hardware type):硬件地址的类型, 1 表示以太网地址。
协议类型(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。
(2)以太网传输 ARP 报文的格式
28 字节的 ARP 数据位于以太网帧格式的数据段。由于以太网数据段最少为 46 个字节,而 ARP 数据包总长度为 28 个字节,因此在 ARP 数据段后面需要填充 18 个字节的数据,以满足以太网传输格式的要求。这个填充的过程称为 Padding(填充),填充的数据可以为任意值,但一般为 0。
三、代码讲解
1、gmii_to_rgmii模块
RGMII to GMII接口时序如下图:
(1)rgmii_rx部分
module rgmii_rx(
input idelay_clk , //200Mhz时钟,IDELAY时钟
//以太网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 define
parameter IDELAY_VALUE = 0;
//wire define
wire rgmii_rxc_bufg; //全局时钟缓存
wire rgmii_rxc_bufio; //全局时钟IO缓存
wire [3:0] rgmii_rxd_delay; //rgmii_rxd输入延时
wire rgmii_rx_ctl_delay; //rgmii_rx_ctl输入延时
wire [1:0] gmii_rxdv_t; //两位GMII接收有效信号
//*****************************************************
//** main code
//*****************************************************
assign gmii_rx_clk = rgmii_rxc_bufg;
assign gmii_rx_dv = gmii_rxdv_t[0] & gmii_rxdv_t[1];
//全局时钟缓存
BUFG BUFG_inst (
.I (rgmii_rxc), // 1-bit input: Clock input
.O (rgmii_rxc_bufg) // 1-bit output: Clock output
);
//全局时钟IO缓存
BUFIO BUFIO_inst (
.I (rgmii_rxc), // 1-bit input: Clock input
.O (rgmii_rxc_bufio) // 1-bit output: Clock output
);
//输入延时控制
// 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" *)
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
)
u_delay_rx_ctrl (
.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: enable increment/decrement
.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
.LD (1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register
.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"
) u_iddr_rx_ctl (
.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" *)
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
)
u_delay_rxd (
.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: enable increment/decrement
.CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion
.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: Inc/Decrement tap delay
.LD (1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register
.REGRST (1'b0) // 1-bit input: Active-high reset tap-delay
);
//输入双沿采样寄存器
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"
) u_iddr_rxd (
.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 rgmii_rxc_bufio
.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
① 全局时钟原语调用![](https://img-blog.csdnimg.cn/direct/cdd60b79bf0743059333e71fb21c800d.png)
BUFG输出信号直接给到gmii_rx_clk(GMII接收时钟)
BUFIO输出信号rgmii_rxc_bufio(全局时钟IO缓存)后续进入IDDR做输入时钟
gmii_rxdv_t[1]实际上是由rx_dv ^ rx_er得到的,如果rx_dv为1,rx_er为0,gmii_rxdv_t[1]即为1,gmii_rxdv_t[0]为rx_dv为1,所以gmii_rx_dv为1表示数据有效。如果rx_dv为1,rx_er为1,gmii_rxdv_t[1]即为0,gmii_rxdv_t[0]为rx_dv为1,所以gmii_rx_dv为0表示有数据无效;
②IDELAYCTRL原语调用(必须联合IDELAYE使用)
IDELAYCTRL与IDELAYE2分组相同均为rgmii_rx_delay(表明在同一时钟区域)。IDELAYCTRL参考时钟为200MHz,对应延时为78ps。
1) rgmii_rx_ctl输入延时与双沿采
IDELAYE2模式配置为FIXED(固定模式),虽然给的IDELAY_VALUE(延时参数)为0,但是输入相对输出还是有600ps延迟,将rgmii_rx_ctl(RGMII接收数据控制信号)(因为是从IO接入,所以要接入IDATAIN,DATAIN引脚为逻辑接入)接入,输出rgmii_rx_ctl_delay给到IDDR进行双沿采样。
①IDDR原语调用 ![](https://img-blog.csdnimg.cn/direct/c4f29e55e8284fc1860b85d43fef17d6.png)
模式为SAME_EDGE_PIPELINED(相同沿流水模式),初值均为0,采用同步复位。
2)rgmii_rxd输入延时与双沿采
IDELAYE2模式配置为FIXED(固定模式),虽然给的IDELAY_VALUE(延时参数)为0,但是输入相对输出还是有600ps延迟,将rgmii_rxd[i](RGMII接收数据)(因为是从IO接入,所以要接入IDATAIN,DATAIN引脚为逻辑接入)接入,输出rgmii_rxd_delay[i]给到IDDR进行双沿采样。
①IDDR原语调用
模式为SAME_EDGE_PIPELINED(相同沿流水模式),初值均为0,采用同步复位。
(2)rgmii_tx部分
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输出数据
);
//*****************************************************
//** main code
//*****************************************************
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
//输出双沿采样寄存器 (rgmii_txd)
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
gmii_tx_clk因为PHY芯片已经有2ns延迟,所以直接给到rgmii_txc。如果没有PHY芯片的物理延迟,要取反后给到rgmii_txc。
1)rgmii_tx_ctl输出双沿采样寄存器
①ODDR原语调用
采用相同沿模式,初值Q为0,D1,D2均为gmii_tx_en,所有数据有效。
2) rgmii_txd输出双沿采样寄存器
采用相同沿模式,初值Q为0,D1上升沿给[3:0],下降沿给[7:4],进行双沿采样。
(3)gmii_to_rgmii顶层
module gmii_to_rgmii(
input idelay_clk , //IDELAY时钟
//以太网GMII接口
output gmii_rx_clk , //GMII接收时钟
output gmii_rx_dv , //GMII接收数据有效信号
output [7:0] gmii_rxd , //GMII接收数据
output gmii_tx_clk , //GMII发送时钟
input gmii_tx_en , //GMII发送数据使能信号
input [7:0] gmii_txd , //GMII发送数据
//以太网RGMII接口
input rgmii_rxc , //RGMII接收时钟
input rgmii_rx_ctl, //RGMII接收数据控制信号
input [3:0] rgmii_rxd , //RGMII接收数据
output rgmii_txc , //RGMII发送时钟
output rgmii_tx_ctl, //RGMII发送数据控制信号
output [3:0] rgmii_txd //RGMII发送数据
);
//parameter define
parameter IDELAY_VALUE = 0; //输入数据IO延时(如果为n,表示延时n*78ps)
//*****************************************************
//** main code
//*****************************************************
assign gmii_tx_clk = gmii_rx_clk;
//RGMII接收
rgmii_rx
#(
.IDELAY_VALUE (IDELAY_VALUE)
)
u_rgmii_rx(
.idelay_clk (idelay_clk),
.gmii_rx_clk (gmii_rx_clk),
.rgmii_rxc (rgmii_rxc ),
.rgmii_rx_ctl (rgmii_rx_ctl),
.rgmii_rxd (rgmii_rxd ),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd )
);
//RGMII发送
rgmii_tx u_rgmii_tx(
.gmii_tx_clk (gmii_tx_clk ),
.gmii_tx_en (gmii_tx_en ),
.gmii_txd (gmii_txd ),
.rgmii_txc (rgmii_txc ),
.rgmii_tx_ctl (rgmii_tx_ctl),
.rgmii_txd (rgmii_txd )
);
endmodule
PHY芯片已有2ns偏移延迟,此处无须延时。
2、ARP模块
arp时序如下图
(1)arp_rx模块
arp_rx时序如下 状态跳转图
st_idle:初始状态,等待接收前导码
st_preamble:接收前导码状态
st_eth_head:接收以太网帧头
st_arp_datd:接收ARP数据
st_rx_end:接收结束
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 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
//reg define
reg [4:0] cur_state ;
reg [4:0] next_state;
reg skip_en ; //控制状态跳转使能信号
reg error_en ; //解析错误使能信号
reg [4:0] cnt ; //解析数据计数器
reg [47:0] des_mac_t ; //接收到的目的MAC地址
reg [31:0] des_ip_t ; //接收到的目的IP地址
reg [47:0] src_mac_t ; //接收到的源MAC地址
reg [31:0] src_ip_t ; //接收到的源IP地址
reg [15:0] eth_type ; //以太网类型
reg [15:0] op_data ; //操作码
//*****************************************************
//** main code
//*****************************************************
//(三段式状态机)同步时序描述状态转移
always @(posedge 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(skip_en)
next_state = st_preamble;
else
next_state = st_idle;
end
st_preamble : begin //接收前导码
if(skip_en)
next_state = st_eth_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_preamble;
end
st_eth_head : begin //接收以太网帧头
if(skip_en)
next_state = st_arp_data;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_eth_head;
end
st_arp_data : begin //接收ARP数据
if(skip_en)
next_state = st_rx_end;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_arp_data;
end
st_rx_end : begin //接收结束
if(skip_en)
next_state = st_idle;
else
next_state = st_rx_end;
end
default : next_state = st_idle;
endcase
end
//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
error_en <= 1'b0;
cnt <= 5'd0;
des_mac_t <= 48'd0;
des_ip_t <= 32'd0;
src_mac_t <= 48'd0;
src_ip_t <= 32'd0;
eth_type <= 16'd0;
op_data <= 16'd0;
arp_rx_done <= 1'b0;
arp_rx_type <= 1'b0;
src_mac <= 48'd0;
src_ip <= 32'd0;
end
else begin
skip_en <= 1'b0;
error_en <= 1'b0;
arp_rx_done <= 1'b0;
case(next_state)
st_idle : begin //检测到第一个8'h5
if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
skip_en <= 1'b1;
end
st_preamble : begin
if(gmii_rx_dv) begin //解析前导码
cnt <= cnt + 5'd1;
if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //7个8'h55
error_en <= 1'b1;
else if(cnt==5'd6) begin
cnt <= 5'd0;
if(gmii_rxd==8'hd5) //1个8'hd5
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
end
end
st_eth_head : begin
if(gmii_rx_dv) begin
cnt <= cnt + 5'b1;
if(cnt < 5'd6)
des_mac_t <= {des_mac_t[39:0],gmii_rxd};
else if(cnt == 5'd6) begin
//判断MAC地址是否为开发板MAC地址或者公共地址
if((des_mac_t != BOARD_MAC)
&& (des_mac_t != 48'hff_ff_ff_ff_ff_ff))
error_en <= 1'b1;
end
else if(cnt == 5'd12)
eth_type[15:8] <= gmii_rxd; //以太网协议类型
else if(cnt == 5'd13) begin
eth_type[7:0] <= gmii_rxd;
cnt <= 5'd0;
if(eth_type[15:8] == ETH_TPYE[15:8] //判断是否为ARP协
&& gmii_rxd == ETH_TPYE[7:0])
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
end
end
st_arp_data : begin
if(gmii_rx_dv) begin
cnt <= cnt + 5'd1;
if(cnt == 5'd6)
op_data[15:8] <= gmii_rxd; //操作码
else if(cnt == 5'd7)
op_data[7:0] <= gmii_rxd;
else if(cnt >= 5'd8 && cnt < 5'd14) //源MAC地址
src_mac_t <= {src_mac_t[39:0],gmii_rxd};
else if(cnt >= 5'd14 && cnt < 5'd18) //源IP地址
src_ip_t<= {src_ip_t[23:0],gmii_rxd};
else if(cnt >= 5'd24 && cnt < 5'd28) //目标IP地址
des_ip_t <= {des_ip_t[23:0],gmii_rxd};
else if(cnt == 5'd28) begin
cnt <= 5'd0;
if(des_ip_t == BOARD_IP) begin //判断目的IP地址和
if((op_data == 16'd1) || (op_data == 16'd2)) begin
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)
arp_rx_type <= 1'b0; //ARP请求
else
arp_rx_type <= 1'b1; //ARP应答
end
else
error_en <= 1'b1;
end
else
error_en <= 1'b1;
end
end
end
st_rx_end : begin
cnt <= 5'd0;
//单包数据接收完成
if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
skip_en <= 1'b1;
end
default : ;
endcase
end
end
endmodule
st_idle:初始状态,等待接收前导码。gmii_rx_dv(GMII输入数据有效信号)为高检测到第一个55(前导码),将skip_en(控制状态跳转使能信号)拉高。
st_preamble:接收前导码状态 。gmii_rx_dv(GMII输入数据有效信号)为高,cnt计数器记录前导码数量。由于状态机次态,因此检测6个55是否正常(前导码),如有错误error_en(解析错误使能信号)拉高。没有错误在cnt为6下清零cnt计数器,判断gmii_rxd(GMII输入数据)是否为8'hd5,然后对skip_en或error_en进行处理。
st_eth_head:接收以太网帧头。gmii_rx_dv(GMII输入数据有效信号)为高,cnt计数器记录前帧头数量。接受前6位为目的MAC,寄存到des_mac_t中(此处采用移位寄存方法,一次缓存48为数据)。判断接收到的目的MAC是否与开发板MAC地址或者公共地址,如均不符合error_en(解析错误使能信号)拉高。6~12组数据为源MAC地址,FPGA端无需处理。cnt计数器为12,将第13组数据(协议类型高位)赋值给eth_type[15:8](以太网类型),cnt计数器为13,将第14组数据(协议类型高位)赋值给eth_type[7:0](以太网类型),计数器cnt清零。cnt计数器为13时,同步判断判断是否为ARP协议,因为是并行结构,所以分高低位分别比较。
st_arp_datd:接收ARP数据。gmii_rx_dv(GMII输入数据有效信号)为高,cnt计数器记录数据段数量。(只需读取OP段数据)首先发送gmii_rxd(操作码)高8位,在发送gmii_rxd(操作码)低8位。在cnt >= 5'd8 && cnt < 5'd14时间段,移位寄存源MAC地址,在cnt >= 5'd14 && cnt < 5'd18时间段,移位寄存器源IP地址,在cnt >= 5'd24 && cnt < 5'd28时间段,移位目标IP地址;28位ARP数据接收完成后,进行判断目的IP地址和操作码,如果不为目标IP返回error_en拉高,正确目标IP下,判断是否为请求或应答包,skip_en(控制状态跳转使能信号)拉高,arp_rx_done(ARP接收完成信号)拉高,src_mac <= src_mac_t(接受源目的MAC寄存器),src_ip <= src_ip_t(接受源目的IP寄存器),之后清除寄存器缓存值。如果op_data为1,则arp_rx_type <= 1'b0(ARP请求),否则arp_rx_type <= 1'b1(ARP应答)。
st_rx_end:接收结束。gmii_rx_dv为0(数据包接收完成),skip_en为0(控制状态跳转使能信号),将skip_en拉高。
(2)arp_tx模块
arp_tx模块时序:
状态跳转:
st_idle //初始状态,等待开始发送信号
st_preamble //发送前导码+帧起始界定符
st_eth_head //发送以太网帧头
st_arp_data //发送ARP数据
st_crc//发送CRC校验值
module arp_tx(
input 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协议
//以太网数据最小为46个字节,不足部分填充数据
localparam MIN_DATA_NUM = 16'd46 ;
//reg define
reg [4:0] cur_state ;
reg [4:0] next_state ;
reg [7:0] preamble[7:0] ; //前导码+SFD
reg [7:0] eth_head[13:0]; //以太网首部
reg [7:0] arp_data[27:0]; //ARP数据
reg tx_en_d0 ; //arp_tx_en信号延时
reg tx_en_d1 ;
reg skip_en ; //控制状态跳转使能信号
reg [5:0] cnt ;
reg [4:0] data_cnt ; //发送数据个数计数器
reg tx_done_t ;
//wire define
wire pos_tx_en ; //arp_tx_en信号上升沿
//*****************************************************
//** main code
//*****************************************************
assign pos_tx_en = (~tx_en_d1) & tx_en_d0;
//对arp_tx_en信号延时打拍两次,用于采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;
end
else begin
tx_en_d0 <= arp_tx_en;
tx_en_d1 <= tx_en_d0;
end
end
//(三段式状态机)同步时序描述状态转移
always @(posedge 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(skip_en)
next_state = st_preamble;
else
next_state = st_idle;
end
st_preamble : begin //发送前导码+帧起始界定符
if(skip_en)
next_state = st_eth_head;
else
next_state = st_preamble;
end
st_eth_head : begin //发送以太网首部
if(skip_en)
next_state = st_arp_data;
else
next_state = st_eth_head;
end
st_arp_data : begin //发送ARP数据
if(skip_en)
next_state = st_crc;
else
next_state = st_arp_data;
end
st_crc: begin //发送CRC校验值
if(skip_en)
next_state = st_idle;
else
next_state = st_crc;
end
default : next_state = st_idle;
endcase
end
//时序电路描述状态输出,发送以太网数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
cnt <= 6'd0;
data_cnt <= 5'd0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
gmii_txd <= 8'd0;
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;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
tx_done_t <= 1'b0;
case(next_state)
st_idle : begin
if(pos_tx_en) begin
skip_en <= 1'b1;
//如果目标MAC地址和IP地址已经更新,则发送正确的地址
if((des_mac != 48'b0) || (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
if(arp_tx_type == 1'b0)
arp_data[7] <= 8'h01; //ARP请求
else
arp_data[7] <= 8'h02; //ARP应答
end
end
st_preamble : begin //发送前导码+帧起始界定符
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if(cnt == 6'd7) begin
skip_en <= 1'b1;
cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
end
st_eth_head : begin //发送以太网首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= eth_head[cnt];
if (cnt == 6'd13) begin
skip_en <= 1'b1;
cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
end
st_arp_data : begin //发送ARP数据
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
//至少发送46个字节
if (cnt == MIN_DATA_NUM - 1'b1) begin
skip_en <= 1'b1;
cnt <= 1'b0;
data_cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
if(data_cnt <= 6'd27) begin
data_cnt <= data_cnt + 1'b1;
gmii_txd <= arp_data[data_cnt];
end
else
gmii_txd <= 8'd0; //Padding,填充0
end
st_crc : begin //发送CRC校验值
gmii_tx_en <= 1'b1;
cnt <= cnt + 1'b1;
if(cnt == 6'd0)
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
endmodule
对arp_tx_en(ARP发送使能信号)信号打两拍用于记录上升沿。
同步时序描述状态转移。
组合逻辑判断状态转移条件,均以skip_en(控制状态跳转使能信号)为参考,skip_en为高进入下一状态,否则保持现有状态
时序电路描述状态输出,发送以太网数据:
复位状态:skip_en(控制状态跳转使能信号)、cnt、data_cnt(发送数据个数计数器)、crc_en(CRC开始校验使能)、gmii_tx_en(GMII输出数据有效信号)、gmii_txd(GMII输出数据)、tx_done_t(以太网发送完成信号缓存器)全部置0。
preamble(前导码+SFD)赋值7个8'h55 + 1个8'hd5;
eth_head(以太网首部)赋值目的MAC地址、源MAC地址和以太网帧类型。
arp_data(ARP数据)赋值硬件类型、上层协议类型、硬件地址、协议地址、操作码、发送端(源)MAC地址、发送端(源)IP地址、接收端(目的)MAC地址、接收端(目的)IP地址。
st_idle:初始状态,等待开始发送信号。如果检测到pos_tx_en(arp_tx_en信号上升沿)如果目标MAC地址和IP地址已经更新,则发送正确的地址(更新后的地址)。然后对类型判断arp_tx_type(ARP发送类型 0:请求 1:应答),对arp_data[7]进行赋值。
st_preamble:发送前导码+帧起始界定符状态。依次发送前导码+帧起始界定符,通过cnt计数器控制发送8位。
st_eth_head:发送以太网帧头。发送以太网首部,通过cnt计数器控制发送14位。crc_en <= 1'b1表明需要进行CRC校验。
st_arp_data:发送ARP数据状态。rc_en <= 1'b1表明需要进行CRC校验。gmii_tx_en(拉高)GMII输出数据有效信号拉高,输出有效。至少发送46个字节,发送完46个字节,skip_en(控制状态跳转使能信号)拉高,cnt计数器清零,data_cnt数据计数器清零。在data_cnt小于27时间内,依次发送arp_data[data_cnt](ARP数据)给gmii_txd输出。超过27位后补0到46位。
st_crc:发送CRC校验值状态。此时crc_en <= 1'b1未被拉高。按照crc算法要求,对数据高低互换,并且进行取反。一共4位,cnt计数器进行控制。先取crc_next高八位(因为此时crc_data还是上一个字节数据),后面取crc_data[23:0]位。tx_done_t拉高,skip_en控制状态跳转使能信号)拉高,cnt计数器清零。
将tx_done_t依次赋值给tx_done(以太网发送完成信号)、tx_done(CRC数据复位信号)
(3)crc32_d8模块
(4)ARP顶层
module arp(
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 //以太网发送完成信号
);
//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};
//wire define
wire crc_en ; //CRC开始校验使能
wire crc_clr ; //CRC数据复位信号
wire [7:0] crc_d8 ; //输入待校验8位数据
wire [31:0] crc_data; //CRC校验数据
wire [31:0] crc_next; //CRC下次校验完成数据
//*****************************************************
//** main code
//*****************************************************
assign crc_d8 = gmii_txd;
//ARP接收模块
arp_rx
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP )
)
u_arp_rx(
.clk (gmii_rx_clk),
.rst_n (rst_n),
.gmii_rx_dv (gmii_rx_dv),
.gmii_rxd (gmii_rxd ),
.arp_rx_done (arp_rx_done),
.arp_rx_type (arp_rx_type),
.src_mac (src_mac ),
.src_ip (src_ip )
);
//ARP发送模块
arp_tx
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_arp_tx(
.clk (gmii_tx_clk),
.rst_n (rst_n),
.arp_tx_en (arp_tx_en ),
.arp_tx_type (arp_tx_type),
.des_mac (des_mac ),
.des_ip (des_ip ),
.crc_data (crc_data ),
.crc_next (crc_next[31:24]),
.tx_done (tx_done ),
.gmii_tx_en (gmii_tx_en),
.gmii_txd (gmii_txd ),
.crc_en (crc_en ),
.crc_clr (crc_clr )
);
//以太网发送CRC校验模块
crc32_d8 u_crc32_d8(
.clk (gmii_tx_clk),
.rst_n (rst_n ),
.data (crc_d8 ),
.crc_en (crc_en ),
.crc_clr (crc_clr ),
.crc_data (crc_data ),
.crc_next (crc_next )
);
endmodule
开发板以太网信息配置。
3、ARP_Ctrl模块
module arp_ctrl(
input clk , //输入时钟
input rst_n , //复位信号,低电平有效
input touch_key , //触摸按键,用于触发开发板发出ARP请求
input arp_rx_done, //ARP接收完成信号
input arp_rx_type, //ARP接收类型 0:请求 1:应答
output reg arp_tx_en , //ARP发送使能信号
output reg arp_tx_type //ARP发送类型 0:请求 1:应答
);
//reg define
reg touch_key_d0;
reg touch_key_d1;
//wire define
wire pos_touch_key; //touch_key信号上升沿
//*****************************************************
//** main code
//*****************************************************
assign pos_touch_key = ~touch_key_d1 & touch_key_d0;
//对arp_tx_en信号延时打拍两次,用于采touch_key的上升沿
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
touch_key_d0 <= 1'b0;
touch_key_d1 <= 1'b0;
end
else begin
touch_key_d0 <= touch_key;
touch_key_d1 <= touch_key_d0;
end
end
//为arp_tx_en和arp_tx_type赋值
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
arp_tx_en <= 1'b0;
arp_tx_type <= 1'b0;
end
else begin
if(pos_touch_key == 1'b1) begin //检测到输入触摸按键上升沿
arp_tx_en <= 1'b1;
arp_tx_type <= 1'b0;
end
//接收到ARP请求,开始控制ARP发送模块应答
else if((arp_rx_done == 1'b1) && (arp_rx_type == 1'b0)) begin
arp_tx_en <= 1'b1;
arp_tx_type <= 1'b1;
end
else
arp_tx_en <= 1'b0;
end
end
endmodule
4、eth_arp_test顶层文件
module eth_arp_test(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位信号,低电平有效
input touch_key , //触摸按键,用于触发开发板发出ARP请求
//PL以太网RGMII接口
input eth_rxc , //RGMII接收数据时钟
input eth_rx_ctl, //RGMII输入数据有效信号
input [3:0] eth_rxd , //RGMII输入数据
output eth_txc , //RGMII发送数据时钟
output eth_tx_ctl, //RGMII输出数据有效信号
output [3:0] eth_txd , //RGMII输出数据
output eth_rst_n //以太网芯片复位信号,低电平有效
);
//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};
//输入数据IO延时(如果为n,表示延时n*78ps)
parameter IDELAY_VALUE = 0;
//wire define
wire clk_200m ; //用于IO延时的时钟
wire gmii_rx_clk; //GMII接收时钟
wire gmii_rx_dv ; //GMII接收数据有效信号
wire [7:0] gmii_rxd ; //GMII接收数据
wire gmii_tx_clk; //GMII发送时钟
wire gmii_tx_en ; //GMII发送数据使能信号
wire [7:0] gmii_txd ; //GMII发送数据
wire arp_rx_done; //ARP接收完成信号
wire arp_rx_type; //ARP接收类型 0:请求 1:应答
wire [47:0] src_mac ; //接收到目的MAC地址
wire [31:0] src_ip ; //接收到目的IP地址
wire arp_tx_en ; //ARP发送使能信号
wire arp_tx_type; //ARP发送类型 0:请求 1:应答
wire tx_done ; //发送的目标MAC地址
wire [47:0] des_mac ; //发送的目标IP地址
wire [31:0] des_ip ; //以太网发送完成信号
//*****************************************************
//** main code
//*****************************************************
assign des_mac = src_mac;
assign des_ip = src_ip;
assign eth_rst_n = sys_rst_n;
//MMCM/PLL
clk_wiz u_clk_wiz
(
.clk_in1 (sys_clk ),
.clk_out1 (clk_200m ),
.reset (~sys_rst_n),
.locked (locked)
);
//GMII接口转RGMII接口
gmii_to_rgmii
#(
.IDELAY_VALUE (IDELAY_VALUE)
)
u_gmii_to_rgmii(
.idelay_clk (clk_200m ),
.gmii_rx_clk (gmii_rx_clk ),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd ),
.gmii_tx_clk (gmii_tx_clk ),
.gmii_tx_en (gmii_tx_en ),
.gmii_txd (gmii_txd ),
.rgmii_rxc (eth_rxc ),
.rgmii_rx_ctl (eth_rx_ctl ),
.rgmii_rxd (eth_rxd ),
.rgmii_txc (eth_txc ),
.rgmii_tx_ctl (eth_tx_ctl ),
.rgmii_txd (eth_txd )
);
//ARP通信
arp
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_arp(
.rst_n (sys_rst_n ),
.gmii_rx_clk (gmii_rx_clk),
.gmii_rx_dv (gmii_rx_dv ),
.gmii_rxd (gmii_rxd ),
.gmii_tx_clk (gmii_tx_clk),
.gmii_tx_en (gmii_tx_en ),
.gmii_txd (gmii_txd ),
.arp_rx_done (arp_rx_done),
.arp_rx_type (arp_rx_type),
.src_mac (src_mac ),
.src_ip (src_ip ),
.arp_tx_en (arp_tx_en ),
.arp_tx_type (arp_tx_type),
.des_mac (des_mac ),
.des_ip (des_ip ),
.tx_done (tx_done )
);
//ARP控制
arp_ctrl u_arp_ctrl(
.clk (gmii_rx_clk),
.rst_n (sys_rst_n),
.touch_key (touch_key),
.arp_rx_done (arp_rx_done),
.arp_rx_type (arp_rx_type),
.arp_tx_en (arp_tx_en),
.arp_tx_type (arp_tx_type)
);
endmodule
四、ARP实验总结
五、下载验证
按下触摸按键,开发板(192.168.1.10)发送ARP请求,电脑做出(192.168.1.102)应答回应MAC地址。