一、简介
FIFO 的英文全称是 First In First Out,即先进先出。 FPGA 使用的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互,也即所谓的跨时钟域信号传递。它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。
1、FIFO分类
FIFO 从输入时钟的角度来分,有两种类型:单时钟 FIFO(SCFIFO)和双时钟 FIFO (DCFIFO);其中,双时钟 FIFO 又可从输出数据的位宽的角度,分为普通双时钟(DCFIFO)和混合宽度双时钟 FIFO(DCFIFO_MIXED_WIDTHS)。单时钟 FIFO 具有一个独立的时钟端口 clock,因此,所有的输入输出信号都同步于 clock 信号。双时钟 FIFO结构中,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟wrclk,所有与读相关的信号都是同步于读时钟 rdclk。
2、同步FIFO和异步FIFO 原理图
3、单时钟 FIFO 和双时钟 FIFO 的应用场景
单时钟 FIFO 常用于同步时钟的数据缓存;
双时钟 FIFO 常用于跨时钟域的数据信号的传递,例如时钟域 A 下的数据 data1 传递给异步时钟域 B,当 data1 为连续变化信号时,如果直接传递给时钟域 B 则可能会导致收非所送的情况,即在采集过程中会出现包括亚稳态(数据采样失真)问题在内的一系列问题,使用双时钟 FIFO 能够将不同时钟域中的数据同步到所需的时钟域中。
二、FIFO的配置
1、FIFO的选择
如图所示,在 IP Catalog 的搜索栏中搜索 FIFO,然后选择 FIFO。(这里和前面调用的pll一样也是需要先进行工程创建,不清楚的小伙伴可以在前面的文章看看)
2、 FIFO存放位置的选择
3、fifo存储容量和同步时钟设置
如下图所示,该界面配置的参数有:
方框①:fifo的数据存储容量
方框②:fifo读写的时钟选择,Yes选项是读写时钟同一个时钟,No是读写使用不同的时钟,这里看自己需要进行选择。在本篇文章中默认使用同一个时钟。然后点击 Next 进入下一界面。
4、控制信号的选择
如下图所示,该界面配置的参数有:
方框①:这里是满信号------full、空信号------empty、fifo剩余容量信号------usedw的选择,这里一般我们不用选,系统默认就已经选择好了,如果没有就自行勾选。
方框②:也是满信号的设置,只不过这里和上面一个的区别就是这个信号是容量接近满的状态,这样可以给fifo留有缓冲的容量,避免数据出错。不做要求一般不选
方框③:这里是空信号的设置,当fifo剩余容量少于某个值时,系统就认为是空,这里和②一样一般也是默认不选。
方框④:这里就是数据清除的选择,第一个选想是异步清除,第二个选项是同步清除。这里我们默认选择异步清除。然后点击 Next 进入下一界面。
5、fifo显示模式的选择
如下图所示,该界面配置的参数有:
方框①:这里是fifo显示模式的选择,认为普通模式和前显模式,如果需要实时读写的话就选择第一个普通模式,如果是对已经存在的数据进行一个读取或者修改的话就选择第二个前显模式。
方框②:这里是fifo构建资源的选择,一般默认就行。然后点击 Next 进入下一界面。
6、最大输出的寄存器选择
这里因为需要占用额外的资源,所以一般选择No。
7、仿真工具文件的选择
8、 生成文件的选择
如下图所示,该页面为需要配置生成的文件。勾选fifo_inst.v 即可。rfifo_bb.v 可勾可不勾选。然后点击 finish。
9、将文件添加到工程中
如下图所示,配置完成,点击"Yes"即可。
三、FIFO的调用
1、设计文件的编写
在rtl文件夹中新建ip_fifo.v文件,其中ram实例化代码可以从 fifo_inst.v 例化模板文件复制。如图:
cpp
module ip_fifo (
input clk,
input rst_n,
input [7:0] data,
input rden,
input wren,
output empty,
output full,
output [7:0] q,
output [7:0] usedw
);
wire rdreq;
wire wrreq;
fifo fifo_inst (
.aclr ( ~rst_n ),//复位
.clock ( clk ),//时钟
.data ( data ),//输入数据
.rdreq ( rden ),//读有效信号
.wrreq ( wren ),//写有效信号
.empty ( empty ),//空标志位
.full ( full),//满标志位
.q ( q ),
.usedw ( usedw )//FIFO 中剩余的数据量
);
endmodule
2、测试文件的编写
cpp
`timescale 1ns/1ns
module fifo_tb();
reg clk ;
reg rst_n ;
reg [7:0] data ;
reg rden ;
reg wren ;
wire empty ;
wire full ;
wire [7:0] q ;
wire [7:0] usedw ;
ip_fifo ip_fifo_inst(
/*input */ .clk (clk ) ,
/*input */ .rst_n (rst_n ) ,
/*input [7:0] */ .data (data ) ,
/*input */ .rden (rden ) ,
/*input */ .wren (wren ) ,
/*output */ .empty (empty ) ,
/*output */ .full (full ) ,
/*output [7:0] */ .q (q ) ,
/*output [7:0] */ .usedw (usedw )
);
//时钟
parameter CLK_CLY =20 ;
initial clk=0;
always #(CLK_CLY/2) clk=~clk;
integer i;
//复位
initial begin
rst_n =1'b0;
data = 0;
wren = 0;
rden = 0;
#(CLK_CLY*2);
#3;
rst_n=1'b1;
#(10*CLK_CLY);
//开始赋值
for(i=0;i<512;i=i+1)begin
data = {$random};
wren = 1'b1;
#(1*CLK_CLY);
wren = 1'b0;
end
#(100*CLK_CLY);
for(i=0;i<200;i=i+1)begin
rden = 1'b1;
#(1*CLK_CLY);
rden = 1'b0;
end
end
endmodule
3、波形仿真
通过波形图和mendata我们可以看到数据已经成功写入且成功读出。调用到此结束。