jjzjj

基于FPGA的UDP 通信(三)

在路上-正出发 2023-12-08 原文


目录

引言

设计框图

UDP接收模块

设计源码

TEST BENCH

仿真结果


引言

前文链接:

基于FPGA的UDP 通信(一)

基于FPGA的UDP 通信(二)

本文基于FPGA设计千兆以太网通信模块:FPGA接收上位机数据。后续会介绍FPGA发送UDP数据的设计。

设计条件:

FPGA芯片:xc7a35tfgg484-2

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

MAC与PHY接口:GMII

接口类型:RJ-45



设计框图

本文先实现接收支路的功能。所设计的模块主要用于 PHY芯片和FPGA之间的通信,从原理图可知,与之对应的引脚:

引脚含义(PHY芯片手册 RTL8211):

UDP接收模块

数据解析利用状态机来实现,按照上篇文章讲的以太网数据格式,按照接收步骤依次解析。具体思路体现在设计代码里,比较容易理解,此处就不再赘述,给出设计源码:

设计源码

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------       UDP 数据接收模块        ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-01-11
// | 完成时间 : 2022-01-11
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |            -1- 参数可配置
// |            -2- 包含MAC地址检验、IP地址检验;未包含UDP端口号检验
// |             -3- 不做接收侧的CRC校验
// |            -4- IP首部仅校验首部长度、目的地址字段
// |
// |
// | =================================         模块修改历史纪录       =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:






