FPGA设计千兆以太网MAC(3)——数据缓存及位宽转换模块设计与验证

  本文设计思想采用明德扬至简设计法。上一篇博文中定制了自定义MAC IP的结构,在用户侧需要位宽转换及数据缓存。本文以TX方向为例,设计并验证发送缓存模块。这里定义该模块可缓存4个最大长度数据包,用户根据需求改动即可。

  该模块核心是利用异步FIFO进行跨时钟域处理,位宽转换由VerilogHDL实现。需要注意的是用户数据包位宽32bit,因此包尾可能有无效字节,而转换为8bit位宽数据帧后是要丢弃无效字节的。内部逻辑非常简单,直接上代码:

  1 `timescale 1ns / 1ps
  2
  3 // Description: MAC IP TX方向用户数据缓存及位宽转换模块
  4 // 整体功能:将TX方向用户32bit位宽的数据包转换成8bit位宽数据包
  5 //用户侧时钟100MHZ,MAC侧125MHZ
  6 //缓存深度:保证能缓存4个最长数据包,TX方向用户数据包包括
  7 //目的MAC地址  源MAC地址 类型/长度 数据 最长1514byte
  8
  9
 10 module tx_buffer#(parameter DATA_W = 32)//位宽不能改动
 11 (
 12
 13     //全局信号
 14     input                         rst_n,//保证拉低三个时钟周期,否则FIF可能不会正确复位
 15
 16     //用户侧信号
 17     input                         user_clk,
 18     input         [DATA_W-1:0]     din,
 19     input                         din_vld,
 20     input                         din_sop,
 21     input                         din_eop,
 22     input         [2-1:0]         din_mod,
 23     output                         rdy,
 24
 25     //MAC侧信号
 26     input                         eth_tx_clk,
 27     output reg     [8-1:0]         dout,
 28     output reg                     dout_sop,
 29     output reg                     dout_eop,
 30     output reg                     dout_vld
 31     );
 32
 33
 34     reg wr_en = 0;
 35     reg [DATA_W+4-1:0] fifo_din = 0;
 36     reg [ (2-1):0]  rd_cnt = 0     ;
 37     wire        add_rd_cnt ;
 38     wire        end_rd_cnt ;
 39     wire rd_en;
 40     wire [DATA_W+4-1:0] fifo_dout;
 41     wire rst;
 42     reg [ (2-1):0]  rst_cnt =0    ;
 43     wire        add_rst_cnt ;
 44     wire        end_rst_cnt ;
 45     reg rst_flag = 0;
 46     wire [11 : 0] wr_data_count;
 47     wire empty;
 48     wire full;
 49
 50 /****************************************写侧*************************************************/
 51 always  @(posedge user_clk or negedge rst_n)begin
 52     if(rst_n==1‘b0)begin
 53         wr_en <= 0;
 54     end
 55     else if(rdy)
 56         wr_en <= din_vld;
 57 end
 58
 59 always  @(posedge user_clk or negedge rst_n)begin
 60     if(rst_n==1‘b0)begin
 61         fifo_din <= 0;
 62     end
 63     else begin//[35] din_sop    [34] din_eop    [33:32] din_mod    [31:0] din
 64         fifo_din <= {din_sop,din_eop,din_mod,din};
 65     end
 66 end
 67
 68 assign rdy = wr_data_count <= 1516 && !rst && !rst_flag && !full;
 69
 70 /****************************************读侧*************************************************/
 71
 72 always @(posedge eth_tx_clk or negedge rst_n) begin
 73     if (rst_n==0) begin
 74         rd_cnt <= 0;
 75     end
 76     else if(add_rd_cnt) begin
 77         if(end_rd_cnt)
 78             rd_cnt <= 0;
 79         else
 80             rd_cnt <= rd_cnt+1 ;
 81    end
 82 end
 83 assign add_rd_cnt = (!empty);
 84 assign end_rd_cnt = add_rd_cnt  && rd_cnt == (4)-1 ;
 85
 86 assign rd_en = end_rd_cnt;
 87
 88 always  @(posedge eth_tx_clk or negedge rst_n)begin
 89     if(rst_n==1‘b0)begin
 90         dout <= 0;
 91     end
 92     else if(add_rd_cnt)begin
 93         dout <= fifo_dout[DATA_W-1-rd_cnt*8 -:8];
 94     end
 95 end
 96
 97 always  @(posedge eth_tx_clk or negedge rst_n)begin
 98     if(rst_n==1‘b0)begin
 99         dout_vld <= 0;
100     end
101     else if(add_rd_cnt && ((rd_cnt <= 3 - fifo_dout[33:32] && fifo_dout[34]) || !fifo_dout[34]))begin
102         dout_vld <= 1;
103     end
104     else
105         dout_vld <= 0;
106 end
107
108 always  @(posedge eth_tx_clk or negedge rst_n)begin
109     if(rst_n==1‘b0)begin
110         dout_sop <= 0;
111     end
112     else if(add_rd_cnt && rd_cnt == 0 && fifo_dout[35])begin
113         dout_sop <= 1;
114     end
115     else
116         dout_sop <= 0 ;
117 end
118
119 always  @(posedge eth_tx_clk or negedge rst_n)begin
120     if(rst_n==1‘b0)begin
121         dout_eop <= 0;
122     end
123     else if(add_rd_cnt && rd_cnt == 3 - fifo_dout[33:32] && fifo_dout[34])begin
124         dout_eop <= 1;
125     end
126     else
127         dout_eop <= 0;
128 end
129
130
131 /******************************FIFO复位逻辑****************************************/
132 assign rst = !rst_n || rst_flag;
133
134 always  @(posedge user_clk or negedge rst_n)begin
135     if(!rst_n)begin
136         rst_flag <= 1;
137     end
138     else if(end_rst_cnt)
139         rst_flag <= 0;
140 end
141
142 always @(posedge user_clk or negedge rst_n) begin
143     if (rst_n==0) begin
144         rst_cnt <= 0;
145     end
146     else if(add_rst_cnt) begin
147         if(end_rst_cnt)
148             rst_cnt <= 0;
149         else
150             rst_cnt <= rst_cnt+1 ;
151    end
152 end
153 assign add_rst_cnt = (rst_flag);
154 assign end_rst_cnt = add_rst_cnt  && rst_cnt == (3)-1 ;
155
156
157
158     //FIFO位宽32bit 一帧数据最长1514byte,即379个16bit数据
159     //FIFO深度:379*4 = 1516  需要2048
160     //异步FIFO例化
161     fifo_generator_0 fifo (
162   .rst(rst),        // input wire rst
163   .wr_clk(user_clk),  // input wire wr_clk   100MHZ
164   .rd_clk(eth_tx_clk),  // input wire rd_clk  125MHZ
165   .din(fifo_din),        // input wire [33 : 0] din
166   .wr_en(wr_en),    // input wire wr_en
167   .rd_en(rd_en),    // input wire rd_en
168   .dout(fifo_dout),      // output wire [33 : 0] dout
169   .full(full),      // output wire full
170   .empty(empty),    // output wire empty
171   .wr_data_count(wr_data_count)  // output wire [11 : 0] wr_data_count
172 );
173
174 endmodule

tx_buffer

  接下来是验证部分,也就是本文的重点。以下的testbench包含了最基本的测试思想:发送测试激励给UUT,将UUT输出与黄金参考值进行比较,通过记分牌输出比较结果。

  1 `timescale 1ns / 1ps
  2
  3 module tx_buffer_tb( );
  4
  5 parameter USER_CLK_CYC = 10,
  6           ETH_CLK_CYC = 8,
  7           RST_TIM = 3;
  8
  9 parameter SIM_TIM = 10_000;
 10
 11 reg user_clk;
 12 reg rst_n;
 13 reg [32-1:0] din;
 14 reg din_vld,din_sop,din_eop;
 15 reg [2-1:0] din_mod;
 16 wire rdy;
 17 reg eth_tx_clk;
 18 wire [8-1:0] dout;
 19 wire dout_sop,dout_eop,dout_vld;
 20 reg [8-1:0] dout_buf [0:1024-1];
 21 reg [16-1:0] len [0:100-1];
 22 reg [2-1:0] mod [0:100-1];
 23 reg err_flag = 0;
 24
 25 tx_buffer#(.DATA_W(32))//位宽不能改动
 26 dut
 27 (
 28
 29     //全局信号
 30    .rst_n      (rst_n) ,//保证拉低三个时钟周期,否则FIF可能不会正确复位
 31    .user_clk   (user_clk) ,
 32    .din        (din) ,
 33    .din_vld    (din_vld) ,
 34    .din_sop    (din_sop) ,
 35    .din_eop    (din_eop) ,
 36    .din_mod    (din_mod) ,
 37    .rdy        (rdy) ,
 38    .eth_tx_clk (eth_tx_clk) ,
 39    .dout       (dout) ,
 40    .dout_sop   (dout_sop) ,
 41    .dout_eop   (dout_eop) ,
 42    .dout_vld   (dout_vld)
 43     );
 44
 45 /***********************************时钟******************************************/
 46     initial begin
 47         user_clk = 1;
 48         forever #(USER_CLK_CYC/2) user_clk = ~user_clk;
 49     end
 50
 51     initial begin
 52         eth_tx_clk = 1;
 53         forever #(ETH_CLK_CYC/2) eth_tx_clk = ~eth_tx_clk;
 54     end
 55 /***********************************复位逻辑******************************************/
 56     initial begin
 57         rst_n = 1;
 58         #1;
 59         rst_n = 0;
 60         #(RST_TIM*USER_CLK_CYC);
 61         rst_n = 1;
 62     end
 63
 64 /***********************************输入激励******************************************/
 65 integer gen_time = 0;
 66     initial begin
 67         #1;
 68         packet_initial;
 69         #(RST_TIM*USER_CLK_CYC);
 70         packet_gen(20,2);
 71         #(USER_CLK_CYC*10);
 72         packet_gen(30,1);
 73     end
 74
 75 /***********************************输出缓存与检测******************************************/
 76 integer j = 0;
 77 integer chk_time = 0;
 78     initial begin
 79         forever begin
 80             @(posedge eth_tx_clk)
 81             if(dout_vld)begin
 82                 if(dout_sop)begin
 83                     dout_buf[0] = dout;
 84                     j = 1;
 85                 end
 86                 else if(dout_eop)begin
 87                     dout_buf[j] = dout;
 88                     j = j+1;
 89                     packet_check;
 90                 end
 91                 else begin
 92                     dout_buf[j] = dout;
 93                     j = j+1;
 94                 end
 95             end
 96         end
 97     end
 98
 99 /***********************************score board******************************************/
