基于FPGA的UDP 通信(五)

article/2025/3/10 21:03:42



引言

前文链接:

基于FPGA的UDP 通信(一)

基于FPGA的UDP 通信(二)

基于FPGA的UDP 通信(三)

基于FPGA的UDP 通信(四)

本文基于FPGA设计千兆以太网通信模块UDP数据发送模块(FPGA发送)

设计条件

FPGA芯片:xc7a35tfgg484-2

网络芯片(PHY):RTL8211(支持1000M/100M/10M)

MAC与PHY接口:GMII

接口类型:RJ-45

Vivado版本:2018.3



设计说明

UDP数据发送模块需要按照以太网的帧数据格式将数据发送,采用状态机的方式实现。设计模块主要包含如下几部分:

1、IP首部校验和计算模块;

2、FCS计算模块(CRC32);

3、UDP数据发送主模块;

FSC计算稍许复杂,此处给出时序图(FCS发送部分时序):

 此处做简要说明:

根据以太网数据协议,参与CRC32校验运算的数据从 以太网数据头到最后一个UDP数据,最开始的FCS发送状态发送的数据全为0,不参与CRC32校验计算,由于CRC校验计算延迟为 1个时钟周期,所以需要对原状态机的发送数据延迟一个时钟周期后送入 GMII接口的数据总线。

具体逻辑可以根据代码和功能仿真理解。

设计源码

IP首部校验和

check sum的计算步骤可以查看本系列的第二篇博文,此处不赘述。

