一、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教程