FIFO(First In First Out),即先进先出。FPGA 或者 ASIC 中使用到的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互。它与普通存储器的区别是没有外部读写地址线,这样使用起来相对简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO 从读写时钟上来分有两类结构:单时钟 FIFO(同步 FIFO)和双时钟 FIFO(异步 FIFO)。单时钟 FIFO 具有一个时钟(读写共用一个时钟)输入,因此所有输入信号的读取都是在这个时钟的上升沿进行的,所有输出信号的变化也是在这个时钟信号的上升沿的控制下进行的,即单时钟 FIFO 的所有输入输出信号都是同步这个时钟信号的。而在双时钟 FIFO 结构中,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wr_clk 的,所有与读相关的信号都是同步于读时钟 rd_clk 的。下面图是双时钟 FIFO 的整体框图和内部实现的框图,有单独的模块对读写时钟域进行同步处理。

本文示范一个单时钟FIFO,数据位宽为8位,数据深度为256,在Vivodo中产生一个FIFO IP核:

将IP核例化到顶层模块中,并在顶层模块中完成写入、读出的控制(不另外编写写入、读出模块)。
例化FIFO:
wire full ;//例化FIFO
wire empty ;
reg wr_en ;
reg rd_en ;
reg [ 7:0] din ;
reg [ 1:0] wr_state ;
reg [ 1:0] rd_state ;
fifo u_fifo(
.clk (clk ),
.srst (srst ),
.din (din ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.dout (dout ),
.full (full ),
.empty (empty )
);
写入控制:
always @(posedge clk ) begin //写数据
if(srst)begin
wr_en<=0;
din<=0;
wr_state<=0;
end
else begin
case (wr_state)
0:
begin
if(empty)begin
wr_en<=1;
wr_state<=1;
end
else
wr_state<=0;
end
1:
begin
if(full)begin
wr_en<=0;
wr_state<=0;
din<=0;
end
else begin
wr_en<=1;
din<=din+1'b1;
end
end
default:wr_state<=0;
endcase
end
end
读出控制:
always @(posedge clk ) begin //读数据
if(srst)begin
rd_en<=0;
rd_state<=0;
end
else begin
case (rd_state)
0:
if(full)begin
rd_en<=1;
rd_state<=1;
end
else begin
rd_en<=0;
rd_state<=0;
end
1:
if(empty)begin
rd_en<=0;
rd_state<=0;
end
else begin
rd_state<=1;
end
default: rd_state<=0;
endcase
end
end
testbench:
`timescale 1ns / 1ps
module fifo_tb();
reg clk;
reg srst;
wire [7:0]dout;
fifo_test u_fifo_test(
.clk (clk ),
.srst (srst ),
.dout (dout )
);
initial clk=1;
always#10 clk=!clk;
initial begin
srst=1;
#201;
srst=0;
#200000;
$stop;
end
endmodule
仿真结果:
时钟高电平复位,可以发现,当empty脉冲信号出现时,wr_en拉高,开始往FIFO中写入数据;写满之后,full信号脉冲拉高,wr_en拉低,rd_en拉高,开始读出数据,直至数据全部读出,empty脉冲信号再次出现,这样就是一个写读周期。