也就是说MIG用户侧时钟为100MHz,提供给IP核正确的钟表和复位条件

  本文设计思路源自明德扬至简设计法。在事先的几篇博文中,由于设计相比简单,所有的机能都是用verilogHDL代码编写实现的。我们要学会站在巨人的肩膀上,这时候就该IP核登场了!

  本文设计思路源自明德扬至简设计法。在事先的几篇博文中,由于设计相比简单,所有的功力都是用verilogHDL代码编写实现的。我们要学会站在巨人的肩头上,这时候就该IP核登场了!

  本文设计思想采取明德扬至简设计法。在飞速信号处理场馆下,很长时间内就要缓存大量的数据,这时片内存储资源已经远远不够了。DDR
SDRAM因其极高的性价比几乎是每一款中高档FPGA开发板的首选外部存储芯片。DDR操作时序卓殊复杂,之所以在FPGA开发中用途如此广泛,都要得意于MIG
IP核。网上有关MIG控制DDR的材料很多,因而本文只讲述个人认为较首要的内容。由于MIG
IP核用户接口时序较复杂,这里给出扩大接口模块用于进一步简化接口时序。

  说白了,IP核就是别人做好了的硬件模块,提供完整的用户接口和表明文档,更扑朔迷离的还有示例工程,你假设能用好这么些IP核,设计已经成功一半了。说起来容易,从冗长的英文文档和网上各种非标准教程中汲取所需,并灵活运用依然需要下一番功力的。

  说白了,IP核就是旁人做好了的硬件模块,提供整机的用户接口和表达文档,更扑朔迷离的还有示例工程,你倘使能用好那些IP核,设计已经形成一半了。说起来容易,从冗长的英文文档和网上各个非标准教程中汲取所需,并灵活运用依旧需要下一番功力的。

  我们从IP核配置起首说起。Controller
Options这页最为重大,其中包括时钟策略和表面DDR芯片参数配置。首先时钟周期采取为400MHz,此时PHY
to Controller Clock
Ratio只可以是4:1,也就是说MIG用户侧时钟为100MHz。下半部分是选项十分的DDR芯片型号和参数,要再三肯定无误。

  我觉得其中最重大的几点如下:

  我觉得其中最着重的几点如下:

公海赌船网址 1

  1)
提供给IP核正确的钟表和复位条件;

  1)
提供给IP核正确的钟表和复位条件;

   Memory
Options这页输入时钟周期选用为200MHz,遵照Controller
Options页的选项,该时钟经过PLL分频和倍频后的钟表分别作为用户侧时钟100MHz和DDR接口时钟100MHz。

  2) 明确各种显要用户接口效能;

  2) 明确各种首要用户接口效率;

公海赌船网址 2

  3) 明白所需指令的操作时序;

  3) 了然所需指令的操作时序;

   这里有个参考时钟选项,假如Memory
Options页PLL输入时钟频率选为200MHz,此处可以直接拔取Use System
Clock,从而简化接口。

  4)
知道里面寄存器地址及意义和部署方式、顺序;

  4)
知道其中寄存器地址及效率和配置模式、顺序;

公海赌船网址 3

  5)
会从官方示例工程中学会IP核正确运用方法;

  5)
会从官方示例工程中学会IP核正确拔取形式;

   以上是MIG
IP核配置过程中相比较首要的片段,实际上上述配置也可经过改动工程代码中参数来重定义。IP核配置完成,打开example
design工程顶层文件,我们来第一关注下用户侧接口效率和时序。

  明日来讲讲一个最常用的IP核,FIFO。可以说它是FPGA能这样灵活处理数据的根底,常用来异步时钟域处理、位宽转换以及需要多少缓存的场子。先来表明下,对于初学者和刚接触一个IP核的人的话,不要过度关注IP核的每一个参数和效能,更没必要了然其中的实际社团和行事原理(还没忘在此之前使用的ILA吧,反正自己是不知情具体怎么规划出来的)只需控制最常用的和最重大的,把IP核用起来就大功告成了。先从生成IP核初始吧:

  前些天来讲讲一个最常用的IP核,FIFO。可以说它是FPGA能这样灵活处理数据的根基,常用来异步时钟域处理、位宽转换以及需要多少缓存的场所。先来表明下,对于初学者和刚接触一个IP核的人的话,不要过度关注IP核的每一个参数和效应,更没必要通晓其中的切实社团和行事原理(还没忘在此之前运用的ILA吧,反正自己是不通晓具体怎么设计出来的)只需精通最常用的和最关键的,把IP核用起来就大功告成了。先从生成IP核先导吧:

公海赌船网址 4

公海赌船网址 5

公海赌船网址 6

  这是自身写的注释,更实际清晰的表明或者要查看官方文档UG586.接下来看看写多少和读数据的接口时序图(时钟比例4:1,burst
length = 8为例):

  配置向导中首先页中是选项FIFO的接口形式和落实格局。这里大家用原来的接口格局。箭头处是促成模式,如若急需异步时钟域处理选取读写独立刻钟格局。

  配置向导中第一页中是采取FIFO的接口情势和兑现情势。这里我们用原始的接口形式。箭头处是贯彻形式,倘使需要异步时钟域处理接纳读写独立刻钟形式。

  指令通道:

公海赌船网址 7

公海赌船网址 8

公海赌船网址 9

  第二页中需要特别强调的是读形式的选用。其实这里的First
Word Fall Through对应的就是Altera FPGA中FIFO IP核读格局中的Show
ahead形式嘛,换个名字而已。这多少个读情势的特色是在读使能有效从前,即把FIFO中首个数据从读数据端口不断送出。在这种形式下,读使能信号倒像是“读清”信号,把上三次的数量清除掉,让FIFO送出下一个数额。这样做的处是吻合dout
和dout_vld相配合的出口信号模式。

  第二页中需要特别强调的是读形式的采取。其实这里的First