100 integer fid;
101     initial begin
102         fid = $fopen("test.txt");
103         $fdisplay(fid,"                 Start testing                      \n");
104         #SIM_TIM;
105         if(err_flag)
106             $fdisplay(fid,"Check is failed\n");
107         else
108             $fdisplay(fid,"Check is successful\n");
109         $fdisplay(fid,"                 Testing is finished                \n");
110         $fclose(fid);
111         $stop;
112     end
113
114 /***********************************子任务******************************************/
115 //包生成子任务
116     task packet_gen;
117         input [16-1:0] length;
118         input [2-1:0] invalid_byte;
119         integer i;
120         begin
121             len[gen_time] = length;
122             mod[gen_time] = invalid_byte;
123
124             for(i = 1;i<=length;i=i+1)begin
125                 if(rdy == 1)begin
126                     din_vld = 1;
127                     if(i==1)
128                         din_sop = 1;
129                     else if(i == length)begin
130                         din_eop = 1;
131                         din_mod = invalid_byte;
132                     end
133                     else begin
134                         din_sop = 0;
135                         din_eop = 0;
136                         din_mod = 0;
137                     end
138                     din = i ;
139                 end
140
141                 else begin
142                     din_sop = din_sop;
143                     din_eop = din_eop;
144                     din_vld = 0;
145                     din_mod = din_mod;
146                     din = din;
147                     i = i - 1;
148                 end
149
150                 #(USER_CLK_CYC*1);
151             end
152             packet_initial;
153             gen_time = gen_time + 1;
154         end
155     endtask
156
157     task packet_initial;
158         begin
159             din_sop = 0;
160             din_eop = 0;
161             din_vld = 0;
162             din = 0;
163             din_mod = 0;
164         end
165     endtask
166
167 //包检测子任务
168     task packet_check;
169         integer k;
170         integer num,packet_len;
171         begin
172             num = 1;
173             $fdisplay(fid,"%dth:Packet checking...\n",chk_time);
174             packet_len = 4*len[chk_time]-mod[chk_time];
175             if(j != packet_len)begin
176                 $fdisplay(fid,"Length of the packet is wrong.\n");
177                 err_flag = 1;
178                 disable packet_check;
179             end
180
181             for(k=0;k<packet_len;k=k+1)begin
182                 if(k%4 == 3)begin
183                     if(dout_buf[k] != num)begin
184                         $fdisplay(fid,"Data of the packet is wrong!\n");
185                         err_flag = 1;
186                     end
187                     num = num+1;
188                 end
189                 else if(dout_buf[k] != 0)begin
190                     $fdisplay(fid,"Data of the packet is wrong,it should be zero!\n");
191                     err_flag = 1;
192                 end
193             end
194             chk_time = chk_time + 1;
195         end
196     endtask
197
198 endmodule

