纯verilog实现,仅使用锁相环IP、FIFO IP,方便跨平台移植。支持ping指令。
以太网系列文章:
以太网ICMP协议(ping指令)------FPGA学习笔记25-CSDN博客
以太网ARP协议------FPGA学习笔记23-CSDN博客
以太网PHY_MDIO通信(基于RTL8211)--FPGA学习笔记22_mdio前导码-CSDN博客
FPGA千兆网口数据传输MDIO接口------FPGA学习笔记3_yt8531sh原理图-CSDN博客

一、UDP简介
UDP(User Datagram Protocol),即用户数据报协议, 是一种面向无连接的传输层协议。无连接是指在传输数据时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用 UDP 协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输(如视频会议等)都会采用 UDP 协议进行传输,这种情况即使偶尔丢失一两个数据包,也不会对接收结果
产生太大影响。

二、UDP协议

UDP 首部共 8 个字节,同 IP 首部一样,也是一行以 32 位(4 个字节)为单位。
源端口号: 16 位发送端端口号,用于区分不同服务的端口,端口号的范围从 0 到 65535。
目的端口号: 16 位接收端端口号。
UDP 长度: 16 位 UDP 长度,包含 UDP 首部长度+数据长度,单位是字节(byte)。
UDP 校验和: 16 位 UDP 校验和。 UDP 计算校验和的方法和计算 IP 数据报首部校验和的方法相似,但不同的是 IP 数据报的校验和只检验 IP 数据报的首部,而 UDP 校验和包含三个部分:UDP 伪首部, UDP 首部和 UDP 的数据部分 。伪首部的数据是从 IP 数据报头和 UDP 数据报头获取的,包括源 IP 地址,目的 IP地址,协议类型和 UDP 长度,其目的是让 UDP 两次检查数据是否已经正确到达目的地,只是单纯为了做校验用的。在大多数使用场景中接收端并不检测 UDP 校验和,因此这里不做过多介绍。
用户数据打包在 UDP 协议中, UDP 协议又是基于 IP 协议之上的, IP 协议又是走 MAC 层发送的,即从包含关系来说: MAC 帧中的数据段为 IP 数据报, IP 报文中的数据段为 UDP 报文, UDP 报文中的数据段为用户希望传输的数据内容。
三、目标实现UDP协议(含ICMP协议)
四、代码编写
1、UDP框图

(1)udp_rx




