纯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
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
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)框图及顶层
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协议分析表原件!!!!
七、工程移植、源码获取请后台私信