首页 > 技术文章 > IP核——FIFO

xianyufpga 2019-01-04 20:21 原文

一、Quartus

1.打开Quartus ii,点击Tools---MegaWizard Plug-In Manager

2.弹出创建页面,选择Creat a new custom megafunction variation,点Next

3.选择IP核,可以直接搜索fifo,选择fifo,右上方选择器件型号,语言选成Verilog,再填写一下路径名字,点Next,后面就是参数设置了。

4.设置数据的宽度和深度,宽度即数据位宽,深度即fifo里做多存放多少个数据,深度的计算要考虑写的时刻和读的时刻以及时钟快慢。下面是选中同步时钟还是异步时钟,这里选择同步时钟,下面的设置也是基于同步时钟。如果是异步时钟则下面的设置会出现写侧和读侧两对参数进行选择,其实是一样的道理。Next

4.选择一些指示信号,需要就选,不需要就不选,Next

5.选择哪种模式,这还是有些学问的,图上用文字简单的说明了一下,下面做个实验来看看他们究竟有什么不同。存储类型选择默认的Auto就行。Next

6.默认,Next

7.默认,Next

8.一般不勾选bb文件,finish。

 

 

 二、ISE

1.创建ISE工程,IP核需要在ISE工程里面进行调用。点击Tools---Core Generator...

2.在新弹出来的界面中创建一个属于IP核的工程:file---new project,并填写文件存储位置和文件名称,一般为ipcore_dir文件夹,点击保存

3.弹出的Part处填写器件的系列、型号、封装以及速度等级,Generation处设置语言为Verilog,点击OK

4.点击文件夹,找到Memories & Storage Elements---FIFOs---FIFO Generator,(也可以直接搜索fifo)双击打开,进行参数设置

5.命名,类型选择默认即可,Next

 6.选择同步或异步,一般是选中含有Block RAM的选项,Next

.

7.模式选择和宽度、深度选择。Next

 8.信号选择,需要就勾选,不需要就不勾选,Next

9.是否进行初始化,Next

10.是否进行数据计数,Next

11.总结页面,Generate

 

三、Normal(Standard)模式和Show-ahead(First-word fall-through)模式的区别

  对此很多书籍没有多提及,教学视频也就一两句话带过。我一直似懂非懂,于是编了个仿真来测试一下。

1.设计文件

  我例化了两个fifo,一个Normal模式的fifo,一个Show-ahead模式的fifo,他们的写数据、写使能、读使能都一样,用控制变量法观察q的输出情况。这段代码意思很简单,我设计了一个0-21的输入数据和对应输入有效指示信号。将数据为123456789 10时,这些数据写入fifo。在数据等于11 12 13 14 15 16 17 18 19 20时,将刚刚写入fifo的数据读出来。后面又加入一个dout输出,测试rdreq做if条件,后面会出现什么有趣结果。

 1 //==========================================================================
 2 // --- 名称 : fifo_ctrl.v
 3 // --- 作者 : xianyu_FPGA
 4 // --- 日期 : 2019-01-03
 5 // --- 描述 : 数据进来,写满就开始读。normal模式和show_ahead模式对比
 6 //==========================================================================
 7 
 8 module fifo_ctrl
 9 //---------------------<端口声明>-------------------------------------------
10 (
11 input  wire             clk                 , //时钟,50Mhz
12 input  wire             rst_n               , //复位,低电平有效
13 input  wire [7:0]       din                 , //输入数据
14 input  wire             din_vld             , //输入数据指示信号
15 output reg  [7:0]       dout_normal         ,
16 output reg  [7:0]       dout_show_ahead
17 );
18 //---------------------<信号定义>-------------------------------------------
19 wire [7:0]              data                ; //fifo输入的数据
20 wire                    rdreq               ; //fifo读请求
21 wire                    wrreq               ; //fifo写请求
22 wire [7:0]              q_normal            ; //fifo输出的数据
23 wire [7:0]              q_show_ahead        ; //fifo输出的数据
24 
25 //--------------------------------------------------------------------------
26 //--   FIFO例化
27 //--------------------------------------------------------------------------
28 ip_fifo_normal u_1
29 (
30     .clock              (clk                ),
31     .data               (data               ),
32     .rdreq              (rdreq              ),
33     .wrreq              (wrreq              ),
34     .empty              (                   ),
35     .full               (                   ),
36     .usedw              (                   ),
37     .q                  (q_normal           )
38 );
39 
40 ip_fifo_show_ahead u_2
41 (
42     .clock              (clk                ),
43     .data               (data               ),
44     .rdreq              (rdreq              ),
45     .wrreq              (wrreq              ),
46     .empty              (                   ),
47     .full               (                   ),
48     .usedw              (                   ),
49     .q                  (q_show_ahead       )
50 );
51 
52 //--------------------------------------------------------------------------
53 //--   fifo 写
54 //--------------------------------------------------------------------------
55 assign data  = din;
56 assign wrreq = din_vld && din>=1 && din<=10; //写进123456789 10
57 
58 
59 //--------------------------------------------------------------------------
60 //--   fifo 读
61 //--------------------------------------------------------------------------
62 assign rdreq = din_vld && din>=11; //第11个数后开始读
63 
64 
65 //--------------------------------------------------------------------------
66 //--   输出
67 //--------------------------------------------------------------------------
68 always @(posedge clk or negedge rst_n) begin
69     if(!rst_n) begin
70         dout_normal     <= 0;
71         dout_show_ahead <= 0;
72     end
73     else if(rdreq) begin
74         dout_normal     <= q_normal;
75         dout_show_ahead <= q_show_ahead;
76     end
77 end
78 
79 
80 
81 
82 endmodule

 

