基于FPGA的UDP 通信(五)

news2024/11/15 21:57:30



引言

前文链接:

基于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 / 1ps

module 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)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD1_L1[0] <= 17'd0;
        R_ADD1_L1[1] <= 17'd0;
        R_ADD1_L1[2] <= 17'd0;
        R_ADD1_L1[3] <= 17'd0;
	end
	else 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)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD1_L2[0] <= 18'd0;
		R_ADD1_L2[1] <= 18'd0;
	end
	else
	begin
		R_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)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD1_L3 <= 19'd0;
	end
	else
	begin
		R_ADD1_L3 <= R_ADD1_L2[0] + R_ADD1_L2[1];
	end
end

// ADD1 第四级
always @ (posedge I_OPR_CLK)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD1_L4 <= 20'd0;
	end
	else
	begin
		R_ADD1_L4 <= R_ADD1_L3 + I_IP_HEAD_DST_ADDR[15:0];
	end
end

// ADD2 
always @ (posedge I_OPR_CLK)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD2 <= 17'd0;
	end
	else
	begin
		R_ADD2 <= R_ADD1_L4[19:16]+ R_ADD1_L4[15:0];
	end
end

// ADD3 
always @ (posedge I_OPR_CLK)
begin
	if(~I_OPR_RSTN)
	begin
		R_ADD3 <= 16'd0;
	end
	else
	begin
		R_ADD3 <= R_ADD2[16]+ R_ADD2[15:0];
	end
end


assign O_IP_HEAD_CHECK_SUM = ~R_ADD3;

endmodule

FCS计算

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

CRC校验程序在线生成icon-default.png?t=MBR7http://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.com

module 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;
generate
    for(GV_8 = 0;GV_8 < 8;GV_8 = GV_8 + 1)
    begin
        assign W_DATA[GV_8] = I_DATA[7-GV_8];
    end
endgenerate