Word Fall Through对应的就是Altera FPGA中FIFO IP核读情势中的Show
ahead形式嘛,换个名字而已。这些读格局的特征是在读使能立竿见影此前,即把FIFO中第一个数据从读数据端口不断送出。在这种格局下,读使能信号倒像是“读清”信号,把上五回的数额清除掉,让FIFO送出下一个数额。这样做的处是相符dout
和dout_vld相匹配的出口信号格局。

  写数据:

公海赌船网址 10

公海赌船网址 11

公海赌船网址 12

  第三页是布局部分可选的标志位,可以按照需要灵活实现部分标志位和抓手特性(我是平昔没用过)。

  第三页是布置部分可选的标志位,能够遵照需要灵活实现部分标志位和抓手特性(我是从来没用过)。

 

公海赌船网址 13

公海赌船网址 14

  从时序图可以观望,指令地址和数量利用两套时序,相互相互独立。为了有利于设计,直接将两套时序严刻对齐(境况1)也可以正常办事。

  第四页可选FIFO内缓存数据量计数器,由于自身起首选拔的是异步FIFO格局,所以此处有五个计数器分别与读侧和写侧时钟上升沿同步。注意一点:那五个计数器均代表FIFO缓存数据量,只然而在时钟上多少错误,切不可错误了解为是写入了依旧读出了不怎么个数据。

  第四页可选FIFO内缓存数据量计数器,由于自家起来拔取的是异步FIFO格局,所以此处有六个计数器分别与读侧和写侧时钟上升沿同步。注意一点:这两个计数器均表示FIFO缓存数据量,只但是在时钟上有些错误,切不可错误精通为是写入了如故读出了不怎么个数据。

  读数据:

公海赌船网址 15

公海赌船网址 16

公海赌船网址 17

  最终总计页,把前边的参数和配置汇总下。没有问题可以点击OK了!

  最终总结页,把前边的参数和布局汇总下。没有问题可以点击OK了!

 

  IP核生成好了,接下去要科学用起来。我们把以太网接口数据传输作为案例背景,通常来说是FPGA逻辑+MAC
IP核+外部PHY芯片的架构。若想让MAC
IP核正确接受待发送数据,需要将数据开展封包并参加MAC头部音信。

  IP核生成好了,接下去要科学用起来。大家把以太网接口数据传输作为案例背景,平时来说是FPGA逻辑+MAC
IP核+外部PHY芯片的架构。若想让MAC
IP核正确接受待发送数据,需要将数据举办封包并出席MAC头部新闻。

  为何说“时钟比例4:1,burst length =
8为例”?这点专门重大。此时用户时钟周期是DDR接口时钟周期的4倍,也就是一个用户时钟信号上升沿对应8个DDR时钟边沿。burst
length可以通晓为MIG连续操作DDR地址的个数,故在4:1时钟比例下,一个用户时钟周期正好对8个地点举办了读/写操作,256bit数目分8次(32bit)写入DDR中。因此分析,在写多少时让app_wdf_end
= app_wdf_wren即可,并且读/写操作时地址递增步长为8.

公海赌船网址 18

公海赌船网址 19

公海赌船网址 20

  为简化设计,先只考虑对封包后数据添加MAC头部的效应,也就是说此时输入的数量即是长度符合以太网规范,且持有数据包格式的数目。由于在数量部分输出前加额外的信息,所以先要缓存输入的数额直到MAC头输出完成再将写入数据发送出来,由此需要用FIFO缓存数据。进一步分析,经过封包后的数据格式如下:

  为简化设计,先只考虑对封包后数据添加MAC头部的效果,也就是说此时输入的数量即是长度符合以太网规范,且富有数据包格式的数目。由于在数据部分输出前加额外的音信,所以先要缓存输入的数额直到MAC头输出完成再将写入数据发送出来,因而需要用FIFO缓存数据。进一步分析,经过封包后的数据格式如下:

  即便MIG
IP核提供了用户接口,但读写指令通道复用且需要实时关注多少个rdy信号造成了时序操作上的不便于。为此大家需要对接口进一步封装,保证写操作时只关注:写使能user_wdata_en
写地址user_waddr
写数据user_wdata和写准备妥当信号user_wdata_公海赌船网址,rdy,读操作时只关注:读使能user_rdata_en
读地址user_raddr 读数据user_rdata
读数据有效user_rdata_vld和读操作准备就绪user_rdata_rdy。

公海赌船网址 21

公海赌船网址 22

  利用扩大接口模块,将读通道和写通道接口分离,并各自例化一个FIFO缓存地址和数量。当读/写指令同时有效时,通过MIG侧的先期级轮换逻辑轮流读取其中一个FIFO,每一遍选一个FIFO读取直至FIFO为空再另行接纳。其工事社团和骨干代码如下:

  其中sop和eop分别是宿迁,包尾指示信号,data_vld是数量有效指示信号。由于数量位宽此处是32位,而数据的细单电元是字节,所以每个32位数据不必然带有4个字节有效数据,使用data_mod指示出无效字节数。为了让该模块输出端知道什么时候输出完一个数据包,要把eop信号和数据信号拼接写入FIFO中,这样输出端发出eop时进入新一轮循环。要是按照写入sop信号来作为先河发送MAC头部和数码部分的注脚,试想当一个短包紧跟着一个长包写进FIFO中时,输出端正在送出上一长包剩下的多少个数据,不可以响应短包的sop信号提醒,那么短包即被“摒弃”了。为了避免丢包现象,需要满意“读写隔离规则”,即FIFO读操作和写操作两者无法遵照一方的处境来控制另一方的行事。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而消息FIFO保留提醒音信,这样讲写侧的指令信号写入消息FIFO中,数据FIFO可以遵照新闻FIFO读侧的音讯来判断读的表现,也就满意了读写隔离规则。

  其中sop和eop分别是宜昌,包尾指示信号,data_vld是数码有效提示信号。由于数量位宽此处是32位,而数据的小小单元是字节,所以每个32位数据不自然带有4个字节有效数据,使用data_mod指示出无效字节数。为了让该模块输出端知道几时输出完一个数据包,要把eop信号和数据信号拼接写入FIFO中,这样输出端发出eop时进入新一轮循环。假如依据写入sop信号来作为先导发送MAC头部和数据部分的注明,试想当一个短包紧跟着一个长包写进FIFO中时,输出端正在送出上一长包剩下的多少个数据,无法响应短包的sop信号提醒,那么短包即被“放弃”了。为了避免丢包现象,需要知足“读写隔离规则”,即FIFO读操作和写操作两者不可能遵照一方的图景来决定另一方的行事。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而音信FIFO保留提醒音信,这样讲写侧的指令信号写入音信FIFO中,数据FIFO可以遵照音信FIFO读侧的新闻来判断读的行为,也就满意了读写隔离规则。