tx_buffer_tb

  可见主要是task编写及文件读写操作帮了大忙,如果都用眼睛看波形来验证设计正确性,真的是要搞到眼瞎。为保证测试完备性,测试包生成task可通过输入接口产生不同长度和无效字节数的递增数据包。testbench中每检测到输出包尾指示信号eop即调用packet_check task对数值进行检测。本文的testbench结构较具通用性,可以用来验证任意对数据包进行处理的逻辑单元。

  之前Modelsim独立仿真带有IP核的Vivado工程时经常报错,只好使用Vivado自带的仿真工具。一直很头痛这个问题,这次终于有了进展!首先按照常规流程使用Vivado调用Modelsim进行行为仿真,启动后会在工程目录下产生些有用的文件,帮助我们脱离Vivado进行独立仿真。

  在新建Modelsim工程时,在红框内选择Vivado工程中<project>.sim -> sim_1 -> behav下的modelsim.ini文件。之后添加文件包括:待测试设计文件、testbench以及IP核可综合文件。第三个文件在<project>.srcs -> sources_1 -> ip -> <ip_name> -> synth下。

  现在可以顺利启动仿真了。我们来看下仿真结果:

  文件中信息打印情况:

  从波形和打印信息的结果来看,基本可以证明数据缓存及位宽转换模块逻辑功能无误。为充分验证要进一步给出覆盖率较高的测试数据集,后期通过编写do文件批量仿真实现。在FPGA或IC设计中,验证占据大半开发周期,可见VerilogHDL的非综合子集也是至关重要的,今后会多总结高效的验证方法!