`timescale 1ns / 1ps
module UDP_RX_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地址
)(
// | ==================================== 模块输入输出端口声明 ====================================
// 系统复位 
input                                                             I_SYS_RSTN,
// PHY芯片接口
input                                                             I_PHY_RX_CLK,
input                                                             I_PHY_RXDV,
input                 [7:0]                                        I_PHY_RXD,
input                                                             I_PHY_RXER,
// 用户数据
output     reg                                                        O_ETH_USR_DATA_VAL,
output     reg            [7:0]                                        O_ETH_USR_DATA
    );
// | ====================================   模块内部参数声明   ====================================
// 状态编码
localparam             LP_ST_IDLE                     =                7'b000_0001;
localparam             LP_ST_PREAMBLE                 =                7'b000_0010;
localparam             LP_ST_ETH_HEAD                 =                7'b000_0100;
localparam             LP_ST_IP_HEAD                =                7'b000_1000;
localparam             LP_ST_UDP_HEAD                 =                7'b001_0000;
localparam             LP_ST_RCV_DATA                 =                7'b010_0000;
localparam             LP_ST_RCV_END                 =                7'b100_0000;
// 以太网类型 IP数据报
localparam             LP_ETH_TYPE                 =                 16'h0800;//以太网 IP数据报 类型
// 
localparam             LP_ETH_PREAMBLE             =                8'h55;
localparam             LP_ETH_SFD                     =                8'hd5;
// | ====================================   模块内部信号声明   ====================================
// 复位同步化
wire                                                             W_RX_MDL_RSTN;
// 状态信号
reg                 [6:0]                                        R_CS;
reg                 [6:0]                                        R_NS;
// 目的MAC地址
reg                 [47:0]                                        R_DST_MAC_ADDR;
// 目的IP地址
reg                 [31:0]                                        R_DST_IP_ADDR;
// 以太网协议类型
reg                 [15:0]                                        R_ETH_TYPE;
// IP头部字节数目
reg                 [5:0]                                        R_IP_HEAD_BYTE_NUM;
// UDP数据字节数目
reg                 [15:0]                                        R_UDP_DATA_BYTE_NUM;
// 标志信号
reg                                                             R_PREAMBLE_RCV_DONE;
reg                                                             R_PREAMBLE_RCV_ERR;
reg                                                             R_ETH_HEAD_RCV_DONE;
reg                                                             R_ETH_HEAD_RCV_ERR;
reg                                                             R_IP_HEAD_RCV_DONE;
reg                                                             R_IP_HEAD_RCV_ERR;
reg                                                             R_IP_RIGHT;
reg                                                             R_UDP_HEAD_RCV_DONE;
reg                                                             R_UDP_DATA_RCV_DONE;
// 计数器
reg                 [2:0]                                        R_PREAMBLE_CNT;
reg                 [3:0]                                        R_ETH_HEAD_CNT;
reg                 [5:0]                                        R_IP_HEAD_CNT;
reg                 [2:0]                                        R_UDP_HEAD_CNT;
reg                 [15:0]                                        R_UDP_DATA_CNT;

// | ====================================   模块内部逻辑设计   ====================================
// 状态机
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        R_CS <= LP_ST_IDLE;
    end
    else if(I_PHY_RXER)
    begin
        R_CS <= LP_ST_RCV_END;
    end
    else if(I_PHY_RXDV)//避免 PHY出现异常 ,数据发送期间 ,有效信号中断
    begin
        R_CS <= R_NS;
    end
    else
    begin
        R_CS <= LP_ST_IDLE;
    end
end

always @ (*)
begin
    if(~W_RX_MDL_RSTN)
    begin

    end
    else
    begin
        case(R_CS)
            LP_ST_IDLE:
            begin
                if(I_PHY_RXDV && (I_PHY_RXD == 8'h55))
                begin
                    R_NS = LP_ST_PREAMBLE;
                end
                else
                begin
                    R_NS = LP_ST_IDLE;
                end
            end         
            LP_ST_PREAMBLE:
            begin
                if(R_PREAMBLE_RCV_DONE)
                begin
                    R_NS = LP_ST_ETH_HEAD;
                end
                else if(R_PREAMBLE_RCV_ERR)
                begin
                    R_NS = LP_ST_RCV_END;
                end
                else
                begin
                    R_NS = LP_ST_PREAMBLE;
                end
            end
            LP_ST_ETH_HEAD:
            begin
                if(R_ETH_HEAD_RCV_DONE)
                begin
                    R_NS = LP_ST_IP_HEAD;
                end
                else if(R_ETH_HEAD_RCV_ERR)
                begin
                    R_NS = LP_ST_RCV_END;
                end
                else
                begin
                    R_NS = LP_ST_ETH_HEAD;
                end
            end
            LP_ST_IP_HEAD:
            begin
                if(R_IP_HEAD_RCV_DONE & R_IP_RIGHT)
                begin
                    R_NS = LP_ST_UDP_HEAD;
                end
                else if(R_IP_HEAD_RCV_ERR)
                begin
                    R_NS = LP_ST_RCV_END;
                end
                else
                begin
                    R_NS = LP_ST_IP_HEAD;
                end
            end
            LP_ST_UDP_HEAD:
            begin
                if(R_UDP_HEAD_RCV_DONE)
                begin
                    R_NS = LP_ST_RCV_DATA;
                end
                else
                begin
                    R_NS = LP_ST_UDP_HEAD;
                end
            end
            LP_ST_RCV_DATA:
            begin
                if(R_UDP_DATA_RCV_DONE)
                begin
                    R_NS = LP_ST_RCV_END;
                end
                else
                begin
                    R_NS = LP_ST_RCV_DATA;
                end
            end
            LP_ST_RCV_END :
            begin
                if(~I_PHY_RXDV)
                begin
                    R_NS = LP_ST_IDLE;
                end
                else
                begin
                    R_NS = LP_ST_RCV_END;
                end
            end
            default:
            begin
                R_NS = LP_ST_IDLE;
            end
        endcase
    end
end

// 接收并检验前导码及SFD
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        R_PREAMBLE_RCV_DONE <= 1'b0;
        R_PREAMBLE_RCV_ERR  <= 1'b0;
        R_PREAMBLE_CNT      <= 3'd0;
    end
    else if(|(R_CS & LP_ST_PREAMBLE))
    begin
        if(I_PHY_RXDV)
        begin
            if((R_PREAMBLE_CNT <= 3'd5) && (I_PHY_RXD != LP_ETH_PREAMBLE))//接收检验 前导码
            begin
                R_PREAMBLE_RCV_DONE <= 1'b0;
                R_PREAMBLE_RCV_ERR  <= 1'b1;
                R_PREAMBLE_CNT      <= 3'd0;
            end
            else if(R_PREAMBLE_CNT == 3'd6)
            begin
                R_PREAMBLE_CNT <= 3'd0;

                if(I_PHY_RXD == LP_ETH_SFD) // 接收检验 SFD
                begin
                    R_PREAMBLE_RCV_DONE <= 1'b1;
                    R_PREAMBLE_RCV_ERR  <= 1'b0;
                end
                else
                begin
                    R_PREAMBLE_RCV_DONE <= 1'b0;
                    R_PREAMBLE_RCV_ERR  <= 1'b1;
                end
            end
            else
            begin
                R_PREAMBLE_RCV_DONE <= 1'b0;
                R_PREAMBLE_RCV_ERR  <= 1'b0;
                R_PREAMBLE_CNT      <= R_PREAMBLE_CNT + 1;
            end
        end
    end
    else
    begin
        R_PREAMBLE_RCV_DONE <= 1'b0;
        R_PREAMBLE_RCV_ERR  <= 1'b0;        
        R_PREAMBLE_CNT      <= 3'd0;
    end
end

// 接收并检验以太网帧头
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        R_ETH_HEAD_RCV_DONE <= 1'b0;
        R_ETH_HEAD_RCV_ERR  <= 1'b0;
        R_ETH_HEAD_CNT      <= 4'd0;
        R_DST_MAC_ADDR         <= 48'd0;
        R_ETH_TYPE          <= 16'd0;
    end
    else if(|(R_NS & LP_ST_ETH_HEAD))
    begin
        if(I_PHY_RXDV)
        begin
            if(R_ETH_HEAD_CNT <= 4'd5)//接收 目的MAC地址
            begin
                R_ETH_HEAD_CNT <= R_ETH_HEAD_CNT + 1;
                R_DST_MAC_ADDR <= {R_DST_MAC_ADDR[0+:40],I_PHY_RXD};
            end
            else if(R_ETH_HEAD_CNT == 4'd12)//接收以太网类型
            begin
                R_ETH_HEAD_CNT <= R_ETH_HEAD_CNT + 1;
                R_ETH_TYPE[15:8] <= I_PHY_RXD;
            end
            else if(R_ETH_HEAD_CNT == 4'd13)//接收以太网类型
            begin
                R_ETH_HEAD_CNT <= 4'd0;
                R_ETH_TYPE[7:0] <= I_PHY_RXD;

                if((R_DST_MAC_ADDR == P_FPGA_MAC_ADDR) || (R_DST_MAC_ADDR == {48{1'b1}}))//判断MAC地址是否一致
                begin
                    R_ETH_HEAD_RCV_DONE <= 1'b1;
                end
                else
                begin
                    R_ETH_HEAD_RCV_ERR <= 1'b1;
                end
            end
            else
            begin
                R_ETH_HEAD_CNT <= R_ETH_HEAD_CNT + 1;
            end
        end
    end
    else
    begin
        R_ETH_HEAD_RCV_DONE <= 1'b0;
        R_ETH_HEAD_RCV_ERR  <= 1'b0;
        R_ETH_HEAD_CNT      <= 4'd0;
    end
end

// 接收并检验IP首部
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        R_IP_HEAD_RCV_DONE <= 1'b0;
        R_IP_HEAD_RCV_ERR  <= 1'b0;
        R_IP_RIGHT            <= 1'b0;
        R_IP_HEAD_CNT      <= 6'd0;
        R_IP_HEAD_BYTE_NUM <= 6'd0;
        R_DST_IP_ADDR      <= 32'd0;
    end
    else if(|(R_NS & LP_ST_IP_HEAD))
    begin
        if(I_PHY_RXDV)
        begin
            if(R_IP_HEAD_CNT == 6'd0)//接收并计算IP头部数据字节个数
            begin
                R_IP_HEAD_BYTE_NUM <= {I_PHY_RXD[3:0],2'd0};//*4
                R_IP_HEAD_CNT <= R_IP_HEAD_CNT + 1;
            end
            else if((R_IP_HEAD_CNT >= 6'd16) && (R_IP_HEAD_CNT <= 6'd18))//接收高3字节IP地址
            begin
                R_DST_IP_ADDR <= {R_DST_IP_ADDR[23:0],I_PHY_RXD};
                R_IP_HEAD_CNT <= R_IP_HEAD_CNT + 1;
            end
            else if(R_IP_HEAD_CNT == 6'd19)//接收第4字节IP地址
            begin
                R_DST_IP_ADDR <= {R_DST_IP_ADDR[23:0],I_PHY_RXD};

                if((R_DST_IP_ADDR[23:0] == P_FPGA_IP_ADDR[31:8]) && (I_PHY_RXD == P_FPGA_IP_ADDR[7:0]))//判断 IP地址是否一致
                begin
                    R_IP_RIGHT <= 1'b1;
                    R_IP_HEAD_RCV_ERR <= 1'b0;
                end
                else
                begin
                    R_IP_RIGHT <= 1'b0;
                    R_IP_HEAD_RCV_ERR <= 1'b1;
                end

                if(6'd20 == R_IP_HEAD_BYTE_NUM)//判断 IP首部是否接收完毕
                begin
                    R_IP_HEAD_CNT <= 6'd0;
                    R_IP_HEAD_RCV_DONE <= 1'b1;
                end
                else
                begin
                    R_IP_HEAD_CNT <= R_IP_HEAD_CNT + 1;
                    R_IP_HEAD_RCV_DONE <= 1'b0;
                end
            end
            else if(R_IP_HEAD_CNT == (R_IP_HEAD_BYTE_NUM-1))//判断 IP首部是否接收完毕
            begin
                R_IP_HEAD_CNT <= 6'd0;
                R_IP_HEAD_RCV_DONE <= 1'b1;
            end
            else
            begin
                R_IP_HEAD_CNT <= R_IP_HEAD_CNT + 1'b1;
            end
        end
    end
    else
    begin
        R_IP_HEAD_RCV_DONE <= 1'b0;
        R_IP_HEAD_RCV_ERR  <= 1'b0;
        R_IP_HEAD_CNT      <= 5'd0;
        R_IP_RIGHT         <= 1'b0;
        R_IP_HEAD_BYTE_NUM <= 6'd0;
    end
end

// 接收并检验UDP首部
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        R_UDP_HEAD_RCV_DONE <= 1'b0;
        R_UDP_HEAD_CNT      <= 3'd0;
        R_UDP_DATA_BYTE_NUM <= 16'd0;
    end
    else if(|(R_NS & LP_ST_UDP_HEAD))
    begin
        if(I_PHY_RXDV)
        begin
            if(R_UDP_HEAD_CNT == 3'd4)// 接收UDP数据字节数 高字节
            begin
                R_UDP_DATA_BYTE_NUM[15:8] <= I_PHY_RXD;
                R_UDP_HEAD_CNT <= R_UDP_HEAD_CNT + 1;
            end
            else if(R_UDP_HEAD_CNT == 3'd5)// 接收UDP数据字节数 低字节
            begin
                R_UDP_DATA_BYTE_NUM[7:0] <= I_PHY_RXD;
                R_UDP_HEAD_CNT <= R_UDP_HEAD_CNT + 1;
            end
            else if(&R_UDP_HEAD_CNT)// UDP首部接收结束
            begin
                R_UDP_DATA_BYTE_NUM <= R_UDP_DATA_BYTE_NUM - 16'd8;
                R_UDP_HEAD_RCV_DONE <= 1'b1;
                R_UDP_HEAD_CNT <= 3'd0;
            end
            else
            begin
                R_UDP_HEAD_CNT <= R_UDP_HEAD_CNT + 1;
            end
        end
    end
    else
    begin
        R_UDP_HEAD_RCV_DONE <= 1'b0;
        R_UDP_HEAD_CNT      <= 3'd0;
        R_UDP_DATA_BYTE_NUM <= R_UDP_DATA_BYTE_NUM;
    end
end

// 接收UDP用户数据
always @ (posedge I_PHY_RX_CLK)
begin
    if(~W_RX_MDL_RSTN)
    begin
        O_ETH_USR_DATA_VAL <= 1'b0;
        O_ETH_USR_DATA     <= 8'd0;
        R_UDP_DATA_RCV_DONE<= 1'b0;
        R_UDP_DATA_CNT     <= 16'd0;
    end
    else if(|(R_NS & LP_ST_RCV_DATA))
    begin
        if(I_PHY_RXDV)
        begin
            if(R_UDP_DATA_CNT == (R_UDP_DATA_BYTE_NUM-1))
            begin
                O_ETH_USR_DATA_VAL <= 1'b1;
                O_ETH_USR_DATA     <= I_PHY_RXD;
                R_UDP_DATA_RCV_DONE<= 1'b1;
                R_UDP_DATA_CNT     <= 16'd0;
            end
            else
            begin
                O_ETH_USR_DATA_VAL <= 1'b1;
                O_ETH_USR_DATA     <= I_PHY_RXD;
                R_UDP_DATA_RCV_DONE<= 1'b0;
                R_UDP_DATA_CNT     <= R_UDP_DATA_CNT + 1;                
            end
        end
    end
    else
    begin
        O_ETH_USR_DATA_VAL <= 1'b0;
        O_ETH_USR_DATA     <= 8'd0;
        R_UDP_DATA_RCV_DONE<= 1'b0;
        R_UDP_DATA_CNT     <= 16'd0;        
    end
end
// | ====================================   模块内部模块例化   ====================================
RESET_SYNC_MDL #(
        .P_INPUT_RESET_ACTIVE_LEVEL (1'b0),
        .P_OUTPUT_RESET_ACTIVE_LEVEL(1'b0),
        .P_SYNC_DEPTH                 (32'd2)
    ) INST_RESET_SYNC_MDL (
        .I_SYNC_CLK (I_PHY_RX_CLK),
        .I_RESET    (I_SYS_RSTN),
        .O_RESET    (W_RX_MDL_RSTN)
    );

endmodule

TEST BENCH

仿真了5种情形:
1、正确传输(2次);
2、传输过程中,接收错误信号拉高;
3、目的MAC地址出错;
4、目的IP地址出错;

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------    UDP 数据接收模块仿真   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-01-12
// | 完成时间 : 2022-01-12
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 正确传输(2次);
// |			-2- 传输过程中,接收错误信号拉高;
// | 			-3- 目的MAC地址出错;
// |			-4- 目的IP地址出错;
// |
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:


`timescale 1ns / 1ps

