一、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条件,后面会出现什么有趣结果。
//==========================================================================
// --- 名称 : fifo_ctrl.v
// --- 作者 : xianyu_FPGA
// --- 日期 : 2019-01-03
// --- 描述 : 数据进来,写满就开始读。normal模式和show_ahead模式对比
//========================================================================== module fifo_ctrl
//---------------------<端口声明>-------------------------------------------
(
input wire clk , //时钟,50Mhz
input wire rst_n , //复位,低电平有效
input wire [:] din , //输入数据
input wire din_vld , //输入数据指示信号
output reg [:] dout_normal ,
output reg [:] dout_show_ahead
);
//---------------------<信号定义>-------------------------------------------
wire [:] data ; //fifo输入的数据
wire rdreq ; //fifo读请求
wire wrreq ; //fifo写请求
wire [:] q_normal ; //fifo输出的数据
wire [:] q_show_ahead ; //fifo输出的数据 //--------------------------------------------------------------------------
//-- FIFO例化
//--------------------------------------------------------------------------
ip_fifo_normal u_1
(
.clock (clk ),
.data (data ),
.rdreq (rdreq ),
.wrreq (wrreq ),
.empty ( ),
.full ( ),
.usedw ( ),
.q (q_normal )
); ip_fifo_show_ahead u_2
(
.clock (clk ),
.data (data ),
.rdreq (rdreq ),
.wrreq (wrreq ),
.empty ( ),
.full ( ),
.usedw ( ),
.q (q_show_ahead )
); //--------------------------------------------------------------------------
//-- fifo 写
//--------------------------------------------------------------------------
assign data = din;
assign wrreq = din_vld && din>= && din<=; //写进123456789 10 //--------------------------------------------------------------------------
//-- fifo 读
//--------------------------------------------------------------------------
assign rdreq = din_vld && din>=; //第11个数后开始读 //--------------------------------------------------------------------------
//-- 输出
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dout_normal <= ;
dout_show_ahead <= ;
end
else if(rdreq) begin
dout_normal <= q_normal;
dout_show_ahead <= q_show_ahead;
end
end endmodule
2.测试文件
输入数据和对应指示信号,数据为:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
`timescale 1ns/1ps //时间精度
`define Clock //时钟周期 module fifo_ctrl_tb; //---------------------<端口定义>-------------------------------------------
reg clk ; //时钟,50Mhz
reg rst_n ; //复位,低电平有效
reg [:] din ; //输入数据
reg din_vld ; //输入数据指示信号
wire [:] dout_normal ;
wire [:] dout_show_ahead ; //--------------------------------------------------------------------------
//-- 模块例化
//--------------------------------------------------------------------------
fifo_ctrl u_fifo_ctrl
(
.clk (clk ),
.rst_n (rst_n ),
.din (din ),
.din_vld (din_vld ),
.dout_normal (dout_normal ),
.dout_show_ahead (dout_show_ahead )
); //----------------------------------------------------------------------
//-- 时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
clk = ;
forever
#(`Clock/) clk = ~clk;
end initial begin
rst_n = ; #(`Clock*+);
rst_n = ;
end //----------------------------------------------------------------------
//-- 设计输入信号
//----------------------------------------------------------------------
reg [:] i; task data;
begin
for(i=;i<=;i=i+) begin
din = i;
din_vld = ;
#(`Clock);
din_vld = ;
#(`Clock*);
end
end
endtask initial begin
#;
din = ;
din_vld = ;
#(`Clock*+); //初始化完成 data;
#(`Clock*);
$stop;
end 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教程