公海赌船网址 23  读侧逻辑核心代码:

公海赌船网址 24

公海赌船网址 25

 1 //读侧--------------------------------------------------------------
 2 
 3 always @(posedge clk or negedge rst_n )begin 
 4     if(rst_n==0) begin
 5         rd_flag <= (0)  ;
 6     end
 7     else if(rd_flag == 0 && mig_rdy && mig_wdf_rdy && !rdempty1 && (rdempty0 || (!rdempty0 && priority == 0)))begin
 8         rd_flag <= (2'b01)  ;//读取 写指令FIFO
 9     end 
10     else if(rd_flag == 0 && mig_rdy && !rdempty0 && (rdempty1 || (!rdempty1 && priority == 1)))begin
11         rd_flag <= (2'b10)  ;//读取 读指令FIFO
12     end 
13     else if((rd_flag == 2'b01 && rdempty1)||(rd_flag == 2'b10 && rdempty0))
14         rd_flag <= 0;
15 end
16 
17 //同时非空时轮换优先级
18 always @(posedge clk or negedge rst_n )begin 
19     if(rst_n==0) begin
20         priority <= (0)  ;
21     end
22     else if(rd_flag == 0 && !rdempty0 && !rdempty1)begin
23         priority <= (!priority)  ;
24     end 
25 end

  在该模块中,可以在写侧现身sop信号时写入信息FIFO一个指示音信,所以当消息FIFO非空即表示有一个数据包正在进入,此时发送MAC头新闻,随之读取数据FIFO中缓存数据,当读侧出现eop信号则读清音信FIFO,循环往复完成了添加头部信息的行事。

  在该模块中,可以在写侧出现sop信号时写入消息FIFO一个指示信息,所以当消息FIFO非空即表示有一个数据包正在进入,此时出殡MAC头新闻,随之读取数据FIFO中缓存数据,当读侧出现eop信号则读清音信FIFO,循环往复完成了添加头部消息的干活。

  为了便利测试,设计样式生成模块与扩充接口模块用户侧接连,不断向一段地址写入一定数据体系并在一段时间后读回。

  MAC头部信息为14字节,而数据位宽是32位,即一次发送多少个字节,所以一定于头部为五个半数码。因而在殡葬第五个头部数据时,低16位要用数据部分填充,前边的数码也要随着移动。如此移位操作后,数据部分就晚了一拍输出最终16位。如若最终这16位数据中有实用字节,那么mac_data_vld当前节奏也要有效,且mac_data_eop和mac_data_mod跟着晚一拍输出;即便无有效字节,则遵照常规情形输出。在代码中本人使用end_normal和end_lag信号来区分上述二种情状。需要特别注意的是,在运动操作后数据包中包含的不算字节个数也会发生变化。为了理清思绪和时序,画出大旨信号时序图:

  MAC头部音讯为14字节,而数据位宽是32位,即三回发送三个字节,所以一定于头部为多少个半数量。由此在发送第六个头部数据时,低16位要用数据部分填充,前边的数码也要随之移动。如此移位操作后,数据部分就晚了一拍输出最终16位。假如最终这16位数据中有实用字节,那么mac_data_vld当前节奏也要使得,且mac_data_eop和mac_data_mod跟着晚一拍输出;假若无有效字节,则依据正常境况输出。在代码中自我利用end_normal和end_lag信号来区别上述三种意况。需要特别注意的是,在活动操作后数据包中包含的失效字节个数也会发生变化。为了理清思绪和时序,画出中央信号时序图:

  1 `timescale 1ns / 1ps
  2 /*
  3 该模块功能:
  4 周期性向一段地址执行读写操作 产生固定样式待写入数据用户测试目的
  5 测试完毕后删除该模块,开发用户接口
  6 
  7 具体为:
  8 1 写从0开始之后的10个用户地址(80个DDR地址):0~9递增序列
  9 2 等待20个时钟周期
 10 3 读取写入的10个用户地址
 11 4 等待20个时钟周期
 12 5 重复上述步骤
 13 
 14 说明:
 15 1 每个步骤之间有一个时钟周期空闲
 16 2 由于burst_len = 8 4:1时钟模式下一个用户时钟周期写入数据对应同样时间内8个DDR时钟边沿写入数据,
 17 因此地址递增步长为8
 18 */
 19 module traffic_gen
 20 #(parameter DATA_WIDTH = 32,
 21             ADDR_WIDTH = 29)
 22 (
 23     input                           clk   ,
 24     input                           rst_n ,
 25 
 26     output reg                      gen_wdata_en ,
 27     output reg [ ADDR_WIDTH-1:0]    gen_waddr    ,
 28     output reg [ DATA_WIDTH-1:0]    gen_wdata ,
 29     input                           gen_wdata_rdy ,//写指令和数据通道准备就绪
 30 
 31     output reg                      gen_rdata_en ,
 32     output reg [ ADDR_WIDTH-1:0]    gen_raddr    ,
 33     input      [ DATA_WIDTH-1:0]    gen_rdata     ,
 34     input                           gen_rdata_vld ,
 35     input                           gen_rdata_rdy //读指令通道准备就绪
 36 );
 37 
 38    
 39 reg [ (8-1):0]  cnt0     ;
 40 wire        add_cnt0 ;
 41 wire        end_cnt0 ;
 42 reg [ (2-1):0]  cnt1     ;
 43 wire        add_cnt1 ;
 44 wire        end_cnt1 ;
 45 
 46 reg [ DATA_WIDTH-1:0]  gen_rdata_r     ;
 47 reg   gen_rdata_vld_r     ;
 48 reg    com_flag     ;
 49 
 50 wire wri_state;
 51 wire rd_state;
 52 wire com_change_t;
 53 
 54 //操作周期计数器,计数值为欲操作用户地址段长度+1(需要一个时钟周期空闲)
 55 always @(posedge clk or negedge rst_n) begin 
 56     if (rst_n==0) begin
 57         cnt0 <= 0; 
 58     end
 59     else if(add_cnt0) begin
 60         if(end_cnt0)
 61             cnt0 <= 0; 
 62         else
 63             cnt0 <= cnt0+1 ;
 64    end
 65 end
 66 assign add_cnt0 = (com_flag == 0 && gen_wdata_rdy) || (com_flag == 1 && gen_rdata_rdy);
 67 assign end_cnt0 = add_cnt0  && cnt0 == (30)-1 ;
 68 
 69 //指令标志位 先是0--写 再是1--读
 70 always @(posedge clk or negedge rst_n )begin 
 71     if(rst_n==0) begin
 72         com_flag <= (0)  ;
 73     end
 74     else if(com_change_t)begin
 75         com_flag <= (!com_flag)  ;
 76     end 
 77 end
 78 
 79 assign com_change_t = add_cnt0 && cnt0 == 10 - 1;
 80 
 81 //写操作---------------------------------------------
 82 always @(posedge clk or negedge rst_n )begin 
 83     if(rst_n==0) begin
 84         gen_wdata_en <= (0)  ;
 85     end
 86     else if(wri_state)begin
 87         gen_wdata_en <= (1'b1)  ;
 88     end 
 89     else begin
 90         gen_wdata_en <= (0)  ;
 91     end 
 92 end
 93 
 94 assign wri_state = add_cnt0 && cnt0 <= 10-1 && com_flag == 0;
 95 assign rd_state  = add_cnt0 && cnt0 <= 10-1 && com_flag == 1;
 96 
 97 always @(posedge clk or negedge rst_n )begin 
 98     if(rst_n==0) begin
 99         gen_wdata <= (0)  ;
100     end
101     else begin
102         gen_wdata <= (cnt0)  ;
103     end 
104 end
105 
106 always@(posedge clk or negedge rst_n)begin
107     if(rst_n == 0)
108         gen_waddr <= 0;
109     else if(wri_state)
110         gen_waddr <= gen_waddr + 29'd8;
111     else 
112         gen_waddr <= 0;
113 end
114 //读操作----------------------------------------------
115 
116 always @(posedge clk or negedge rst_n )begin 
117     if(rst_n==0) begin
118         gen_rdata_en <= (0)  ;
119     end
120     else if(rd_state)begin
121         gen_rdata_en <= (1'b1)  ;
122     end 
123     else begin
124         gen_rdata_en <= (0)  ;
125     end 
126 end
127 
128 always@(posedge clk or negedge rst_n)begin
129     if(rst_n == 0)
130         gen_raddr <= 0;
131     else if(rd_state)
132         gen_raddr <= gen_raddr + 29'd8;
133     else 
134         gen_raddr <= 0;
135 end
136 
137 always @(posedge clk or negedge rst_n )begin 
138     if(rst_n==0) begin
139         gen_rdata_r <= (0)  ;
140     end
141     else begin
142         gen_rdata_r <= (gen_rdata)  ;
143     end 
144 end
145 
146 always @(posedge clk or negedge rst_n )begin 
147     if(rst_n==0) begin
148         gen_rdata_vld_r <= (0)  ;
149     end
150     else if(gen_rdata_vld)begin
151         gen_rdata_vld_r <= (1'b1)  ;
152     end 
153     else begin
154         gen_rdata_vld_r <= (0)  ;
155     end 
156 end
157 
158 endmodule

公海赌船网址 26

公海赌船网址 27

   将traffic_gen和extend_interface模块例化在MIG的example
design中,利用ILA抓取MIG IP核用户接口信号。

公海赌船网址 28

公海赌船网址 29

 公海赌船网址 30

 

 

公海赌船网址 31

 

 

 公海赌船网址 32

 

 

  向地址8~80写入数据0~9,再从此段地址中读回数据,0~9被正确读出,MIG
IP核控制DDR3读写测试停止。

 

 

 

 

   

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  有了序列需求,设计思路后显著模块接口列表:

  有了品种需要,设计思路后明明模块接口列表:

 公海赌船网址 33

 公海赌船网址 34

 

 

  最先编制代码了:

  先导编制代码了:

  1 `timescale 1ns / 1ps
  2 
  3 module add_mac_head(
  4     input clk,
  5     input rst_n,
  6     input [31:0] app_data,
  7     input app_data_vld,
  8     input app_data_sop,
  9     input app_data_eop,
 10     input [1:0] app_data_mod,//无效字节数
 11 
 12     input mac_tx_rdy,//MAC IP发送准备就绪信号
 13     output reg [31:0] mac_data,
 14     output reg mac_data_vld,
 15     output reg mac_data_sop,
 16     output reg mac_data_eop,
 17     output reg [1:0] mac_data_mod
 18     );
 19     
 20     reg [34:0] wdata;
 21     reg wrreq,rdreq;
 22     reg wdata_xx;
 23     reg wrreq_xx,rdreq_xx;
 24     reg [1:0] head_cnt;
 25     reg head_flag,head_tmp,rd_flag,rd_flag_tmp;
 26     reg [34:0] q_tmp;
 27     
 28     wire [31:0] data_shift;
 29     wire add_head_cnt,end_head_cnt;
 30     wire head_neg;
 31     wire [34:0] q;
 32     wire rdempty_xx;
 33     wire sop_in;
 34     wire [2:0] head_len;
 35     wire [111:0] mac_head;
 36     wire [47:0] des_mac,sour_mac;
 37     wire [15:0] pack_type;
 38     wire rd_neg;
 39     
 40     fifo_generator_0 fifo_data (
 41   .clk(clk),      // input wire clk
 42   .din(wdata),      // input wire [34 : 0] din
 43   .wr_en(wrreq),  // input wire wr_en
 44   .rd_en(rdreq),  // input wire rd_en
 45   .dout(q),    // output wire [34 : 0] dout
 46   .full(),    // output wire full
 47   .empty()  // output wire empty
 48 );
 49 
 50     fifo_generator_1 fifo_message (
 51   .clk(clk),      // input wire clk
 52   .din(wdata_xx),      // input wire [0 : 0] din
 53   .wr_en(wrreq_xx),  // input wire wr_en
 54   .rd_en(rdreq_xx),  // input wire rd_en
 55   .dout(),    // output wire [0 : 0] dout
 56   .full(),    // output wire full
 57   .empty(rdempty_xx)  // output wire empty
 58 );
 59     
 60     //数据fifo写数据
 61     always@(posedge clk or negedge rst_n)begin
 62         if(!rst_n)
 63             wdata <= 0;
 64         else if(app_data_vld)    
 65             wdata <= {app_data_eop,app_data_mod,app_data};
 66     end
 67     
 68     always@(posedge clk or negedge rst_n)begin
 69         if(!rst_n)
 70             wrreq <= 0;
 71         else if(app_data_vld)
 72             wrreq <= 1;
 73         else 
 74             wrreq <= 0;
 75     end
 76     
 77     always@(posedge clk or negedge rst_n)begin
 78         if(!rst_n)
 79             wdata_xx <= 0;
 80         else if(sop_in)
 81             wdata_xx <= 1;
 82         else 
 83             wdata_xx <= 0;
 84     end
 85     
 86     assign sop_in = app_data_vld && app_data_sop;
 87     
 88     //当写侧出现sop时表明有一个数据包正在写入,此时写信息FIFO任意数据告知读侧开始发送MAC头部信息
 89     always@(posedge clk or negedge rst_n)begin
 90         if(!rst_n)
 91             wrreq_xx <= 0;
 92         else if(sop_in)
 93             wrreq_xx <= 1;
 94         else 
 95             wrreq_xx <= 0;
 96     end
 97     
 98     //MAC头部有14个字节 数据位宽是32位,即一个数据4个字节,需要发送4个数据(最后一个数据只有2个字节是头部)
 99     always@(posedge clk or negedge rst_n)begin
100         if(!rst_n)
101             head_cnt <= 0;
102         else if(add_head_cnt)begin
103             if(end_head_cnt)
104                 head_cnt <= 0;
105             else 
106                 head_cnt <= head_cnt + 1'b1;
107         end
108     end
109     
110     assign add_head_cnt = head_flag && mac_tx_rdy;
111     assign end_head_cnt = add_head_cnt && head_cnt == head_len - 1 - 1;
112     assign head_len = 4;
113     
114     //发送MAC头部标志位
115     always@(posedge clk or negedge rst_n)begin
116         if(!rst_n)
117             head_flag <= 0;
118         else if(end_head_cnt)
119             head_flag <= 0;
120         else if(!rdempty_xx && !rd_flag)
121             head_flag <= 1;
122     end
123     
124     //读数据FIFO标志位
125     always@(posedge clk or negedge rst_n)begin
126         if(!rst_n)
127             rd_flag <= 0;
128         else if(end_head_cnt)
129             rd_flag <= 1;
130         else if(rd_eop)
131             rd_flag <= 0;
132     end
133     
134     assign rd_eop = rdreq && q[34];
135     
136     always@(*)begin
137         if(rd_flag && mac_tx_rdy)
138             rdreq <= 1;
139         else
140             rdreq <= 0;
141     end
142     
143     //读侧出现eop读取完整版报文,此时读清信息FIFO
144     always@(*)begin
145         if(rd_eop)
146             rdreq_xx <= 1;
147         else
148             rdreq_xx <= 0;
149     end
150     
151     //寄存头部标志位找出下降沿
152     always@(posedge clk or negedge rst_n)begin
153         if(!rst_n)
154             head_tmp <= 0;
155         else 
156             head_tmp <= head_flag;
157     end
158     
159     assign head_neg = head_flag == 0 && head_tmp == 1;
160     
161     //寄存q用于移位操作
162     always@(posedge clk or negedge rst_n)begin
163         if(!rst_n)
164             q_tmp <= 0;
165         else 
166             q_tmp <= q;
167     end
168     
169     assign data_shift = {q_tmp[15:0],q[31:16]};
170     
171     //MAC头 14字节
172     assign mac_head  = {des_mac,sour_mac,pack_type};
173     assign des_mac        = 48'hD0_17_C2_00_E5_40  ;//目的MAC PC网卡物理地址
174     assign sour_mac       = 48'h01_02_03_04_05_06  ;//源MAC地址为01_02_03_04_05_06
175     assign pack_type      = 16'h0800               ;//IP数据报
176     
177     always@(posedge clk or negedge rst_n)begin
178         if(!rst_n)
179             rd_flag_tmp <= 0;
180         else 
181             rd_flag_tmp <= rd_flag;
182     end
183     
184     assign rd_neg = rd_flag == 0 && rd_flag_tmp == 1;
185     
186     //数据输出
187     always@(posedge clk or negedge rst_n)begin
188         if(!rst_n)
189             mac_data_sop <= 0;
190         else if(add_head_cnt && head_cnt == 0)
191             mac_data_sop <= 1;
192         else 
193             mac_data_sop <= 0;
194     end
195     
196     always@(posedge clk or negedge rst_n)begin
197         if(!rst_n)
198             mac_data_eop <= 0;
199         else if(end_normal || end_lag)
200             mac_data_eop <= 1;
201         else 
202             mac_data_eop <= 0;
203     end
204     
205     assign end_normal = rd_eop && q[33:32]      > 2'd1;
206     assign end_lag    = rd_neg && q_tmp[33:32] <= 2'd1;
207     
208     always@(posedge clk or negedge rst_n)begin
209         if(!rst_n)
210             mac_data <= 0;
211         else if(add_head_cnt)//由于MAC不是32位数据的整数倍,需要对数据进行移位
212             mac_data <= mac_head[111 - head_cnt*32 -: 32];
213         else if(head_neg)
214             mac_data <= {mac_head[15:0],q[31:16]};
215         else 
216             mac_data <= data_shift;
217     end
218     
219     always@(posedge clk or negedge rst_n)begin
220         if(!rst_n)
221             mac_data_vld <= 0;
222         else if(head_flag || rd_flag || end_lag)
223             mac_data_vld <= 1;
224         else 
225             mac_data_vld <= 0;
226     end
227     
228     //输出无效字节个数 由于输出端进行了数据移位,导致无效数据个数发生变化
229     always@(posedge clk or negedge rst_n)begin
230         if(!rst_n)
231             mac_data_mod <= 0;
232         else if(end_normal)
233             mac_data_mod <= q[33:32] - 2;
234         else if(end_lag && q_tmp[33:32] == 2'd1)
235             mac_data_mod <= 1;
236         else if(end_lag && q_tmp[33:32] == 0)
237             mac_data_mod <= 2;
238         else 
239             mac_data_mod <= 0;
240     end
241     
242 endmodule
  1 `timescale 1ns / 1ps
  2 
  3 module add_mac_head(
  4     input clk,
  5     input rst_n,
  6     input [31:0] app_data,
  7     input app_data_vld,
  8     input app_data_sop,
  9     input app_data_eop,
 10     input [1:0] app_data_mod,//无效字节数
 11 
 12     input mac_tx_rdy,//MAC IP发送准备就绪信号
 13     output reg [31:0] mac_data,
 14     output reg mac_data_vld,
 15     output reg mac_data_sop,
 16     output reg mac_data_eop,
 17     output reg [1:0] mac_data_mod
 18     );
 19     
 20     reg [34:0] wdata;
 21     reg wrreq,rdreq;
 22     reg wdata_xx;
 23     reg wrreq_xx,rdreq_xx;
 24     reg [1:0] head_cnt;
 25     reg head_flag,head_tmp,rd_flag,rd_flag_tmp;
 26     reg [34:0] q_tmp;
 27     
 28     wire [31:0] data_shift;
 29     wire add_head_cnt,end_head_cnt;
 30     wire head_neg;
 31     wire [34:0] q;
 32     wire rdempty_xx;
 33     wire sop_in;
 34     wire [2:0] head_len;
 35     wire [111:0] mac_head;
 36     wire [47:0] des_mac,sour_mac;
 37     wire [15:0] pack_type;
 38     wire rd_neg;
 39     
 40     fifo_generator_0 fifo_data (
 41   .clk(clk),      // input wire clk
 42   .din(wdata),      // input wire [34 : 0] din
 43   .wr_en(wrreq),  // input wire wr_en
 44   .rd_en(rdreq),  // input wire rd_en
 45   .dout(q),    // output wire [34 : 0] dout
 46   .full(),    // output wire full
 47   .empty()  // output wire empty
 48 );
 49 
 50     fifo_generator_1 fifo_message (
 51   .clk(clk),      // input wire clk
 52   .din(wdata_xx),      // input wire [0 : 0] din
 53   .wr_en(wrreq_xx),  // input wire wr_en
 54   .rd_en(rdreq_xx),  // input wire rd_en
 55   .dout(),    // output wire [0 : 0] dout
 56   .full(),    // output wire full
 57   .empty(rdempty_xx)  // output wire empty
 58 );
 59     
 60     //数据fifo写数据
 61     always@(posedge clk or negedge rst_n)begin
 62         if(!rst_n)
 63             wdata <= 0;
 64         else if(app_data_vld)    
 65             wdata <= {app_data_eop,app_data_mod,app_data};
 66     end
 67     
 68     always@(posedge clk or negedge rst_n)begin
 69         if(!rst_n)
 70             wrreq <= 0;
 71         else if(app_data_vld)
 72             wrreq <= 1;
 73         else 
 74             wrreq <= 0;
 75     end
 76     
 77     always@(posedge clk or negedge rst_n)begin
 78         if(!rst_n)
 79             wdata_xx <= 0;
 80         else if(sop_in)
 81             wdata_xx <= 1;
 82         else 
 83             wdata_xx <= 0;
 84     end
 85     
 86     assign sop_in = app_data_vld && app_data_sop;
 87     
 88     //当写侧出现sop时表明有一个数据包正在写入,此时写信息FIFO任意数据告知读侧开始发送MAC头部信息
 89     always@(posedge clk or negedge rst_n)begin
 90         if(!rst_n)
 91             wrreq_xx <= 0;
 92         else if(sop_in)
 93             wrreq_xx <= 1;
 94         else 
 95             wrreq_xx <= 0;
 96     end
 97     
 98     //MAC头部有14个字节 数据位宽是32位,即一个数据4个字节,需要发送4个数据(最后一个数据只有2个字节是头部)
 99     always@(posedge clk or negedge rst_n)begin
100         if(!rst_n)
101             head_cnt <= 0;
102         else if(add_head_cnt)begin
103             if(end_head_cnt)
104                 head_cnt <= 0;
105             else 
106                 head_cnt <= head_cnt + 1'b1;
107         end
108     end
109     
110     assign add_head_cnt = head_flag && mac_tx_rdy;
111     assign end_head_cnt = add_head_cnt && head_cnt == head_len - 1 - 1;
112     assign head_len = 4;
113     
114     //发送MAC头部标志位
115     always@(posedge clk or negedge rst_n)begin
116         if(!rst_n)
117             head_flag <= 0;
118         else if(end_head_cnt)
119             head_flag <= 0;
120         else if(!rdempty_xx && !rd_flag)
121             head_flag <= 1;
122     end
123     
124     //读数据FIFO标志位
125     always@(posedge clk or negedge rst_n)begin
126         if(!rst_n)
127             rd_flag <= 0;
128         else if(end_head_cnt)
129             rd_flag <= 1;
130         else if(rd_eop)
131             rd_flag <= 0;
132     end
133     
134     assign rd_eop = rdreq && q[34];
135     
136     always@(*)begin
137         if(rd_flag && mac_tx_rdy)
138             rdreq <= 1;
139         else
140             rdreq <= 0;
141     end
142     
143     //读侧出现eop读取完整版报文,此时读清信息FIFO
144     always@(*)begin
145         if(rd_eop)
146             rdreq_xx <= 1;
147         else
148             rdreq_xx <= 0;
149     end
150     
151     //寄存头部标志位找出下降沿
152     always@(posedge clk or negedge rst_n)begin
153         if(!rst_n)
154             head_tmp <= 0;
155         else 
156             head_tmp <= head_flag;
157     end
158     
159     assign head_neg = head_flag == 0 && head_tmp == 1;
160     
161     //寄存q用于移位操作
162     always@(posedge clk or negedge rst_n)begin
163         if(!rst_n)
164             q_tmp <= 0;
165         else 
166             q_tmp <= q;
167     end
168     
169     assign data_shift = {q_tmp[15:0],q[31:16]};
170     
171     //MAC头 14字节
172     assign mac_head  = {des_mac,sour_mac,pack_type};
173     assign des_mac        = 48'hD0_17_C2_00_E5_40  ;//目的MAC PC网卡物理地址
174     assign sour_mac       = 48'h01_02_03_04_05_06  ;//源MAC地址为01_02_03_04_05_06
175     assign pack_type      = 16'h0800               ;//IP数据报
176     
177     always@(posedge clk or negedge rst_n)begin
178         if(!rst_n)
179             rd_flag_tmp <= 0;
180         else 
181             rd_flag_tmp <= rd_flag;
182     end
183     
184     assign rd_neg = rd_flag == 0 && rd_flag_tmp == 1;
185     
186     //数据输出
187     always@(posedge clk or negedge rst_n)begin
188         if(!rst_n)
189             mac_data_sop <= 0;
190         else if(add_head_cnt && head_cnt == 0)
191             mac_data_sop <= 1;
192         else 
193             mac_data_sop <= 0;
194     end
195     
196     always@(posedge clk or negedge rst_n)begin
197         if(!rst_n)
198             mac_data_eop <= 0;
199         else if(end_normal || end_lag)
200             mac_data_eop <= 1;
201         else 
202             mac_data_eop <= 0;
203     end
204     
205     assign end_normal = rd_eop && q[33:32]      > 2'd1;
206     assign end_lag    = rd_neg && q_tmp[33:32] <= 2'd1;
207     
208     always@(posedge clk or negedge rst_n)begin
209         if(!rst_n)
210             mac_data <= 0;
211         else if(add_head_cnt)//由于MAC不是32位数据的整数倍,需要对数据进行移位
212             mac_data <= mac_head[111 - head_cnt*32 -: 32];
213         else if(head_neg)
214             mac_data <= {mac_head[15:0],q[31:16]};
215         else 
216             mac_data <= data_shift;
217     end
218     
219     always@(posedge clk or negedge rst_n)begin
220         if(!rst_n)
221             mac_data_vld <= 0;
222         else if(head_flag || rd_flag || end_lag)
223             mac_data_vld <= 1;
224         else 
225             mac_data_vld <= 0;
226     end
227     
228     //输出无效字节个数 由于输出端进行了数据移位,导致无效数据个数发生变化
229     always@(posedge clk or negedge rst_n)begin
230         if(!rst_n)
231             mac_data_mod <= 0;
232         else if(end_normal)
233             mac_data_mod <= q[33:32] - 2;
234         else if(end_lag && q_tmp[33:32] == 2'd1)
235             mac_data_mod <= 1;
236         else if(end_lag && q_tmp[33:32] == 0)
237             mac_data_mod <= 2;
238         else 
239             mac_data_mod <= 0;
240     end
241     
242 endmodule

 编写测试激励验证效能:

 编写测试激励验证效率:

 1 `timescale 1ns / 1ps
 2 
 3 module add_mac_head_tb;
 4 
 5     
 6     reg clk,rst_n;
 7     reg [31:0] app_data;
 8     reg app_data_sop,app_data_eop,app_data_vld;
 9     reg [1:0] app_data_mod;
10     reg mac_tx_rdy;
11     
12     wire [31:0] mac_data;
13     wire mac_data_vld,mac_data_sop,mac_data_eop;
14     wire [1:0] mac_data_mod;
15 
16     add_mac_head add_mac_head(
17     .clk(clk),
18     .rst_n(rst_n),
19     .app_data(app_data),
20     .app_data_vld(app_data_vld),
21     .app_data_sop(app_data_sop),
22     .app_data_eop(app_data_eop),
23     .app_data_mod(app_data_mod),//无效字节数
24 
25     .mac_tx_rdy(mac_tx_rdy),//MAC IP发送准备就绪信号
26     .mac_data(mac_data),
27     .mac_data_vld(mac_data_vld),
28     .mac_data_sop(mac_data_sop),
29     .mac_data_eop(mac_data_eop),
30     .mac_data_mod(mac_data_mod)
31     );
32     
33     parameter CYC = 5,
34               RST_TIME = 2;
35               
36     integer i;
37               
38     initial begin
39         clk = 1;
40         forever #(CYC / 2.0) clk = ~clk;
41     end
42     
43     initial begin
44         rst_n = 1;
45         #1;
46         rst_n = 0;
47         #(CYC*RST_TIME);
48         rst_n = 1;
49     end
50     
51     initial begin
52         #1;
53         app_data = 0;
54         app_data_sop = 0;
55         app_data_eop = 0;
56         app_data_mod = 0;
57         app_data_vld = 0;
58         mac_tx_rdy = 1;
59         #(CYC*RST_TIME);
60         packet_gen(10,0);
61         packet_gen(5,0);
62         packet_gen(15,2);
63         #1000;
64         $stop;
65     end
66     
67     task packet_gen;
68         input [15:0] length;
69         input [1:0] invld_num;
70         begin
71             app_data_vld = 1;
72             app_data_sop = 1;
73             app_data = 32'h01020300;
74             for(i = 0;i < length;i = i + 1'b1)begin
75                 if(i == 1)
76                     app_data_sop = 0;
77                 else if(i == length - 1)begin
78                     app_data_mod = invld_num;
79                     app_data_eop = 1;
80                 end
81                 app_data = app_data +1'b1;
82                 #(CYC*1);
83             end
84             app_data_eop = 0;
85             app_data_vld = 0;
86             app_data_mod = 0;
87         end
88     endtask
89 
90 endmodule
 1 `timescale 1ns / 1ps
 2 
 3 module add_mac_head_tb;
 4 
 5     
 6     reg clk,rst_n;
 7     reg [31:0] app_data;
 8     reg app_data_sop,app_data_eop,app_data_vld;
 9     reg [1:0] app_data_mod;
10     reg mac_tx_rdy;
11     
12     wire [31:0] mac_data;
13     wire mac_data_vld,mac_data_sop,mac_data_eop;
14     wire [1:0] mac_data_mod;
15 
16     add_mac_head add_mac_head(
17     .clk(clk),
18     .rst_n(rst_n),
19     .app_data(app_data),
20     .app_data_vld(app_data_vld),
21     .app_data_sop(app_data_sop),
22     .app_data_eop(app_data_eop),
23     .app_data_mod(app_data_mod),//无效字节数
24 
25     .mac_tx_rdy(mac_tx_rdy),//MAC IP发送准备就绪信号
26     .mac_data(mac_data),
27     .mac_data_vld(mac_data_vld),
28     .mac_data_sop(mac_data_sop),
29     .mac_data_eop(mac_data_eop),
30     .mac_data_mod(mac_data_mod)
31     );
32     
33     parameter CYC = 5,
34               RST_TIME = 2;
35               
36     integer i;
37               
38     initial begin
39         clk = 1;
40         forever #(CYC / 2.0) clk = ~clk;
41     end
42     
43     initial begin
44         rst_n = 1;
45         #1;
46         rst_n = 0;
47         #(CYC*RST_TIME);
48         rst_n = 1;
49     end
50     
51     initial begin
52         #1;
53         app_data = 0;
54         app_data_sop = 0;
55         app_data_eop = 0;
56         app_data_mod = 0;
57         app_data_vld = 0;
58         mac_tx_rdy = 1;
59         #(CYC*RST_TIME);
60         packet_gen(10,0);
61         packet_gen(5,0);
62         packet_gen(15,2);
63         #1000;
64         $stop;
65     end
66     
67     task packet_gen;
68         input [15:0] length;
69         input [1:0] invld_num;
70         begin
71             app_data_vld = 1;
72             app_data_sop = 1;
73             app_data = 32'h01020300;
74             for(i = 0;i < length;i = i + 1'b1)begin
75                 if(i == 1)
76                     app_data_sop = 0;
77                 else if(i == length - 1)begin
78                     app_data_mod = invld_num;
79                     app_data_eop = 1;
80                 end
81                 app_data = app_data +1'b1;
82                 #(CYC*1);
83             end
84             app_data_eop = 0;
85             app_data_vld = 0;
86             app_data_mod = 0;
87         end
88     endtask
89 
90 endmodule

   连续输入几个长度不同的报文,此处为了设计重用,用可参数化的task对鼓舞报文举行打包。需要输入报文时只需调用packet_gen任务即可兑现所有不同长度,不同无效字节个数的数据包。观察输出波形:

   连续输入六个长度不同的报文,此处为了设计重用,用可参数化的task对鼓舞报文举行包装。需要输入报文时只需调用packet_gen任务即可实现所有不同长度,不同无效字节个数的数据包。观看输出波形:

公海赌船网址 35

公海赌船网址 36

 mac侧输出六个包文数据如下:

 mac侧输出三个包文数据如下:

公海赌船网址 37

公海赌船网址 38

 

 

 

 

 

 

公海赌船网址 39

公海赌船网址 40

公海赌船网址 41

公海赌船网址 42

 

 

 

 

 

 

   可以看出mac侧数据发送正确。本博文由于紧要讲述FIFO应用,这里只做出行为仿真,读者能够灵活运用,添加在自己的项目中。

   可以见到mac侧数据发送正确。本博文由于重要讲述FIFO应用,这里只做出行为仿真,读者可以灵活运用,添加在自己的品类中。

相关文章