2.测试文件

  输入数据和对应指示信号,数据为:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

 1 `timescale 1ns/1ps  //时间精度
 2 `define    Clock 20 //时钟周期
 3 
 4 module fifo_ctrl_tb;
 5 
 6 //---------------------<端口定义>-------------------------------------------
 7 reg                     clk                 ; //时钟,50Mhz
 8 reg                     rst_n               ; //复位,低电平有效
 9 reg  [7:0]              din                 ; //输入数据
10 reg                     din_vld             ; //输入数据指示信号
11 wire [7:0]              dout_normal         ;
12 wire [7:0]              dout_show_ahead     ;
13 
14 //--------------------------------------------------------------------------
15 //--   模块例化
16 //--------------------------------------------------------------------------
17 fifo_ctrl u_fifo_ctrl
18 (
19     .clk                (clk                ),     
20     .rst_n              (rst_n              ),
21     .din                (din                ),
22     .din_vld            (din_vld            ),
23     .dout_normal        (dout_normal        ),
24     .dout_show_ahead    (dout_show_ahead    )
25 );
26 
27 //----------------------------------------------------------------------
28 //--   时钟信号和复位信号
29 //----------------------------------------------------------------------
30 initial begin
31     clk = 1;
32     forever
33         #(`Clock/2) clk = ~clk;
34 end
35 
36 initial begin
37     rst_n = 0; #(`Clock*20+1);
38     rst_n = 1;
39 end
40 
41 //----------------------------------------------------------------------
42 //--   设计输入信号
43 //----------------------------------------------------------------------
44 reg     [4:0]    i;
45 
46 task data;
47     begin
48         for(i=0;i<=20;i=i+1) begin
49             din     = i;
50             din_vld = 1;
51             #(`Clock);
52             din_vld = 0;
53             #(`Clock*4);
54         end
55     end
56 endtask
57 
58 
59 initial begin
60     #1;
61     din     = 0;
62     din_vld = 0;
63     #(`Clock*20+1); //初始化完成
64     
65     data;
66     #(`Clock*5);
67     $stop;
68 end
69 
70 
71 
72 endmodule

 

3.仿真波形

  波形中出现了多个rdreq信号,其实是同一个,为了方便看情况,我进行了复制信号而且改了颜色罢了。图中信号,绿色为输入信号,红色为fifo写信号,黄色为normal模式的情况,紫色为show-ahead模式的情况。

 

 

4.结论

  ①Normal:先有rdreq,q中再有数据。输出不能用rdreq做if判断,否则会丢数据,如果一定要用到rdreq搞事情,那么rdreq打一拍再用就行了。

  ②Show-ahead:q上一直有数据,有rdreq就切换到下一个数据,rdreq信号像是应答信号ack。输出可以直接用rdreq做if判断。

 

四、ROM、RAM和FIFO的区别

1.ROM有地址,只能读而不能写。用初始化文件mif/ceo将内容存进去,读取不会使得数据减少消失。

2.RAM有地址,可以进行寻址读写,数据写进去后,读取不会使得数据减少消失。

3.FIFO没有地址,只能是先进先出,数据写进去后,读取会使得数据减少消失,读一个少一个。

 

参考资料:

[1]小梅哥FPGA教程

[2]威三学院FPGA教程

推荐阅读