always @ (posedge I_OPR_CLK)
begin
    if(~I_OPR_RSTN)
    begin
        R_CRC_RES <= {32{1'b1}};
    end
    else if(I_CRC_INIT)
    begin
       R_CRC_RES <= {32{1'b1}}; 
    end
    else if(I_CRC_EN)
    begin
        R_CRC_RES <= nextCRC32_D8(W_DATA,R_CRC_RES);
    end
end

genvar GV_32;
generate
    for(GV_32 = 0;GV_32 < 32;GV_32 = GV_32 + 1)
    begin
        assign 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;
  begin
    d = 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
endfunction

endmodule

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"*)
output  reg 		[7:0]										O_GMII_TXD, //插入I/O缓冲,提高驱动
(*IOB = "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_1_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)
begin
	if(~I_SYS_RSTN)
	begin
		R_CS <= LP_ST_IDLE;
	end
	else
	begin
		R_CS <= R_NS;
	end
end

always @ (*)
begin
	if(~I_SYS_RSTN)
	begin
		R_NS = LP_ST_IDLE;
	end
	else
	begin
		case(R_CS)
			LP_ST_IDLE:
			begin
				if(I_TX_EN)
				begin
					R_NS = LP_ST_PREAMBLE;
				end
				else
				begin
					R_NS = LP_ST_IDLE;
				end
			end 		
			LP_ST_PREAMBLE:
			begin
				if(R_PREAMBLE_CNT == 3'd7)
				begin
					R_NS = LP_ST_ETH_HEAD;
				end
				else
				begin
					R_NS = LP_ST_PREAMBLE;
				end
			end 
			LP_ST_ETH_HEAD:
			begin
				if(R_ETH_HEAD_CNT == 4'd13)
				begin
					R_NS = LP_ST_IP_HEAD;
				end
				else
				begin
					R_NS = LP_ST_ETH_HEAD;
				end
			end 
			LP_ST_IP_HEAD:
			begin
				if(R_IP_HEAD_CNT == 5'd19)
				begin
					R_NS = LP_ST_UDP_HEAD;
				end
				else
				begin
					R_NS = LP_ST_IP_HEAD;
				end
			end	
			LP_ST_UDP_HEAD:
			begin
				if(R_UDP_HEAD_CNT == 3'd7)
				begin
					R_NS = LP_ST_TX_DATA;
				end
				else
				begin
					R_NS = LP_ST_UDP_HEAD;
				end
			end 
			LP_ST_TX_DATA:
			begin
				if((I_TX_DATA_LEN < 16'd18) && (R_UDP_DATA_CNT == I_TX_DATA_LEN - 1'b1))
				begin
					R_NS = LP_ST_TX_FILL;
				end
				else if(R_UDP_DATA_CNT == I_TX_DATA_LEN - 1'b1)
				begin
					R_NS = LP_ST_FCS;
				end
				else
				begin
					R_NS = LP_ST_TX_DATA;
				end
			end 	
			LP_ST_TX_FILL:
			begin
				if(R_FILL_CNT == (5'd17 - I_TX_DATA_LEN[4:0]))
				begin
					R_NS = LP_ST_FCS;
				end
				else
				begin
					R_NS = LP_ST_TX_FILL;
				end
			end 	
			LP_ST_FCS:
			begin
				if(R_CRC_CNT == 2'd3)
				begin
					R_NS = LP_ST_IDLE;
				end
				else
				begin
					R_NS = LP_ST_FCS;
				end
			end
			default:
			begin
				R_NS = LP_ST_IDLE;
			end 					
		endcase
	end
end
// 以太网数据发送
always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		R_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;
	end
	else
	begin
		case(R_CS)
			LP_ST_IDLE:
			begin
				R_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;
			end
			LP_ST_PREAMBLE:
			begin
				R_PREAMBLE_CNT <= R_PREAMBLE_CNT + 1;
				R_GMII_TX_EN   <= 1'b1;
				if(R_PREAMBLE_CNT <= 3'd6)
					R_GMII_TX_DATA <= 8'h55;
				else
					R_GMII_TX_DATA <= 8'hd5;
			end
			LP_ST_ETH_HEAD:
			begin
				R_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;
				endcase
			end
			LP_ST_IP_HEAD:
			begin
				R_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;
				endcase
			end
			LP_ST_UDP_HEAD:
			begin
				R_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;
				endcase
			end
			LP_ST_TX_DATA:
			begin
				R_UDP_DATA_CNT <= R_UDP_DATA_CNT + 1;
				R_GMII_TX_DATA <= I_TX_DATA;
			end
			LP_ST_TX_FILL:
			begin
				R_FILL_CNT <= R_FILL_CNT + 1;
				R_GMII_TX_DATA <= 8'd0;
			end
			LP_ST_FCS:
			begin
				R_CRC_CNT <= R_CRC_CNT + 1;
				R_GMII_TX_DATA <= 8'd0;
			end
			default:
			begin
				R_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;
			end
		endcase
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		O_DATA_REQ <= 1'b0;
	end
	else
	begin
		if((|(LP_ST_UDP_HEAD & R_CS)) && R_UDP_HEAD_CNT == 3'd6)
		begin
			O_DATA_REQ <= 1'b1;
		end
		else if(R_UDP_DATA_CNT == I_TX_DATA_LEN - 16'd2)
		begin
			O_DATA_REQ <= 1'b0;
		end
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		R_CRC_EN <= 1'b0;
	end
	else if(R_CS[2])
	begin
		R_CRC_EN <= 1'b1;
	end
	else if(R_CS[7])
	begin
		R_CRC_EN <= 1'b0;
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		R_CRC_ST <= 1'b0;
	end
	else
	begin
		R_CRC_ST <= R_CS[7];
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		O_GMII_TXD <= 8'd0;
		O_GMII_TX_EN <= 1'b0;
	end
	else if(~R_CRC_ST & R_GMII_TX_EN)
	begin
		O_GMII_TXD <= R_GMII_TX_DATA;
		O_GMII_TX_EN <= 1'b1;
		O_TX_DONE <= 1'b0;		
	end
	else if(R_CRC_ST)
	begin
		O_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;
		endcase
	end
	else
	begin
		O_GMII_TXD <= 8'd0;
		O_GMII_TX_EN <= 1'b0;	
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		R_CRC_CNT2 <= 2'd0;
	end
	else if(R_CRC_ST)
	begin
		R_CRC_CNT2 <= R_CRC_CNT2 + 1;
	end
	else
	begin
		R_CRC_CNT2 <= 2'd0;
	end
end

always @ (posedge I_CLK_125M)
begin
	if(~I_SYS_RSTN)
	begin
		R_IFG_FLAG <= 1'b0;
		R_IFG_CNT <= 4'd0;
		O_TX_DONE <= 1'b0;
	end
	else
	begin
		case(R_IFG_FLAG)
			0:
			begin
				R_IFG_CNT <= 4'd0;
				O_TX_DONE <= 1'b0;

				if(R_CRC_ST && (R_CRC_CNT2 == 2'd3))
				begin
					R_IFG_FLAG <= 1'b1;
				end
				else
				begin
					R_IFG_FLAG <= 1'b0;
				end
			end
			1:
			begin
				if(R_IFG_CNT == 4'd12)
				begin
					O_TX_DONE <= 1'b1;
					R_IFG_CNT <= 4'd0;
					R_IFG_FLAG <= 1'b0;
				end
				else
				begin
					O_TX_DONE <= 1'b0;
					R_IFG_CNT <= R_IFG_CNT + 1;
					R_IFG_FLAG <= 1'b1;
				end
			end
			default:
			begin
				R_IFG_FLAG <= 1'b0;
			end
		endcase
	end
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 / 1ps



module 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
begin
	I_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;

end

UDP_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://www.coloradmin.cn/o/164881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

12.I/O复用

I/O复用 多进程方式跳过 基于I/O复用的服务器端 接下来讨论并发服务器实现方法的延伸。如果有读者已经跳过第10章和第11章&#xff0c;那就只需把本章内容当做并发服务器实现的第一种方法即可。将要讨论的内容中包含一部分与多进程服务器端的比较&#xff0c;跳过第10章和第…

Android WebView中H5调用Android原生方法

最近做项目&#xff0c;使用webView看一些网页&#xff0c;和网页开发一起找什么方法进行交互&#xff0c;还好解决&#xff0c;分享一下经验。 对于webView的使用就不写了&#xff0c;百度大法好&#xff0c;主要是交互方面&#xff0c;对WebView增加以下代码&#xff1a; bi…

五个了解自己天赋优势的分析工具(一)霍兰德兴趣测试

霍兰德兴趣测试 霍兰德职业兴趣自测&#xff08;Self-Directed Search&#xff09;是由美国职业指导专家霍兰德&#xff08;John Holland&#xff09;根据他本人大量的职业咨询经验及其职业类型理论编制的测评工具。 霍兰德认为&#xff0c;个人职业兴趣特性与职业之间应有一…

74、Beyond RGB: Scene-Property Synthesis with Neural Radiance Fields

简介 List item 论文地址&#xff1a;http://arxiv-export3.library.cornell.edu/abs/2206.04669v1 利用隐式三维表示和神经渲染的最新进展&#xff0c;从综合模型的角度提供了一种新的场景理解方法&#xff0c;能够从新颖的视点渲染照片逼真的RGB图像&#xff0c;而且还能够…

我们怎样才能过好这一生?

文章目录1. 日拱一卒&#xff0c;功不唐捐1.1 适当的时候给自己一个奖励1.2 一个人可能走的更快&#xff0c;但一群人才能走的更远1.3 通过一些事情去逼自己一把1.4 从真理中去感悟1.5 当你面临绝路时2. 梦想的意义不在于实现3. 孤独4. 烦恼5. 别总说来日方长6. 忍和韧性7. 事情…

低成本搭建一台家庭存储服务器:前篇

本篇文章&#xff0c;记录搭建备份服务器的过程。 写在前面 今年考虑专门搭建一台用于数据备份的机器&#xff0c;一来今年外出的需求比较多&#xff0c;历史的设备已经用了几年了&#xff0c;需要有更新的设备来“接力”&#xff1b;二来也想验证方案的靠谱程度&#xff0c;…

k8s之ingress实战小栗子

写在前面 本文接k8s之ingress 。 本文看一个基于ingress作为流量入口的实战例子&#xff0c;架构图如下&#xff1a; 接下来详细看下。 1&#xff1a;部署MariaDB 首先我们需要定义MariaDB使用的configmap&#xff0c;如下&#xff1a; apiVersion: v1 kind: ConfigMap meta…

1587_AURIX_TC275_SMU的部分寄存器3

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) SMU的章节&#xff0c;剩下的部分全都是寄存器了&#xff0c;没有太多需要特别关注的。因此&#xff0c;接下来选择性整理&#xff0c;完成整个SMU的文档学习整理。 这一页是上一份笔记的…

05_FreeRTOS中断管理

目录 什么是中断 中断相关寄存器 源码实验 什么是中断 简介:让CPU打断正常运行的程序,转而去处理紧急的事件(程序) ,就叫中断。 举例:上课可以比做CPU正常运行的程序,上厕所可以比做中断程序。 中断执行机制,可简单概括为三步: 中断请求:外设产生中断请求(GPIO外部中断、…

【精品】k8s(Kubernetes)cka由基础到实战学法指南

轻松快速学会k8s四招 图1 k8s四招 学完本篇,您会获得什么惊喜? 从初学k8s,到帮助别人学会的过程中,发现朋友们和我,并非不努力,而是没有掌握更好的方法。有方法可让我们学的更快更轻松,这篇文章,以一个networkpolicy的题目,来逐步讲解,帮助大家建立一种,自己可以根…

Java基础语法

文章目录Java 基础语法一、注释1. 注释介绍2. 注释分类3. 注释颜色二、关键字1. 关键字介绍2. 所有关键词三、字面量四、变量1. 变量2. Debug 工具1&#xff09;如何加断点&#xff1f;2&#xff09;如何开启 Debug 运行&#xff1f;3&#xff09;点哪里 ?4&#xff09;看哪里…

ElasticSearch架构之整合ELK

前言本篇文章主要是说ElasticSearch对Logstash、FileBeat、Kibana整合形成ELK的架构&#xff0c;为什么需要整合这个架构呢&#xff1f;一个很重要的原因就是我们开发过程中有相当多的日志需要进行查看&#xff0c;如果我们要查找一个问题需要到多台服务器进行查看那是相当麻烦…

【Java基础知识 4】Java数据类型之间的转换、运算符

本文已收录专栏 &#x1f332;《Java进阶之路》&#x1f332; 目录 &#x1f334;基本数据类型 &#x1f343;01、布尔 &#x1f343;02、byte &#x1f343;03、short &#x1f343;04、int &#x1f343;05、long &#x1f343;06、float &#x1f343;07、double …

C生万物 | 详解程序环境和预处理【展示程序编译+链接全过程】

&#x1f451;作者主页&#xff1a;Fire_Cloud_1 &#x1f3e0;学习社区&#xff1a;烈火神盾 &#x1f517;专栏链接&#xff1a;万物之源——C 文章目录一、程序的翻译环境和执行环境二、详解编译链接1、前言小知识&#x1f50d;2、翻译环境【important】2.1 编译① 预编译【…

【LeetCode每日一题】【2023/1/15】2293. 极大极小游戏

文章目录2293. 极大极小游戏方法1&#xff1a;双指针2293. 极大极小游戏 LeetCode: 2293. 极大极小游戏 简单\color{#00AF9B}{简单}简单 给你一个下标从 0 开始的整数数组 nums &#xff0c;其长度是 2 的幂。 对 nums 执行下述算法&#xff1a; 设 n 等于 nums 的长度&#x…

Windows 10的子系统不是非Ubuntu不可

Ubuntu 的制造商 Canonical 早已和微软进行合作&#xff0c;让我们体验了极具争议的 Bash on Windows。外界对此也是褒贬不一&#xff0c;许多 Linux 重度用户则是质疑其是否有用&#xff0c;以及更进一步认为 Bash on Windows 是一个安全隐患。 Unix 的 Bash 是通过 WSL&#…

MyBatis动态设置表名 获取添加功能自增的主键 自定义映射

MyBatis动态设置表名 获取添加功能自增的主键 自定义映射动态设置表名获取添加功能自增的主键自定义映射解决字段名和属性名不一致的情况为字段起别名,保持和属性名的一致设置全局配置,保持和属性名的一致通过resultMap设置自定义的映射关系动态设置表名 mapper接口&#xff1a…

Java实现线段树

问题一&#xff1a;开始的子区间是怎么分的&#xff1f; M (LR)/2&#xff0c;左子区间为[L,M]&#xff0c;右子区间为[M1,R] 问题二&#xff1a;如何进行区间统计&#xff1f; 假设这13个数为1,2,3,4,1,2,3,4,1,2,3,4,1. 在区间之后标上该区间的数字之和&#xff1a; 如…

Windows安全加固-AD建立与加入

AD建立与加入 实验环境说明&#xff1a; 域控从机&#xff1a; Windows 2003 域控主机&#xff1a; Windows 2008 IP设置 设置同一网段-保证连通性 将NDS指向Windos2008 第一步&#xff0c;域控服务器需要将服务器设为静态 IP地址 1.将Windows2008服务器IP地址修改为192.1…

c++ - 第24节 - c++的IO流

1.C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf()与printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。C语言借助了相应的缓冲区…