// | ===================================================---------------------------===================================================
// | --------------------------------------------------- 	IP数据头部计算模块 	   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-01-14
// | 完成时间 : 2022-01-14
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// | 			-1- 计算延迟 6 个时钟周期,欲使用该模块的结果,须计划好数据输入的时间
// | 			-2- 建议发送前导码时就开始计算
// |			-3- 与下游模块交互采用握手机制
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:`timescale 1ns / 1psmodule IP_HEAD_CHECK_SUM_CAL_MDL(// | ==================================== 模块输入输出端口声明 ====================================
input 															I_OPR_CLK,
input 															I_OPR_RSTN,input 															I_CAL_EN,//脉冲信号// IP 头部参数
input 			[3:0]											I_IP_HEAD_VER,
input 			[3:0]											I_IP_HEAD_LEN,	
input 			[7:0]											I_IP_HEAD_TOS,   
input 			[15:0]											I_IP_HEAD_TOTLEN,
input 			[15:0]											I_IP_HEAD_ID,
input 			[2:0]											I_IP_HEAD_FLAG, 
input 			[12:0]											I_IP_HEAD_OFFSET,
input 			[7:0]											I_IP_HEAD_TTL,	
input 			[7:0]											I_IP_HEAD_PROT, 
input 			[31:0]											I_IP_HEAD_SRC_ADDR,
input 			[31:0]											I_IP_HEAD_DST_ADDR,output 			[15:0]											O_IP_HEAD_CHECK_SUM
);// | ====================================   模块内部参数声明   ====================================// | ====================================   模块内部信号声明   ====================================
reg 			[16:0]											R_ADD1_L1[3:0];
reg 			[17:0]											R_ADD1_L2[1:0];
reg 			[18:0]											R_ADD1_L3;
reg 			[19:0]											R_ADD1_L4;
reg 			[16:0]											R_ADD2;
reg 			[15:0]											R_ADD3;// | ====================================   模块内部逻辑设计   ====================================
// ADD1 第一级
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD1_L1[0] <= 17'd0;R_ADD1_L1[1] <= 17'd0;R_ADD1_L1[2] <= 17'd0;R_ADD1_L1[3] <= 17'd0;endelse if(I_CAL_EN)begin R_ADD1_L1[0] <=   {I_IP_HEAD_VER,I_IP_HEAD_LEN,I_IP_HEAD_TOS} + I_IP_HEAD_TOTLEN;  R_ADD1_L1[1] <=   I_IP_HEAD_ID + {I_IP_HEAD_FLAG,I_IP_HEAD_OFFSET};  R_ADD1_L1[2] <=   {I_IP_HEAD_TTL,I_IP_HEAD_PROT} + I_IP_HEAD_SRC_ADDR[31:16];  R_ADD1_L1[3] <=   I_IP_HEAD_SRC_ADDR[15:0] + I_IP_HEAD_DST_ADDR[31:16];  end
end// ADD1 第二级
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD1_L2[0] <= 18'd0;R_ADD1_L2[1] <= 18'd0;endelsebeginR_ADD1_L2[0] <= R_ADD1_L1[0] + R_ADD1_L1[1];R_ADD1_L2[1] <= R_ADD1_L1[2] + R_ADD1_L1[3];end
end// ADD1 第三级
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD1_L3 <= 19'd0;endelsebeginR_ADD1_L3 <= R_ADD1_L2[0] + R_ADD1_L2[1];end
end// ADD1 第四级
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD1_L4 <= 20'd0;endelsebeginR_ADD1_L4 <= R_ADD1_L3 + I_IP_HEAD_DST_ADDR[15:0];end
end// ADD2 
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD2 <= 17'd0;endelsebeginR_ADD2 <= R_ADD1_L4[19:16]+ R_ADD1_L4[15:0];end
end// ADD3 
always @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_ADD3 <= 16'd0;endelsebeginR_ADD3 <= R_ADD2[16]+ R_ADD2[15:0];end
endassign O_IP_HEAD_CHECK_SUM = ~R_ADD3;endmodule

FCS计算

以太网的数据校验采用的CRC32校验方式。对于CRC32校验的计算,有在线生成设计源码的网站可用:

CRC校验程序在线生成http://crctool.easics.be/

 做如下需改:


// Copyright (C) 1999-2008 Easics NV.
// This source file may be used and distributed without restriction
// provided that this copyright statement is not removed from the file
// and that any derivative work contains the original copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Purpose : synthesizable CRC function
//   * polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
//   * data width: 8
//
// Info : tools@easics.be
//        http://www.easics.commodule CRC32_D8 (
// 输入输出端口
input               I_OPR_CLK,
input               I_OPR_RSTN,input               I_CRC_INIT,input               I_CRC_EN,
input     [7:0]     I_DATA,
output    [31:0]    O_CRC_RES);
// 内部信号
wire [7:0]  W_DATA;
reg  [31:0] R_CRC_RES;genvar GV_8;
generatefor(GV_8 = 0;GV_8 < 8;GV_8 = GV_8 + 1)beginassign W_DATA[GV_8] = I_DATA[7-GV_8];end
endgeneratealways @ (posedge I_OPR_CLK)
beginif(~I_OPR_RSTN)beginR_CRC_RES <= {32{1'b1}};endelse if(I_CRC_INIT)beginR_CRC_RES <= {32{1'b1}}; endelse if(I_CRC_EN)beginR_CRC_RES <= nextCRC32_D8(W_DATA,R_CRC_RES);end
endgenvar GV_32;
generatefor(GV_32 = 0;GV_32 < 32;GV_32 = GV_32 + 1)beginassign O_CRC_RES[GV_32] = ~R_CRC_RES[31-GV_32];end
endgenerate// polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
// data width: 8
// convention: the first serial bit is D[7]
function [31:0] nextCRC32_D8;input [7:0] Data;input [31:0] crc;reg [7:0] d;reg [31:0] c;reg [31:0] newcrc;begind = Data;c = crc;newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];newcrc[20] = d[4] ^ c[12] ^ c[28];newcrc[21] = d[5] ^ c[13] ^ c[29];newcrc[22] = d[0] ^ c[14] ^ c[24];newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];newcrc[31] = d[5] ^ c[23] ^ c[29];nextCRC32_D8 = newcrc;end
endfunctionendmodule

UDP发送顶层

主要是使用状态机控制发送流程:

// | ===================================================---------------------------===================================================
// | --------------------------------------------------- 	  UDP 数据发送模块 	   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-01-13
// | 完成时间 : 2022-01-13
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 参数可配置
// |			-2- 包含CRC32校验
// |			-3- 发出数据请求后,须1个时钟周期后给出有效数据,输入到此模块
// |			-4- IP头部 固定为 20字节
// |			-5- 帧间距长度 12字节
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:`timescale 1ns / 1ps
module UDP_TX_MDL #(
// | ====================================  模块可配置参数声明  ==================================== 
parameter 			P_FPGA_MAC_ADDR				=				48'h00_00_00_00_00_00,   // FPGA侧 MAC地址
parameter 			P_FPGA_IP_ADDR				=				{8'd0,8'd0,8'd0,8'd0},   // FPGA侧 IP地址
parameter 			P_FPGA_UDP_PORT				=				16'd0, 				     // FPGA侧 UDP端口号parameter 			P_DST_MAC_ADDR				=				48'h00_00_00_00_00_00,   // 目的侧 MAC地址
parameter 			P_DST_IP_ADDR				=				{8'd0,8'd0,8'd0,8'd0},   // 目的侧 IP地址
parameter 			P_DST_UDP_PORT				=				16'd0 				     // 目的侧 UDP端口号
)(
// | ==================================== 模块输入输出端口声明 ====================================
// 时钟、复位
input 															I_CLK_125M,
input 															I_SYS_RSTN,
// 发送 握手信号
input 															I_TX_EN,//脉冲信号
output  reg 													O_TX_DONE,
// 数据长度
input 				[15:0]										I_TX_DATA_LEN,   //一直有效至下一次传输开始,// 最小数据为 1 最大为 1472(最大值前提是 IP首部长度 20)
// 数据
input 				[7:0]										I_TX_DATA,
output 	reg														O_DATA_REQ,
// GMII接口
output 															O_GMII_TX_CLK,
(*IOB = "TRUE"*)(*MARK_DEBUG = "TRUE"*)
output  reg 		[7:0]										O_GMII_TXD, //插入I/O缓冲,提高驱动
(*IOB = "TRUE"*)(*MARK_DEBUG = "TRUE"*)
output 	reg 													O_GMII_TX_EN //插入I/O缓冲);
// | ====================================   模块内部参数声明   ====================================
// 状态编码
localparam 			LP_ST_IDLE 					=				8'b0000_0001;
localparam 			LP_ST_PREAMBLE 				=				8'b0000_0010;
localparam 			LP_ST_ETH_HEAD 				=				8'b0000_0100;
localparam 			LP_ST_IP_HEAD				=				8'b0000_1000;
localparam 			LP_ST_UDP_HEAD 				=				8'b0001_0000;
localparam 			LP_ST_TX_DATA 				=				8'b0010_0000;
localparam 			LP_ST_TX_FILL 				=				8'b0100_0000;
localparam 			LP_ST_FCS 					=				8'b1000_0000;
// 以太网类型 IP数据报
localparam 			LP_ETH_TYPE 				= 				16'h0800;//以太网 IP数据报 类型
// 前导码
localparam 			LP_ETH_PREAMBLE 			=				8'h55;
localparam 			LP_ETH_SFD 					=				8'hd5;
// IP首部参数
localparam 			LP_IP_HEAD_VER				=				4'h4;
localparam 			LP_IP_HEAD_LEN 				=				4'h5;
localparam 			LP_IP_HEAD_TOS      		=				8'h00;
localparam 			LP_IP_HEAD_ID 				=				16'h0000;
localparam 			LP_IP_HEAD_FLAG 			=				3'b0_0_0;
localparam 			LP_IP_HEAD_OFFSET 			=				13'd0;
localparam 			LP_IP_HEAD_TTL 				=				8'h40;
localparam 			LP_IP_HEAD_PROT 			=				8'd17;// | ====================================   模块内部信号声明   ====================================
// 状态信号
(*MARK_DEBUG = "TRUE"*)
reg 				[7:0]										R_CS;
reg 				[7:0]										R_NS;
// 计数器
reg 				[2:0]										R_PREAMBLE_CNT;
reg 				[3:0]										R_ETH_HEAD_CNT;
reg 				[4:0]										R_IP_HEAD_CNT;
reg 				[2:0]										R_UDP_HEAD_CNT;
reg 				[15:0]										R_UDP_DATA_CNT;
reg 				[4:0]										R_FILL_CNT;
reg 				[1:0]										R_CRC_CNT;
reg 				[1:0]										R_CRC_CNT2;
reg 				[3:0]										R_IFG_CNT;
// IP首部校验和
wire 			   [15:0]										W_IP_HEAD_CHECK_SUM;
wire 			   [15:0]										W_UDP_HEAD_CHECK;
// CRC32校验模块
reg              												R_CRC_EN;
wire   			    [31:0]    									W_CRC_RES;
// 数据长度
wire 				[15:0]										W_UDP_LEN;
wire 				[15:0]										W_IP_LEN;
// 数据发送
reg 															R_GMII_TX_EN;
reg 				[7:0]										R_GMII_TX_DATA;reg 															R_CRC_ST;reg 															R_IFG_FLAG;// | ====================================   模块内部逻辑设计   ====================================
assign 		W_UDP_LEN 		= 		I_TX_DATA_LEN + 16'd8;
assign 		W_IP_LEN  		= 		W_UDP_LEN + 16'd20;
assign 		W_UDP_HEAD_CHECK=		16'd0;
assign 		O_GMII_TX_CLK   = 		I_CLK_125M;
always @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_CS <= LP_ST_IDLE;endelsebeginR_CS <= R_NS;end
endalways @ (*)
beginif(~I_SYS_RSTN)beginR_NS = LP_ST_IDLE;endelsebegincase(R_CS)LP_ST_IDLE:beginif(I_TX_EN)beginR_NS = LP_ST_PREAMBLE;endelsebeginR_NS = LP_ST_IDLE;endend 		LP_ST_PREAMBLE:beginif(R_PREAMBLE_CNT == 3'd7)beginR_NS = LP_ST_ETH_HEAD;endelsebeginR_NS = LP_ST_PREAMBLE;endend LP_ST_ETH_HEAD:beginif(R_ETH_HEAD_CNT == 4'd13)beginR_NS = LP_ST_IP_HEAD;endelsebeginR_NS = LP_ST_ETH_HEAD;endend LP_ST_IP_HEAD:beginif(R_IP_HEAD_CNT == 5'd19)beginR_NS = LP_ST_UDP_HEAD;endelsebeginR_NS = LP_ST_IP_HEAD;endend	LP_ST_UDP_HEAD:beginif(R_UDP_HEAD_CNT == 3'd7)beginR_NS = LP_ST_TX_DATA;endelsebeginR_NS = LP_ST_UDP_HEAD;endend LP_ST_TX_DATA:beginif((I_TX_DATA_LEN < 16'd18) && (R_UDP_DATA_CNT == I_TX_DATA_LEN - 1'b1))beginR_NS = LP_ST_TX_FILL;endelse if(R_UDP_DATA_CNT == I_TX_DATA_LEN - 1'b1)beginR_NS = LP_ST_FCS;endelsebeginR_NS = LP_ST_TX_DATA;endend 	LP_ST_TX_FILL:beginif(R_FILL_CNT == (5'd17 - I_TX_DATA_LEN[4:0]))beginR_NS = LP_ST_FCS;endelsebeginR_NS = LP_ST_TX_FILL;endend 	LP_ST_FCS:beginif(R_CRC_CNT == 2'd3)beginR_NS = LP_ST_IDLE;endelsebeginR_NS = LP_ST_FCS;endenddefault:beginR_NS = LP_ST_IDLE;end 					endcaseend
end
// 以太网数据发送
always @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_PREAMBLE_CNT <= 3'd0;R_ETH_HEAD_CNT <= 4'd0;R_IP_HEAD_CNT  <= 5'd0;R_UDP_HEAD_CNT <= 3'd0;R_UDP_DATA_CNT <= 16'd0;R_FILL_CNT     <= 5'd0;R_CRC_CNT      <= 2'd0;R_GMII_TX_EN   <= 1'b0;R_GMII_TX_DATA <= 8'd0;endelsebegincase(R_CS)LP_ST_IDLE:beginR_PREAMBLE_CNT <= 3'd0;R_ETH_HEAD_CNT <= 4'd0;R_IP_HEAD_CNT  <= 5'd0;R_UDP_HEAD_CNT <= 3'd0;R_UDP_DATA_CNT <= 16'd0;R_FILL_CNT     <= 5'd0;R_CRC_CNT      <= 2'd0;R_GMII_TX_EN   <= 1'b0;R_GMII_TX_DATA <= 8'd0;endLP_ST_PREAMBLE:beginR_PREAMBLE_CNT <= R_PREAMBLE_CNT + 1;R_GMII_TX_EN   <= 1'b1;if(R_PREAMBLE_CNT <= 3'd6)R_GMII_TX_DATA <= 8'h55;elseR_GMII_TX_DATA <= 8'hd5;endLP_ST_ETH_HEAD:beginR_ETH_HEAD_CNT <= R_ETH_HEAD_CNT + 1;case(R_ETH_HEAD_CNT)4'd0: R_GMII_TX_DATA <= P_DST_MAC_ADDR[47:40];4'd1: R_GMII_TX_DATA <= P_DST_MAC_ADDR[39:32];4'd2: R_GMII_TX_DATA <= P_DST_MAC_ADDR[31:24];4'd3: R_GMII_TX_DATA <= P_DST_MAC_ADDR[23:16];4'd4: R_GMII_TX_DATA <= P_DST_MAC_ADDR[15:8];4'd5: R_GMII_TX_DATA <= P_DST_MAC_ADDR[7:0];4'd6: R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[47:40];4'd7: R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[39:32];4'd8: R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[31:24];4'd9: R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[23:16];4'd10:R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[15:8];4'd11:R_GMII_TX_DATA <= P_FPGA_MAC_ADDR[7:0];4'd12:R_GMII_TX_DATA <= LP_ETH_TYPE[15:8];4'd13:R_GMII_TX_DATA <= LP_ETH_TYPE[7:0];default:R_GMII_TX_DATA <= 8'h00;endcaseendLP_ST_IP_HEAD:beginR_IP_HEAD_CNT <= R_IP_HEAD_CNT + 1;case(R_IP_HEAD_CNT)5'd0:  R_GMII_TX_DATA <= {LP_IP_HEAD_VER,LP_IP_HEAD_LEN};5'd1:  R_GMII_TX_DATA <= LP_IP_HEAD_TOS;5'd2:  R_GMII_TX_DATA <= W_IP_LEN[15:8];5'd3:  R_GMII_TX_DATA <= W_IP_LEN[7:0];5'd4:  R_GMII_TX_DATA <= LP_IP_HEAD_ID[15:8];5'd5:  R_GMII_TX_DATA <= LP_IP_HEAD_ID[7:0];5'd6:  R_GMII_TX_DATA <= {LP_IP_HEAD_FLAG,LP_IP_HEAD_OFFSET[12:8]};5'd7:  R_GMII_TX_DATA <= LP_IP_HEAD_OFFSET[7:0];5'd8:  R_GMII_TX_DATA <= LP_IP_HEAD_TTL;5'd9:  R_GMII_TX_DATA <= LP_IP_HEAD_PROT;5'd10: R_GMII_TX_DATA <= W_IP_HEAD_CHECK_SUM[15:8];5'd11: R_GMII_TX_DATA <= W_IP_HEAD_CHECK_SUM[7:0];5'd12: R_GMII_TX_DATA <= P_FPGA_IP_ADDR[31:24];5'd13: R_GMII_TX_DATA <= P_FPGA_IP_ADDR[23:16];5'd14: R_GMII_TX_DATA <= P_FPGA_IP_ADDR[15:8];5'd15: R_GMII_TX_DATA <= P_FPGA_IP_ADDR[7:0];5'd16: R_GMII_TX_DATA <= P_DST_IP_ADDR[31:24];5'd17: R_GMII_TX_DATA <= P_DST_IP_ADDR[23:16];5'd18: R_GMII_TX_DATA <= P_DST_IP_ADDR[15:8];5'd19: R_GMII_TX_DATA <= P_DST_IP_ADDR[7:0];default: R_GMII_TX_DATA <= 8'd0;endcaseendLP_ST_UDP_HEAD:beginR_UDP_HEAD_CNT <= R_UDP_HEAD_CNT + 1;case(R_UDP_HEAD_CNT)3'd0: R_GMII_TX_DATA <= P_FPGA_UDP_PORT[15:8];3'd1: R_GMII_TX_DATA <= P_FPGA_UDP_PORT[7:0];3'd2: R_GMII_TX_DATA <= P_DST_UDP_PORT[15:8];3'd3: R_GMII_TX_DATA <= P_DST_UDP_PORT[7:0];3'd4: R_GMII_TX_DATA <= W_UDP_LEN[15:8];3'd5: R_GMII_TX_DATA <= W_UDP_LEN[7:0];3'd6: R_GMII_TX_DATA <= W_UDP_HEAD_CHECK[15:8];3'd7: R_GMII_TX_DATA <= W_UDP_HEAD_CHECK[7:0];default: R_GMII_TX_DATA <= 8'd0;endcaseendLP_ST_TX_DATA:beginR_UDP_DATA_CNT <= R_UDP_DATA_CNT + 1;R_GMII_TX_DATA <= I_TX_DATA;endLP_ST_TX_FILL:beginR_FILL_CNT <= R_FILL_CNT + 1;R_GMII_TX_DATA <= 8'd0;endLP_ST_FCS:beginR_CRC_CNT <= R_CRC_CNT + 1;R_GMII_TX_DATA <= 8'd0;enddefault:beginR_PREAMBLE_CNT <= 3'd0;R_ETH_HEAD_CNT <= 4'd0;R_IP_HEAD_CNT  <= 5'd0;R_UDP_HEAD_CNT <= 3'd0;R_UDP_DATA_CNT <= 16'd0;R_FILL_CNT     <= 5'd0;R_CRC_CNT      <= 2'd0;R_GMII_TX_EN   <= 1'b0;R_GMII_TX_DATA <= 8'd0;endendcaseend
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginO_DATA_REQ <= 1'b0;endelsebeginif((|(LP_ST_UDP_HEAD & R_CS)) && R_UDP_HEAD_CNT == 3'd6)beginO_DATA_REQ <= 1'b1;endelse if(I_TX_DATA_LEN == 16'd1)beginO_DATA_REQ <= 1'b0;endelse if((R_UDP_DATA_CNT == I_TX_DATA_LEN - 16'd2) && (I_TX_DATA_LEN > 16'd1) && (|(LP_ST_TX_DATA & R_CS))) beginO_DATA_REQ <= 1'b0;endend
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_CRC_EN <= 1'b0;endelse if(R_CS[2])beginR_CRC_EN <= 1'b1;endelse if(R_CS[7])beginR_CRC_EN <= 1'b0;end
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_CRC_ST <= 1'b0;endelsebeginR_CRC_ST <= R_CS[7];end
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginO_GMII_TXD <= 8'd0;O_GMII_TX_EN <= 1'b0;endelse if(~R_CRC_ST & R_GMII_TX_EN)beginO_GMII_TXD <= R_GMII_TX_DATA;O_GMII_TX_EN <= 1'b1;	endelse if(R_CRC_ST)beginO_GMII_TX_EN <= 1'b1;case(R_CRC_CNT2)2'd0:O_GMII_TXD <= W_CRC_RES[7:0];2'd1:O_GMII_TXD <= W_CRC_RES[15:8];2'd2:O_GMII_TXD <= W_CRC_RES[23:16];2'd3:O_GMII_TXD <= W_CRC_RES[31:24];default:O_GMII_TXD <= 8'h00;endcaseendelsebeginO_GMII_TXD <= 8'd0;O_GMII_TX_EN <= 1'b0;	end
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_CRC_CNT2 <= 2'd0;endelse if(R_CRC_ST)beginR_CRC_CNT2 <= R_CRC_CNT2 + 1;endelsebeginR_CRC_CNT2 <= 2'd0;end
endalways @ (posedge I_CLK_125M)
beginif(~I_SYS_RSTN)beginR_IFG_FLAG <= 1'b0;R_IFG_CNT <= 4'd0;O_TX_DONE <= 1'b0;endelsebegincase(R_IFG_FLAG)0:beginR_IFG_CNT <= 4'd0;O_TX_DONE <= 1'b0;if(R_CRC_ST && (R_CRC_CNT2 == 2'd3))beginR_IFG_FLAG <= 1'b1;endelsebeginR_IFG_FLAG <= 1'b0;endend1:beginif(R_IFG_CNT == 4'd12)beginO_TX_DONE <= 1'b1;R_IFG_CNT <= 4'd0;R_IFG_FLAG <= 1'b0;endelsebeginO_TX_DONE <= 1'b0;R_IFG_CNT <= R_IFG_CNT + 1;R_IFG_FLAG <= 1'b1;endenddefault:beginR_IFG_FLAG <= 1'b0;endendcaseend
end// | ====================================   模块内部模块例化   ====================================
// CRC32 校验
CRC32_D8 INST_CRC32_D8(.I_OPR_CLK  (I_CLK_125M),.I_OPR_RSTN (I_SYS_RSTN),.I_CRC_INIT (R_CS[0] & I_TX_EN),.I_CRC_EN   (R_CRC_EN),.I_DATA     (R_GMII_TX_DATA),.O_CRC_RES  (W_CRC_RES));// IP首部校验模块
IP_HEAD_CHECK_SUM_CAL_MDL INST_IP_HEAD_CHECK_SUM_CAL_MDL(.I_OPR_CLK           (I_CLK_125M),.I_OPR_RSTN          (I_SYS_RSTN),.I_CAL_EN            (I_TX_EN),.I_IP_HEAD_VER       (LP_IP_HEAD_VER),.I_IP_HEAD_LEN       (LP_IP_HEAD_LEN),.I_IP_HEAD_TOS       (LP_IP_HEAD_TOS),.I_IP_HEAD_TOTLEN    (W_IP_LEN),.I_IP_HEAD_ID        (LP_IP_HEAD_ID),.I_IP_HEAD_FLAG      (LP_IP_HEAD_FLAG),.I_IP_HEAD_OFFSET    (LP_IP_HEAD_OFFSET),.I_IP_HEAD_TTL       (LP_IP_HEAD_TTL),.I_IP_HEAD_PROT      (LP_IP_HEAD_PROT),.I_IP_HEAD_SRC_ADDR  (P_FPGA_IP_ADDR),.I_IP_HEAD_DST_ADDR  (P_DST_IP_ADDR),.O_IP_HEAD_CHECK_SUM (W_IP_HEAD_CHECK_SUM));endmodule

仿真测试

TEST BENCH

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------   UDP 数据发送模块测试    ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-01-15
// | 完成时间 : 2022-01-15
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// | 			-1- 
// | 			-2- 
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:`timescale 1ns / 1psmodule TB_UDP_TX_MDL();
// | ====================================  模块可配置参数声明  ==================================== 
parameter 			P_FPGA_MAC_ADDR				=				48'h00_0a_35_01_fe_c0;   // FPGA侧 MAC地址
parameter 			P_FPGA_IP_ADDR				=				{8'd192,8'd168,8'd8,8'd3};   // FPGA侧 IP地址
parameter 			P_FPGA_UDP_PORT				=				16'd6001; 				     // FPGA侧 UDP端口号parameter 			P_DST_MAC_ADDR				=				48'hC8_5B_76_DD_0B_38;   // 目的侧 MAC地址
parameter 			P_DST_IP_ADDR				=				{8'd192,8'd168,8'd8,8'd2};   // 目的侧 IP地址
parameter 			P_DST_UDP_PORT				=				16'd6002; 				     // 目的侧 UDP端口号// | ==================================== 模块输入输出端口声明 ====================================
// 时钟、复位
reg 															I_CLK_125M;
reg 															I_SYS_RSTN;
// 发送 握手信号
reg 															I_TX_EN;//脉冲信号
wire        													O_TX_DONE;
// 数据长度
reg 				[15:0]										I_TX_DATA_LEN;   //一直有效至下一次传输开始,// 最小数据为 1 最大为 1472(最大值前提是 IP首部长度 20)
// 数据
reg 				[7:0]										I_TX_DATA;
wire    														O_DATA_REQ;
// GMII接口
wire 															O_GMII_TX_CLK;
(*IOB = "TRUE"*)
wire     		    [7:0]										O_GMII_TXD; //插入I/O缓冲,提高驱动
(*IOB = "TRUE"*)
wire     													    O_GMII_TX_EN; //插入I/O缓冲
// | ====================================     产生测试激励 	   ====================================
initial I_CLK_125M = 1'b0;
always #4 I_CLK_125M = ~I_CLK_125M;initial
beginI_SYS_RSTN = 0;I_TX_EN = 0;I_TX_DATA_LEN = 16'd0;I_TX_DATA = 8'd0;#124;I_SYS_RSTN = 1;#29;@(posedge I_CLK_125M)I_TX_EN <= 1'b1;I_TX_DATA_LEN <= 16'd1000;@(posedge I_CLK_125M)I_TX_EN <= 1'b0;@(posedge O_DATA_REQ)repeat(1000)begin@(posedge I_CLK_125M)I_TX_DATA <= I_TX_DATA + 1; end@(posedge O_TX_DONE);#2000;@(posedge I_CLK_125M)I_TX_EN <= 1'b1;I_TX_DATA_LEN <= 16'd10;@(posedge I_CLK_125M)I_TX_EN <= 1'b0;@(posedge O_DATA_REQ)repeat(10)begin@(posedge I_CLK_125M)I_TX_DATA <= I_TX_DATA + 1; end@(posedge O_TX_DONE);#2000;$finish;endUDP_TX_MDL #(.P_FPGA_MAC_ADDR(P_FPGA_MAC_ADDR),.P_FPGA_IP_ADDR (P_FPGA_IP_ADDR),.P_FPGA_UDP_PORT(P_FPGA_UDP_PORT),.P_DST_MAC_ADDR (P_DST_MAC_ADDR),.P_DST_IP_ADDR  (P_DST_IP_ADDR),.P_DST_UDP_PORT (P_DST_UDP_PORT)) INST_UDP_TX_MDL (.I_CLK_125M    (I_CLK_125M),.I_SYS_RSTN    (I_SYS_RSTN),.I_TX_EN       (I_TX_EN),.O_TX_DONE     (O_TX_DONE),.I_TX_DATA_LEN (I_TX_DATA_LEN),.I_TX_DATA     (I_TX_DATA),.O_DATA_REQ    (O_DATA_REQ),.O_GMII_TX_CLK (O_GMII_TX_CLK),.O_GMII_TXD    (O_GMII_TXD),.O_GMII_TX_EN  (O_GMII_TX_EN));endmodule

仿真结果


有问题可以评论区留言~


http://chatgpt.dhexx.cn/article/Vtg2uidi.shtml

相关文章

Verilog:【8】基于FPGA实现SD NAND FLASH的SPI协议读写

碎碎念&#xff1a; 终于熬过了期末周&#xff0c;可以开始快乐的开发之旅了。 这一期作为一千粉后的首篇博客&#xff0c;由于之后项目会涉及到相关的部分&#xff0c;因此介绍的是使用FPGA实现SD NAND FLASH的读写操作&#xff0c;以雷龙发展提供的CS创世SD NAND FLASH样品为…

【FPGA算法加速】运行PYNQ,对应FPGA芯片版本:赛灵思黑金AX7020

黑金AX7020开放板实物图&#xff1a; 这里的右下角需要连接本地的路由器&#xff0c;图中并未连上&#xff0c;PC端与FPGA在同一路由的网下&#xff0c;到时候PC端可以在线访问FPGA的资源。 一、连接FPGA硬件设备 1、 SD卡插回开发板&#xff0c;确认开发板启动模式为 SD卡模…

黑金Xilinx FPGA学习笔记(一)verilogHDL扫盲文-(1)

verilog简介 HDL 顾名思义Hardware Description Languag verilog HDL 语言的语法和格式都比较随便&#xff0c;它没有 VDL HDL 语言那么严谨&#xff0c;因此受到了广泛的应用。 0.3RTL级和组合逻辑级 笔者的眼中 Verilog HDL 语言建立的硬件模块可以 分为有时钟源和无时钟…

基于FPGA的示波器设计

目录 一、设计要求 二、系统架构设计 一、设计要求 本次基于FPGA的示波器设计主要技术要求包含以下内容&#xff1a; 系统能够实现模数转换功能&#xff0c;即包含ADC驱动模块&#xff1b;系统能够实现ADC采集数据的缓存功能&#xff1b;系统包含辅助测试模块&#xff0c;DA…

FPGA实现 贪吃蛇

一. 硬件 黑金AX4010AN430显示屏&#xff08;480*272&#xff09;蓝牙模块安卓手机 采用app通过蓝牙模块来操作蛇的移动情况 二. 数据的存储 reg[18:0] Snake[0:7]; //蛇 18:10 x 9:1 y 0 :flag 是否是蛇身 reg[17:0] Food; // 食物三. 蛇的移动的方向 通过上下左右来…

黑金全部开发板资料(FPGA+ZYNQ)分享

黑金开发板 能找到的所有黑金开发板的资料&#xff0c;足够学习使用。 ALTERA CYCLONE 10 黑金开发板ZYNQ7020_2019 黑金zynq7015_2017 黑金zynq7010_2017 …

FPAG—UART串口实现与解析-黑金fpga资料解析

UART实现-黑金fpga开发板案例解析 uart 异步串口通讯 无需时钟线&#xff0c;俩根线一跟复制发数据一根负责收数据。 具体的时序如下图1&#xff1a; 图1&#xff1a;UART时序图 可以看到 当数据线由高位变为地位时&#xff0c;即遇到一个下降沿时刻&#xff0c;表示开始这一…

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十四:SD卡模块

驱动SD卡是件容易让人抓狂的事情,驱动SD卡好比SDRAM执行页读写,SD卡虽然不及SDRAM的麻烦要求(时序参数),但是驱动过程却有猥琐操作。除此此外,描述语言只要稍微比较一下C语言,描述语言一定会泪流满面,因为嵌套循环,嵌套判断,或者嵌套函数等都是它的痛。. 史莱姆模块是…

黑金AX7Z100 FPGA开发板移植LWIP库(二)PL端

前言 上一篇博文中实验了PS端移植LwIP库的演示程序。本篇接下来基于Vivado17.4整理比较详细的PL端移植过程。 一、Vivado 工程建立 1、新建一个空工程&#xff0c;名称为net_lwip_pl。 2、FPGA芯片选择xc7z100ffg900-2。 二、配置PS&PL系统硬件 1、工程建好以后&…

【FPGA从0开始系列】黑金EP4CE10F17C8开发板按键实验(二)

项目目录 1.实验目的2.准备阶段3.实验原理4.编写Verilog HDL程序5.配置引脚6.查看和分析RTL7.下载程序8.总结 1.实验目的 查阅AX4010黑金系列用户手册&#xff0c;编写按键程序&#xff0c;实验简单的按键控制LED的功能&#xff0c;同时学习 Quartus RTL Viewer 的使用 2.准备…

汉诺塔(C语言实现)

目录 汉诺塔的游戏规则&#xff1a; 当A只有一个环的时候&#xff1a; 当A只有两个环的时候&#xff1a; 当A只有三个环的时候&#xff1a; 思路&#xff1a; 当n1时&#xff1a; 当n2时&#xff1a; 当n3时&#xff1a; 当n4时&#xff1a; 见代码 运行截图 汉诺塔的游戏…

【C语言】汉诺塔问题

汉诺塔是一个非常经典的问题&#xff0c;其背后是一个传说故事&#xff1a; 在世界中心贝拿勒斯&#xff08;在印度北部&#xff09;的圣庙里&#xff0c;一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候&#xff0c;在其中一根针上从下到上地穿好了由大到小的6…

汉诺塔C语言步骤解析

汉诺塔问题在C语言中一般采用递归法来写&#xff0c;假设有A、B、C三根棒&#xff0c;A棒放着若干个圆盘&#xff0c;将其移动到C棒上&#xff0c;中途可在B棒中暂时放置圆盘。 分析&#xff1a; (1) 如果只有一个圆盘&#xff0c;则把该圆盘从A棒移动到C棒 (2) 如果圆盘数量…

汉诺塔递归的c语言实现(递归)

对于递归来讲, 汉诺塔实际是经典到不能再经典的例子了, 每个数据结构的教材对会提到. 但是到最后只给出一段类似下面的一段代码: #include<stdio.h>void move(int n,char a,char b,char c) {if(n1)printf("\t%c->%c\n",a,c); //当n只有1个的时候直接从…

C语言编程实现汉诺塔问题

C语言编程实现汉诺塔问题 1.首先解释一下&#xff0c;汉诺塔问题&#xff1a;古代梵塔内有A、B、C3个座&#xff0c;开始时A座上面有64个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。一个老和尚想把64个盘子从A移到C&#xff0c;规定移动过程中3个…

汉诺塔C语言实现(纯代码)

a、b、c三座塔&#xff0c;将n个从小到大&#xff08;自上而下&#xff09;的圆盘从a移动到c&#xff0c;移动期间小圆盘必须在大圆盘上面&#xff0c;问移动步骤。 #include<stdio.h>int main() {void hanoi(int n,char one,char two,char three);int m;printf("…

【汉诺塔】C语言递归解法,深层次地带你理解汉诺塔公式

目录 汉诺塔公式 汉诺塔问题在数学层面的公式&#xff1a; C语言递归公式 两层汉诺塔 三层汉诺塔 递归问题可谓是学习C语言以来的第一个拦路虎&#xff0c;而汉诺塔问题更是递归中对新手很不友好的一道经典题&#xff0c;我们接下来从公式角度和更深层的图解角度来让你理解…

汉诺塔c语言代码实现

汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重…

【C语言】汉诺塔超详解(脑把脑解释)

文章目录 一、前言二、准备工作关于递归函数 三、汉诺塔步数计算详解&#xff1a;函数的设计符合递归函数的两个基本条件&#xff1a; 四、汉诺塔步骤打印&#xff08;绝对详细&#xff0c;仔细看就能看懂 &#xff09;铺垫换个思路&#xff1a;柱子竟然是可以移动的&#xff0…

汉诺塔C语言实现

汉诺塔背景 文章目录 汉诺塔背景前言一、什么是汉诺塔&#xff1f;二、汉诺塔问题的背景三、汉诺塔问题的代码实现1.代码原理2.代码实现 前言 汉诺塔问题&#xff0c;是心理学实验研究常用的任务之一。该问题的主要材料包括三根高度相同的柱子和一些大小及颜色不同的圆盘&…