cs
module udp_rx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
output reg rec_pkt_done, //以太网单包数据接收完成信号
output reg rec_en , //以太网接收的数据使能信号
output reg [7 :0] rec_data ,
output reg [15:0] rec_byte_num //以太网接收的有效字数 单位:byte
);
//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};
localparam st_idle = 7'b000_0001; //初始状态,等待接收前导码
localparam st_preamble = 7'b000_0010; //接收前导码状态
localparam st_eth_head = 7'b000_0100; //接收以太网帧头
localparam st_ip_head = 7'b000_1000; //接收IP首部
localparam st_udp_head = 7'b001_0000; //接收UDP首部
localparam st_rx_data = 7'b010_0000; //接收有效数据
localparam st_rx_end = 7'b100_0000; //接收结束
localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
localparam UDP_TYPE = 8'd17 ; //UDP协议类型
reg [6:0] cur_state ;
reg [6:0] next_state ;
reg skip_en ; //控制状态跳转使能信号
reg error_en ; //解析错误使能信号
reg [4:0] cnt ; //解析数据计数器
reg [47:0] des_mac ; //目的MAC地址
reg [15:0] eth_type ; //以太网类型
reg [31:0] des_ip ; //目的IP地址
reg [5:0] ip_head_byte_num; //IP首部长度
reg [15:0] udp_byte_num ; //UDP长度
reg [15:0] data_byte_num ; //数据长度
reg [15:0] data_cnt ; //有效数据计数
//*****************************************************
//** 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_ip_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_eth_head;
end
st_ip_head : begin //接收IP首部
if(skip_en)
next_state = st_udp_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_ip_head;
end
st_udp_head : begin //接收UDP首部
if(skip_en)
next_state = st_rx_data;
else
next_state = st_udp_head;
end
st_rx_data : begin //接收有效数据
if(skip_en)
next_state = st_rx_end;
else
next_state = st_rx_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
end
else begin
skip_en <= 1'b0;
error_en <= 1'b0;
rec_pkt_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
end
st_preamble:begin
if (gmii_rx_dv) begin
cnt <= cnt + 1'b1;
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 + 1'b1;
if(cnt < 5'd6)
des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址
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;
//判断MAC地址是否为开发板MAC地址或者公共地址
if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))&& eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
end
end
st_ip_head :begin
if (gmii_rx_dv) begin
cnt <= cnt + 1'b1;
if (cnt == 5'd0) begin
ip_head_byte_num <= {gmii_rxd[3:0],2'd0};
end
else if (cnt == 5'd9) begin
if (gmii_rxd != UDP_TYPE) begin
error_en <= 1'b1;
cnt <= 5'd0;
end
end
else if ((cnt >= 5'd16)&&(cnt <= 5'd18))begin //目的IP地址
des_ip <= {des_ip[23:0],gmii_rxd};
end
else if (cnt == 5'd19) begin
des_ip <= {des_ip[23:0],gmii_rxd};
if ({des_ip[23:0],gmii_rxd} == BOARD_IP) begin //判断目的IP是否为开发板
skip_en <= 1'b1;
cnt <= 5'd0;
end
else begin //IP错误
error_en <= 1'b0;
cnt <= 5'd0;
end
end
end
end
st_udp_head:begin
if (gmii_rx_dv) begin
cnt <= cnt + 1'b1;
if ((cnt >= 5'd4)&&(cnt <= 5'd5)) begin //解析UDP字节长度
udp_byte_num <= {udp_byte_num[7:0],gmii_rxd};
end
else if(cnt == 5'd7) begin //有效长度=字节长度-首部长度
data_byte_num <= udp_byte_num - 16'd8;
skip_en <= 1'b1;
cnt <= 5'd0;
end
end
end
st_rx_data :begin
if (gmii_rx_dv) begin
data_cnt <= data_cnt + 1'b1 ; //接收数据计数器
rec_data <= gmii_rxd ; //以太网接收数据
rec_en <= 1'b1 ; //接收数据使能信号
if (data_cnt == data_byte_num - 1'b1) begin
skip_en <= 1'b1;
data_cnt <= 16'd0;
rec_pkt_done <= 1'b1;
rec_byte_num <= data_byte_num; //以太网接收数据有效数量
end
end
end
st_rx_end :begin
rec_en <= 1'b0; //单包数据传输完成
if ((gmii_rx_dv == 1'b0) && (skip_en == 1'b0 ) ) begin
skip_en <= 1'b1;
end
end
default: ;
endcase
end
end
endmodule
仿真结果:

(2)udp_tx




cpp
module udp_tx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input tx_start_en, //以太网开始发送信号
input [ 7:0] tx_data , //以太网待发送数据
input [15:0] tx_byte_num, //以太网发送的有效字节数
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 tx_req , //读数据请求信号
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.123
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd123};
//目的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 = 7'b000_0001; //初始状态,等待开始发送信号
localparam st_check_sum = 7'b000_0010; //IP首部校验和
localparam st_preamble = 7'b000_0100; //发送前导码+帧起始界定符
localparam st_eth_head = 7'b000_1000; //发送以太网帧头
localparam st_ip_head = 7'b001_0000; //发送IP首部+UDP首部
localparam st_tx_data = 7'b010_0000; //发送数据
localparam st_crc = 7'b100_0000; //发送CRC校验值
localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam MIN_DATA_NUM = 16'd18 ;
//reg define
reg [6:0] cur_state ;
reg [6:0] next_state ;
reg [7:0] preamble [7:0] ; //前导码
reg [7:0] eth_head [13:0] ; //以太网首部
reg [31:0] ip_head [6:0] ; //IP首部 + UDP首部
reg start_en_d0 ;
reg start_en_d1 ;
reg start_en_d2 ;
reg [15:0] tx_data_num ; //发送的有效数据字节个数
reg [15:0] total_num ; //总字节数
reg trig_tx_en ;
reg [15:0] udp_num ; //UDP字节数
reg skip_en ; //控制状态跳转使能信号
reg [4:0] cnt ;
reg [31:0] check_buffer ; //首部校验和
reg [1:0] tx_bit_sel ;
reg [15:0] data_cnt ; //发送数据个数计数器
reg tx_done_t ;
reg [4:0] real_add_cnt ; //以太网数据实际多发的字节数
//wire define
wire pos_start_en ;//开始发送数据上升沿
wire [15:0] real_tx_data_num ;//实际发送的字节数(以太网最少字节要求)
//采tx_start_en上升沿
assign pos_start_en = (!start_en_d2) & start_en_d1 ;
//判断实际发送数据
assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM ;
//采tx_start_en上升沿
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
start_en_d0 <= 1'd0;
start_en_d1 <= 1'd0;
start_en_d2 <= 1'd0;
end
else begin
start_en_d0 <= tx_start_en;
start_en_d1 <= start_en_d0;
start_en_d2 <= start_en_d1;
end
end
//寄存数据有效长度
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_num <= 16'd0; //发送的有效数据字节个数
total_num <= 16'd0; //总字节数
udp_num <= 16'd0; //UDP字节数
end
else begin
if (pos_start_en && cur_state == st_idle) begin
//有效数据长度
tx_data_num <= tx_byte_num ;
//IP长度:有效数据长度 + IP首部长度
total_num <= tx_data_num + 16'd28;
//UDP长度:有效数据 + UDP首部长度
udp_num <= tx_byte_num + 16'd8;
end
end
end
//寄存触发发送信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
trig_tx_en <= 1'b0;
else
trig_tx_en <= pos_start_en;
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_check_sum;
else
next_state = st_idle;
end
st_check_sum: begin //IP首部校验
if(skip_en)
next_state = st_preamble;
else
next_state = st_check_sum;
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_ip_head;
else
next_state = st_eth_head;
end
st_ip_head : begin //发送IP首部+UDP首部
if(skip_en)
next_state = st_tx_data;
else
next_state = st_ip_head;
end
st_tx_data : begin //发送数据
if(skip_en)
next_state = st_crc;
else
next_state = st_tx_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 <= 5'd0 ;
check_buffer <= 32'd0;
ip_head[1][31:16] <= 16'd0; //IP首部表示
tx_bit_sel <= 2'b0 ;
crc_en <= 1'b0 ;
gmii_tx_en <= 1'b0 ;
gmii_txd <= 8'd0 ;
tx_req <= 1'b0 ;
tx_done_t <= 1'b0 ;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0 ;
//初始化数组
//前导码 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 ;
//目的MAC地址
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] ;
//源MAC地址
eth_head[6] <= BOARD_MAC[47:40];
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] ;
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(trig_tx_en) begin
skip_en <= 1'b1;
//版本号:4 首部长度:5(单位:32bit,20byte/4=5)
ip_head[0] <= {8'h45,8'h00,total_num};
//16位标识,每次发送累加1
ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
//bit[15:13]: 010表示不分片
ip_head[1][15:0] <= 16'h4000;
//协议:17(udp)
ip_head[2] <= {8'h40,8'd17,16'h0};
//源IP地址
ip_head[3] <= BOARD_IP;
//目的IP地址
if(des_ip != 32'd0)
ip_head[4] <= des_ip;
else
ip_head[4] <= DES_IP;
//16位源端口号:1234 16位目的端口号:1234
ip_head[5] <= {16'd1234,16'd1234};
//16位udp长度,16位udp校验和
ip_head[6] <= {udp_num,16'h0000};
//更新MAC地址
if(des_mac != 48'b0) begin
//目的MAC地址
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];
end
end
end
st_check_sum :begin //IP首部校验和
cnt <= cnt + 1'b1;
if (cnt == 5'd0) begin
check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
+ ip_head[1][31:16] + ip_head[1][15:0]
+ ip_head[2][31:16] + ip_head[2][15:0]
+ ip_head[3][31:16] + ip_head[3][15:0]
+ ip_head[4][31:16] + ip_head[4][15:0];
end
else if(cnt == 5'd1) //可能出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
end
else if(cnt == 5'd3) begin //按位取反
skip_en <= 1'b1;
cnt <= 5'd0;
ip_head[2][15:0] <= ~check_buffer[15:0];
end
end
st_preamble :begin //发送前导码+帧起始界定符
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if (cnt == 5'd7) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
st_eth_head :begin //发送以太网首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= eth_head[cnt];
if (cnt == 5'd13) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
st_ip_head :begin //发送IP首部 + UDP首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
tx_bit_sel <= tx_bit_sel + 1'b1;
if (tx_bit_sel == 2'd0) begin
gmii_txd <= ip_head[cnt][31:24];
end
else if (tx_bit_sel == 2'd1) begin
gmii_txd <= ip_head[cnt][23:16];
end
else if (tx_bit_sel == 2'd2) begin
gmii_txd <= ip_head[cnt][15:8];
if (cnt == 5'd6) begin
//提前读请求数据,等待数据有效时发送
tx_req <= 1'b1;
end
else begin
tx_req <= 1'b0;
end
end
else if (tx_bit_sel == 2'd3) begin //tx_bit_sel自动溢出
gmii_txd <= ip_head[cnt][7:0];
if (cnt == 5'd6) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
st_tx_data :begin //发送数据
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= tx_data;
tx_bit_sel <= tx_bit_sel + 1'b1;
if (data_cnt < tx_data_num - 16'd1) begin
data_cnt <= data_cnt + 1'b1;
end
else if (data_cnt == tx_data_num - 16'd1) begin
//如果发送的有效数据少于18个字节,在后面填补充位
//补充的值为最后一次发送的有效数据
if (data_cnt + real_add_cnt < real_tx_data_num - 16'd1) begin
real_add_cnt <= real_add_cnt + 1'b1;
end
else begin
skip_en <= 1'b1;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0;
tx_bit_sel <= 3'd0;
end
end
if (data_cnt == tx_byte_num - 16'd2) begin
tx_req <= 1'b0;
end
end
st_crc : begin //发送CRC校验值
gmii_tx_en <= 1'b1;
tx_bit_sel <= tx_bit_sel + 3'd1;
tx_req <= 1'b0;
if(tx_bit_sel == 3'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(tx_bit_sel == 3'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(tx_bit_sel == 3'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(tx_bit_sel == 3'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;
end
else ;
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
仿真结果:

2、协议栈顶层
(1)框图及顶层

cs
module eth_udp_loop(
input sys_rst_n , //系统复位信号,低电平有效
//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输出数据
);
//parameter define
parameter BOARD_MAC = 48'h00_11_22_33_44_55; //开发板MAC地址 00-11-22-33-44-55
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10}; //开发板IP地址 192.168.1.10
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff; //目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102}; //目的IP地址 192.168.1.102
parameter IDELAY_VALUE = 15; //输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps)
//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_gmii_tx_en ; //ARP GMII输出数据有效信号
wire [7:0] arp_gmii_txd ; //ARP 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 [47:0] des_mac ; //发送的目标MAC地址
wire [31:0] des_ip ; //发送的目标IP地址
wire arp_tx_done ; //ARP发送完成信号
wire icmp_gmii_tx_en ; //ICMP GMII输出数据有效信号
wire [7:0] icmp_gmii_txd ; //ICMP GMII输出数据
wire icmp_rec_pkt_done ; //ICMP单包数据接收完成信号
wire icmp_rec_en ; //ICMP接收的数据使能信号
wire [ 7:0] icmp_rec_data ; //ICMP接收的数据
wire [15:0] icmp_rec_byte_num ; //ICMP接收的有效字节数 单位:byte
wire [15:0] icmp_tx_byte_num ; //ICMP发送的有效字节数 单位:byte
wire icmp_tx_done ; //ICMP发送完成信号
wire icmp_tx_req ; //ICMP读数据请求信号
wire [ 7:0] icmp_tx_data ; //ICMP待发送数据
wire icmp_tx_start_en ; //ICMP发送开始使能信号
wire udp_gmii_tx_en ; //UDP GMII输出数据有效信号
wire [7:0] udp_gmii_txd ; //UDP GMII输出数据
wire rec_pkt_done ; //UDP单包数据接收完成信号
wire udp_rec_en ; //UDP接收的数据使能信号
wire [ 7:0] udp_rec_data ; //UDP接收的数据
wire [15:0] rec_byte_num ; //UDP接收的有效字节数 单位:byte
wire [15:0] tx_byte_num ; //UDP发送的有效字节数 单位:byte
wire udp_tx_done ; //UDP发送完成信号
wire udp_tx_req ; //UDP读数据请求信号
wire [ 7:0] udp_tx_data ; //UDP待发送数据
wire tx_start_en ; //UDP发送开始使能信号
wire [7:0] rec_data ; //FIFO写入数据
wire rec_en ; //FIFO写使能
wire tx_req ; //FIFO读使能
wire [7:0] tx_data ; //FIFO读出数据
assign icmp_tx_start_en = icmp_rec_pkt_done ; //ICMP 接收端结束标志,作为 ICMP发送端开始标志
assign icmp_tx_byte_num = icmp_rec_byte_num ; //ICMP 接收端数据个数,作为 ICMP发送端发送数据
assign tx_start_en = rec_pkt_done ; //UDP 接收端结束标志,作为 UDP发送开始使能信号
assign tx_byte_num = rec_byte_num ; //UDP 接收端数据个数,作为 UDP发送端发送数据个数
assign des_mac = src_mac ; //ARP 接收到的 源MAC,作为 ICMP\UDP 目的MAC,实际为电脑端MAC
assign des_ip = src_ip ; //ARP 接收到的 源IP ,作为 ICMP\UDP 目的IP ,实际为电脑端IP
//数据位于异步FIFO之中,如需单独使用一侧功能,可以修改FIFO数据
//需要注意写入有效数据数量要与此处相同,一定要注意 数据个数与FIFO读出数据对齐!!!!!!!
//MMCM/PLL 产生200Mhz时钟--> gmii2rgmii
clk_wiz_0 u_clk_wiz_0
(
.clk_out1 (clk_200m ), // output clk_out1
.reset (~sys_rst_n ), // input reset
.locked (locked ), // output locked
.clk_in1 (eth_rxc ) // PHY侧提供eth_rxc时钟125Mhz
);
//GMII接口转RGMII接口
gmii_to_rgmii
#(
.IDELAY_VALUE (IDELAY_VALUE )
)
u_gmii_to_rgmii(
.idelay_clk (clk_200m ), //IDELAY时钟
//以太网GMII接口
.gmii_rx_clk (gmii_rx_clk ), //GMII接收时钟
.gmii_rx_dv (gmii_rx_dv ), //GMII接收数据有效信号
.gmii_rxd (gmii_rxd ), //GMII接收数据
.gmii_tx_clk (gmii_tx_clk ), //GMII发送时钟
.gmii_tx_en (gmii_tx_en ), //GMII发送数据使能信号
.gmii_txd (gmii_txd ), //GMII发送数据
//以太网RGMII接口
.rgmii_rxc (eth_rxc ), //RGMII接收时钟
.rgmii_rx_ctl (eth_rx_ctl ), //RGMII接收数据控制信号
.rgmii_rxd (eth_rxd ), //RGMII接收数据
.rgmii_txc (eth_txc ), //RGMII发送时钟
.rgmii_tx_ctl (eth_tx_ctl ), //RGMII发送数据控制信号
.rgmii_txd (eth_txd ) //RGMII发送数据
);
//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接口
//input
.gmii_rx_clk (gmii_rx_clk ), //GMII接收数据时钟
.gmii_rx_dv (gmii_rx_dv ), //GMII输入数据有效信号
.gmii_rxd (gmii_rxd ), //GMII输入数据
.gmii_tx_clk (gmii_tx_clk ), //GMII发送数据时钟
//output
.gmii_tx_en (arp_gmii_tx_en ), //GMII输出数据有效信号
.gmii_txd (arp_gmii_txd ), //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地址
//input
.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地址
//output
.tx_done (arp_tx_done ) //以太网发送完成信号
);
//ICMP通信
icmp
#(
.BOARD_MAC (BOARD_MAC ), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_icmp(
.rst_n (sys_rst_n ), //复位信号,低电平有效
//GMII接口
//input
.gmii_rx_clk (gmii_rx_clk ), //GMII接收数据时钟
.gmii_rx_dv (gmii_rx_dv ), //GMII输入数据有效信号
.gmii_rxd (gmii_rxd ), //GMII输入数据
.gmii_tx_clk (gmii_tx_clk ), //GMII发送数据时钟
//output
.gmii_tx_en (icmp_gmii_tx_en ), //GMII输出数据有效信号
.gmii_txd (icmp_gmii_txd ), //GMII输出数据
//用户接口
//output
.rec_pkt_done (icmp_rec_pkt_done ), //以太网单包数据接收完成信号
.rec_en (icmp_rec_en ), //以太网接收的数据使能信号
.rec_data (icmp_rec_data ), //以太网接收的数据
.rec_byte_num (icmp_rec_byte_num ), //以太网接收的有效字节数 单位:byte
//input
.tx_start_en (icmp_tx_start_en ), //以太网开始发送信号
.tx_data (icmp_tx_data ), //以太网待发送数据
.tx_byte_num (icmp_tx_byte_num ), //以太网发送的有效字节数 单位:byte
.des_mac (des_mac ), //发送的目标MAC地址
.des_ip (des_ip ), //发送的目标IP地址
//output
.tx_done (icmp_tx_done ), //以太网发送完成信号
.tx_req (icmp_tx_req ) //读数据请求信号
);
//UDP通信
udp
#(
.BOARD_MAC (BOARD_MAC ), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_udp(
.rst_n (sys_rst_n ), //复位信号,低电平有效
//GMII接口
//input
.gmii_rx_clk (gmii_rx_clk ), //GMII接收数据时钟
.gmii_rx_dv (gmii_rx_dv ), //GMII输入数据有效信号
.gmii_rxd (gmii_rxd ), //GMII输入数据
.gmii_tx_clk (gmii_tx_clk ), //GMII发送数据时钟
//output
.gmii_tx_en (udp_gmii_tx_en ), //GMII输出数据有效信号
.gmii_txd (udp_gmii_txd ), //GMII输出数据
//用户接口
//outpur
.rec_pkt_done (rec_pkt_done ), //以太网单包数据接收完成信号
.rec_en (udp_rec_en ), //以太网接收的数据使能信号
.rec_data (udp_rec_data ), //以太网接收的数据
.rec_byte_num (rec_byte_num ), //以太网接收的有效字节数 单位:byte
//input
.tx_start_en (tx_start_en ), //以太网开始发送信号
.tx_data (udp_tx_data ), //以太网待发送数据
.tx_byte_num (tx_byte_num ), //以太网发送的有效字节数 单位:byte
.des_mac (des_mac ), //发送的目标MAC地址
.des_ip (des_ip ), //发送的目标IP地址
//output
.tx_done (udp_tx_done ), //以太网发送完成信号
.tx_req (udp_tx_req ) //读数据请求信号
);
//异步FIFO,实际做同步FIFO使用
async_fifo_2048x8b u_async_fifo_2048x8b (
//input
.rst (~sys_rst_n ), //input wire rst
.wr_clk (gmii_rx_clk ), //input wire wr_clk
.rd_clk (gmii_rx_clk ), //input wire rd_clk
.din (rec_data ), //input wire [7 : 0] din
.wr_en (rec_en ), //input wire wr_en
.rd_en (tx_req ), //input wire rd_en
//output
.dout (tx_data ), //output wire [7 : 0] dout
.full ( ), //output wire full
.empty ( ) //output wire empty
);
//以太网控制模块
eth_ctrl u_eth_ctrl(
//input
.clk (gmii_rx_clk ), //时钟
.rst_n (sys_rst_n ), //系统复位信号,低电平有效
//ARP相关端口信号
//input
.arp_rx_done (arp_rx_done ), //ARP接收完成信号
.arp_rx_type (arp_rx_type ), //ARP接收类型 0:请求 1:应答
.arp_tx_done (arp_tx_done ), //ARP发送完成信号
.arp_gmii_tx_en (arp_gmii_tx_en ), //ARP GMII输出数据有效信号
.arp_gmii_txd (arp_gmii_txd ), //ARP GMII输出数据
//output
.arp_tx_en (arp_tx_en ), //ARP发送使能信号
.arp_tx_type (arp_tx_type ), //ARP发送类型 0:请求 1:应答
//ICMP相关端口信号
//input
.icmp_tx_start_en (icmp_tx_start_en ), //ICMP开始发送信号
.icmp_tx_done (icmp_tx_done ), //ICMP发送完成信号
.icmp_gmii_tx_en (icmp_gmii_tx_en ), //ICMP GMII输出数据有效信号
.icmp_gmii_txd (icmp_gmii_txd ), //ICMP GMII输出数据
//ICMP fifo接口信号
//input
.icmp_rec_en (icmp_rec_en ), //ICMP接收的数据使能信号
.icmp_rec_data (icmp_rec_data ), //ICMP接收的数据
.icmp_tx_req (icmp_tx_req ), //ICMP读数据请求信号
//output
.icmp_tx_data (icmp_tx_data ), //ICMP待发送数据
//UDP相关端口信号
//input
.udp_tx_start_en (tx_start_en ), //UDP开始发送信号
.udp_tx_done (udp_tx_done ), //UDP发送完成信号
.udp_gmii_tx_en (udp_gmii_tx_en ), //UDP GMII输出数据有效信号
.udp_gmii_txd (udp_gmii_txd ), //UDP GMII输出数据
//UDP fifo接口信号
//input
.udp_rec_data (udp_rec_data ), //UDP接收的数据
.udp_rec_en (udp_rec_en ), //UDP接收的数据使能信号
.udp_tx_req (udp_tx_req ), //UDP读数据请求信号
//output
.udp_tx_data (udp_tx_data ), //UDP待发送数据
//fifo接口信号
//output
.rec_data (rec_data ), //待发送的数据
.rec_en (rec_en ), //读数据请求信号
.tx_req (tx_req ), //接收的数据使能信号
//input
.tx_data (tx_data ), //接收的数据
//GMII发送引脚
//output
.gmii_tx_en (gmii_tx_en ), //GMII输出数据有效信号
.gmii_txd (gmii_txd ) //GMII输出数据
);
endmodule
(2)代码框架

3、资源消耗情况
五、下载验证
ping测试:


网口环回测试:


六、福利获取
后台私信 UDP协议分析表,即可获得 UDP协议分析表原件!!!!