module TB_UDP_RX_MDL();
// | ====================================  模块可配置参数声明  ==================================== 
parameter 			P_FPGA_MAC_ADDR				=				48'h11_11_11_11_11_11;  	  // FPGA侧 MAC地址
parameter 			P_FPGA_IP_ADDR				=				{8'd192,8'd168,8'd137,8'd3};  // FPGA侧 IP地址

// | ==================================== 模块输入输出端口声明 ====================================
// 系统复位 
reg 															I_SYS_RSTN;
// PHY芯片接口
reg 															I_PHY_RX_CLK;
reg 															I_PHY_RXDV;
reg 				[7:0]										I_PHY_RXD;
reg 															I_PHY_RXER;
// 用户数据
wire															O_ETH_USR_DATA_VAL;
wire				[7:0]										O_ETH_USR_DATA;

// | ====================================     产生仿真激励     ====================================
initial I_PHY_RX_CLK  =  1'b0;
always #4 I_PHY_RX_CLK = ~I_PHY_RX_CLK;

initial
begin
	I_SYS_RSTN = 1'b0;
	I_PHY_RXDV = 1'b0;
	I_PHY_RXD  = 8'd0;
	I_PHY_RXER = 1'b0;
	#134;
	I_SYS_RSTN = 1'b1;
	#123;
	repeat(2)
	begin
		// 前导码
		repeat(7)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'h55;
		end
		// SFD
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'hd5;
		// MAC地址
		TASK_SEND_MAC_ADDR(P_FPGA_MAC_ADDR);
		TASK_SEND_MAC_ADDR(48'h01_11_10_11_11_11);
		// 协议类型
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h08;
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h00;
		// IP首部
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'b0100_0101;

		repeat(15)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		TASK_SEND_IP_ADDR(P_FPGA_IP_ADDR);
		// UDP首部
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd0;	
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd108;	
		repeat(2)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		// UDP数据
		repeat(100)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= I_PHY_RXD + 1;
		end
		// FCS
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b0;
		I_PHY_RXD  <= 8'd0;	
		#1250;
	end
	#100;

	repeat(1)
	begin
		// 前导码
		repeat(7)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'h55;
		end
		// SFD
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'hd5;
		// MAC地址
		TASK_SEND_MAC_ADDR(P_FPGA_MAC_ADDR);
		TASK_SEND_MAC_ADDR(48'h01_11_10_11_11_11);
		// 协议类型
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h08;
		I_PHY_RXER <= 1'b1;//接收错误
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h00;
		// IP首部
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'b0100_0101;
		I_PHY_RXER <= 1'b0;

		repeat(15)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		TASK_SEND_IP_ADDR(P_FPGA_IP_ADDR);
		// UDP首部
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd0;	
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd108;	
		repeat(2)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		// UDP数据
		repeat(100)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= I_PHY_RXD + 1;
		end
		// FCS
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b0;
		I_PHY_RXD  <= 8'd0;	
		#1250;
	end
	#100;

	repeat(1)
	begin
		// 前导码
		repeat(7)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'h55;
		end
		// SFD
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'hd5;
		// MAC地址
		TASK_SEND_MAC_ADDR(48'h01_31_10_11_11_11);//MAC地址出错
		TASK_SEND_MAC_ADDR(48'h01_11_10_11_11_11);
		// 协议类型
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h08;
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h00;
		// IP首部
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'b0100_0101;

		repeat(15)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		TASK_SEND_IP_ADDR(P_FPGA_IP_ADDR);
		// UDP首部
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd0;	
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd108;	
		repeat(2)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		// UDP数据
		repeat(100)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= I_PHY_RXD + 1;
		end
		// FCS
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b0;
		I_PHY_RXD  <= 8'd0;	
		#1250;
	end
	#100;

	repeat(1)
	begin
		// 前导码
		repeat(7)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'h55;
		end
		// SFD
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'hd5;
		// MAC地址
		TASK_SEND_MAC_ADDR(P_FPGA_MAC_ADDR);
		TASK_SEND_MAC_ADDR(48'h01_11_10_11_11_11);
		// 协议类型
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h08;
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'h00;
		// IP首部
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'b0100_0101;

		repeat(15)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		TASK_SEND_IP_ADDR(P_FPGA_IP_ADDR + 32'd2);//IP地址出错
		// UDP首部
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd0;	
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= 8'd108;	
		repeat(2)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		// UDP数据
		repeat(100)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= I_PHY_RXD + 1;
		end
		// FCS
		repeat(4)
		begin
			@(posedge I_PHY_RX_CLK)
			I_PHY_RXDV <= 1'b1;
			I_PHY_RXD  <= 8'd0;
		end
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b0;
		I_PHY_RXD  <= 8'd0;	
		#1250;
	end
	#100;
	$finish;
end
// | ====================================     被测模块例化     ====================================
UDP_RX_MDL #(
		.P_FPGA_MAC_ADDR(P_FPGA_MAC_ADDR),
		.P_FPGA_IP_ADDR (P_FPGA_IP_ADDR)
	) INST_UDP_RX_MDL (
		.I_SYS_RSTN         (I_SYS_RSTN),
		.I_PHY_RX_CLK       (I_PHY_RX_CLK),
		.I_PHY_RXDV         (I_PHY_RXDV),
		.I_PHY_RXD          (I_PHY_RXD),
		.I_PHY_RXER 		(I_PHY_RXER),
		.O_ETH_USR_DATA_VAL (O_ETH_USR_DATA_VAL),
		.O_ETH_USR_DATA     (O_ETH_USR_DATA)
	);
// | ====================================     仿真任务声明     ====================================
task TASK_SEND_MAC_ADDR;
	input [47:0] MAC_ADDR;

	begin
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[47:40];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[39:32];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[31:24];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[23:16];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[15:8];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= MAC_ADDR[7:0];
	end
endtask

task TASK_SEND_IP_ADDR;
	input [31:0] IP_ADDR;

	begin
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXD  <= IP_ADDR[31:24];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= IP_ADDR[23:16];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= IP_ADDR[15:8];
		@(posedge I_PHY_RX_CLK)
		I_PHY_RXDV <= 1'b1;
		I_PHY_RXD  <= IP_ADDR[7:0];
	end
endtask
endmodule

仿真结果

此过程可自行仿真查看。


有关基于FPGA的UDP 通信(三)的更多相关文章

  1. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  2. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  5. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  6. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  7. ruby - 在 Rakefile 中动态生成 Rake 测试任务(基于现有的测试文件) - 2

    我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n

  8. ruby - 如何使用 Ruby 基于字母数字字符串生成颜色? - 2

    我想要像“嘿那里”这样的东西变成,例如,#316583。我希望将任意长度的字符串“归结”为十六进制颜色。我不知道从哪里开始。我在想,每个字符串的MD5散列都是不同的-但如何将该散列转换为十六进制颜色数字? 最佳答案 你可以只取几位前几位:require'digest/md5'color=Digest::MD5.hexdigest('Mytext')[0..5] 关于ruby-如何使用Ruby基于字母数字字符串生成颜色?,我们在StackOverflow上找到一个类似的问题:

  9. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  10. ruby-on-rails - Rails 是否支持监听 UDP 套接字的简洁方式? - 2

    在Rails中,什么是集成更新模型某些元素的UDP监听过程的最佳方式(特别是它将向其中一个表添加行)。简单的答案似乎是在同一个进程中使用UDP套接字对象启动一个线程,但我什至不清楚我应该在哪里做适合Rails方式的事情。有没有一种巧妙的方法来开始收听UDP?具体来说,我希望能够编写一个UDPController并在每个数据报消息上调用一个特定的方法。理想情况下,我希望避免在UDP上使用HTTP(因为它会浪费一些在这种情况下非常宝贵的空间),但我完全控制消息格式,因此我可以为Rails提供它需要的任何信息。 最佳答案 Rails是一个

随机推荐