原文地址:https://www.cnblogs.com/moluoqishi/p/9751652.html

时间: 2024-07-31 08:29:17

FPGA设计千兆以太网MAC(3)——数据缓存及位宽转换模块设计与验证的相关文章

FPGA设计千兆以太网MAC(2)——以太网协议及设计规划

上篇该系列博文中通过MDIO接口实现了PHY芯片的状态检测,验证其已处于1000M 全双工工作模式.在设计MAC逻辑之前,要先清楚MAC与PHY之间的接口以及以太网协议细节,这样才能保证网络的兼容性.本文内容多来自Xilinx官方文档pg051 tri-mode-eth-mac. 1.GMII接口 此处使用较简单的GMII接口,接口列表及说明如下: TX方向时钟tx_mac_aclk由FPGA给出,RX方向时钟gmii_rx_clk由PHY芯片经过内部CDR模块从接收数据中恢复出来,时钟频率均为

【小梅哥FPGA进阶学习之旅】基于Altera FPGA 的DDR2+千兆以太网电路设计

DDR2电路设计 在高速大数据的应用中,高速大容量缓存是必不可少的硬件.当前在FPGA系统中使用较为广泛的高速大容量存储器有经典速度较低的单数据速率的SDRAM存储器,以及速度较高的双速率DDR.DDR2.DDR3型SDRAM存储器,DDR系列的存储器都需要FPGA芯片有对应的硬件电路结构支持.对于Altera Cyclone IV系列的FPGA,其最高支持到DDR2存储器(不支持DDR3存储器,到了Cyclone V系列的FPGA才支持DDR3存储器).芯航线AC6102开发板作为一个高速应用

AC6102 开发板千兆以太网UDP传输实验

AC6102 开发板千兆以太网UDP传输实验 在芯航线AC6102开发板上,设计了一路GMII接口的千兆以太网电路,通过该以太网电路,用户可以将FPGA采集或运算得到的数据传递给其他设备如PC或服务器,或者接收其他设备传输过来的数据并进行处理. 接触过以太网的用户,应该最常听说的是TCP/IP协议,确实,在PC端或者嵌入式系统中,TCP/IP协议应用非常广泛,因此,当大家看到FPGA上带有以太网接口时,可能第一个想到的也是实现TC/IP协议.这里,首先可以很肯定的告诉大家,使用FPGA实现TCP

