目录
引言
设计框图
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
仿真结果
此过程可自行仿真查看。