关于千兆以太网芯片及VLAN浅析

MARVEL出产的高端千兆以太网交换芯片,对每个端口支持不同的交换模式. 包括4种模式: Secure模式:所带VLAN tag必须存在于VTU表中,且入端口必须是该VLAN成员,否则丢弃报文. Check模式:所带VLAN tag必须存在于VTU表中,否则丢弃报文. Fallback模式:入端口报文不丢弃. 802.1Q Disabled:802.1Q关闭,使用端口VLAN模式,所有报文透传. 前3种模式都遵循802.1Q规则,报文进入后按照VLAN表项进行转发,不同就在于进入的时候条件限制,

千兆以太网规范

千兆以太网规范 因为千兆以太网技术仍是目前一种最主流应用的以太网技术,所以关于这种以太网的规范还在不断推出,以满足不同应用环境需求,改进技术性能. 最 早在1998和1999年发布的IEEE 802.3z和IEEE 802.3ab标准中就包括1000Base-LX.1000Base-SX.1000Base-CX和1000Base-T(前三种统称为 1000Base-X子系列),如图5-12所示.其中前三个是由IEEE 802.3z标准规定的,而1000Base-T标准则是由IEEE 802.3a

迅为4418/6818开发板cortex-A9四核千兆以太网 4G模块

6818核心板与4418核心板兼容同一底板 核心板:经过大批量检验的核心板连接方式,更优的排列,更放心的连接性能. 六层PCB沉金设计,紧凑精致,解决电磁兼容,达到成本与性能的完美统一,更加技高一筹.核心板通过设计阶段的电磁兼容测试,高低温环境适应实验等. 6818开发板(基本型): 6818开发板(豪华型): 支持选配模块: RFID模块,VGA模块,CAN/RS485模块,继电器模块,500万摄像头,USB摄像头,USB转串转接头等. 应用行业: 规格参数:   核心板 尺寸:50mm*60

[转帖]树莓派 4 正式发布!硬件性能大提升:CPU提升3倍,支持USB3.0、蓝牙5.0、千兆以太网、4G LPDDR4、H.265

树莓派 4 正式发布!硬件性能大提升:CPU提升3倍,支持USB3.0.蓝牙5.0.千兆以太网.4G LPDDR4.H.265 http://www.itpub.net/2019/06/28/2308/ 其实应该拿来试试的 树莓派(Raspberry Pi)基金会,6月24日正式发布了Raspberry Pi 4 Model B. 树莓派是全球知名的基本计算微型电脑,深受全球开发者.编程者.极客等人士的追捧和喜爱. 这一代Raspberry Pi 4 Model B开发了3年的时间,内存(RAM

10GE---超长距离的万兆以太网

万兆以太网 锁定 本词条由“科普中国”百科科学词条编写与应用工作项目 审核 . 以太网标准是一个古老而又充满活力的标准.自从1982年以太网协议被IEEE采纳成为标准以后,已经历了20年的风风雨雨.在这20年中,以太网技术作为局域网链路层标准战胜了令牌总线.令牌环等技术,成为局域网事实标准.以太网技术当前在局域网范围市场占有率超过90%. 中文名 万兆以太网 太网标准 是一个古老而又充满活力的标准 太网技术 作为局域标准战胜了令牌总线 经历了20年 的风风雨雨 目录 1 简介 2 成立背景 3 

基于嵌入式Linux的千兆以太网卡驱动程序设计及测试

一. 引言 千兆以太网是一种具有高带宽和高响应的新网络技术,相关协议遵循IEEE 802.3规范标准.采用和10M以太网相似的帧格式.网络协议和布线系统,基于光纤和短距离同轴电缆的物理层介质,更适用于交换机.服务器等数据吞吐率大的设备.本文设计实现一种基于嵌入式Linux千兆以太网卡的驱动程序,并完成后续的测试工作和代码移植. 千兆以太网网卡工作在OSI网络架构的物理层和数据链路层,其中物理层由PHY芯片管理,数据链路层由千兆以太网控制器(GMAC)管理.硬件构架上,GMAC控制器由核心层.MT