部分转载~
FIFO:First Input First Output,即先入先出队列,在计算机中,先进先出队列是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。
主要功能包括:
(1)数据的缓冲。如果数据的写入速率高,但间隔大,且会有突发;读出速率小,但相对均匀。则通过设置相应深度的FIFO,可以起到数据暂存的功能,且能够使后续处理流程平滑,避免前级突发时,后级来不及处理而丢弃数据。
(2)时钟域的隔离。对于不同时钟域的数据传递,则数据可以通过FIFO进行隔离,避免跨时钟域的数据传输带来的设计与约束上的复杂度。
(3)不同宽度的数据接口。例如单片机8位数据输出,而DSP是16位数据输入,在单片机和DSP连接时就可以使用FIFO来达到数据匹配的目的。
example:
当模块A要发送数据给模块B,其中模块A的工作时钟是100MHz,模块B的工作时钟是80MHz。若要直接讲A数据送给B,由于时钟不同,B模块势必会丢失数据或多采集数据。
解决问题的方案:使用FIFO,由于FIFO内部可以做跨时钟域处理,读写时钟可以不同,因此非常适合解决这类问题。
如图所示,模块C的功能如下:
a. 模块内部包含一个FIFO,该FIFO数据位宽16bit,深度是64,有rdusedw、wrusedw、empty等指示信号。
b. 输入数据data_in和data_in_vld都是输入clk_in时钟域的,模块首先将数据写入到内部FIFO中,等待被模块B读取。
c. 如果内部FIFO快满时(wrusede>=61),仍然有数据要写入,为了防止FIFO溢出,丢弃该数据,不再写入FIFO。
d. Data_out、data_out_vld和b_rdy都属于clk_out时钟域的。
e. b_rdy是模块B产生的接收数据准备好信号,当此信号为1时,表示模块B已经准备好接收数据,本模块可以将数据发送给模块B。如果为0,表示未准备好,不能将数据发送给模块B。
f. 当b_rdy为1时,且FIFO内有数据(rdempey==0)时,将数据送给模块B。
使用FIFO时要保证当FIFO已经处于满状态时,就不再往FIFO里写数据;当FIFO处于空状态时,不再读FIFO的数据。
基于Vivdao的FIFO IP核使用实例
1、打开IP Catalog–>搜索fifo—>选择FIFO Generator
2、打开FIFO IP核的配置界面
Vivado的IP一般分为以下几个步骤:
- Component Name:IP名称
- Basic:基本设置
- Native Ports:本地端口
- Status Flags:标志位
- Data Counts:数据数量
- Summary:总结
一、Basic 基本设置
(1)IP核的一般命名规则:同步/一般_fifo_数据位宽_数据深度
(2) Interface Type:端口类型。一般选择第一个,AXI是Xilinx特有的高速接口。
(3)Fifo Implementation:FIFO的实现方式。
FIFO实现方式有Common Clock同步和Independent Clocks异步两种,每种FIFO下有四种存储形式:
- Block RAM:使用FGPA内部的RAM。
- Distributed RAM:分布式RAM,使用内部的LUT和逻辑电路组成的fifo。
- Shift Register:移位寄存器形式,深度不大于32时可以使用。
- Build-in:内部自带的fifo,深度大于128可以使用。
二、Native Ports本地接口
1.Read Mode读模式
-
Standard FIFO:标准FIFO。选择这个模式,读出的数据会之后使能信号1个时钟周期,第1个数据先暂存在存储单元中,当信号变为高电平后,下1个时钟周期输出;
-
First Word Fall Through:简称FWFT FIFO。第1个数据直接送入输出缓冲区,并保持在输出总线上,当读信号变为高电平后,当前周期输出第1个数据,第2个周期输出第2个数据。
第1个写入数据将被从ram中提前读出到读数据线,第1个数据有效与empty无效同时,即当信号有效后,读数据线将显示下一个数据地址(读出的数据与读使能信号同一个时钟周期)
-
Data Port Parameters 数据接口参数
write width 写宽度
write depth 写深度
read width 读宽度
read depth 读深度
(后面读的设置通常与写的设置相同) -
Initialization 初始化
Reset Pin 复位引脚
Enable Safety Circuit 启用安全电路,一般都要选上
Reset Type 复位类型,有两种:异步复位和同步复位
Full Flag Reset Value 复位的值,0为低电平复位,1为高电平复 位
三、Status Flag 标志位设置
- Optional flag 选择的标志
Almost Full Flag 几乎满标志位
Almost Empty Flag 几乎空标志 - Handshakiing Options 握手选项,用于设置什么时候读,什么时候写
write port handshaking 写握手选项
write acknowledge 写标志位 分为高有效和低有效
overflow 溢出标志 分为高有效和低有效
read port handshaking 写握手选项
valid flag 有效读 分为高有效和低有效
underflow flag 读空标志位 分为高有效和低有效 - Programmable Flags 自定义标志位 用于自定义什么时候写满和什么时候读空的标准,可编程满或空,推荐使用可编程满或空指示信号,用于保证整包数据写入,避免数据包内的数据丢失。为保证fifo运行的可靠性,尽量多保留些阈值。
(1)Programmable Full Type 可编程写满类型 - No Programmable Full Threshold 不采用可编程满
- Single Programmable Full Threshold Constant 单个门限值,将门限确认和失效门限设置为同一个值
- Multiple Programmbale Full Threshold Constants 单独控制PROG_FULL_THRESH_ASSERT和PROG_FULL_THRESH_NEGATE
(2)Full Threshold Assert Value 可编程满门限确认,可选端口,用于设置可编程满标记使用的确认门限,可以在复位时动态设置;拉高prog_full信号当FIFO中数据的数量大于这个值的时候。
(3) Full Threshold Negate Value 可编程满门限失效,可选端口,用于设置可编程满标记使用的失效门限,可以在复位时动态设置;拉低prog_full信号当FIFO中数据的数量小于这个值的时候。
(4)Programmable Empty Type 可编程空门限,可选端口,这个信号用于输出使可编程空标记(prog_empty)确认或失效的门限值,门限值可以在复位期间动态设置。 - No Programmalbe Empty Threshold 不采用可编程读空
- Single Programmable Empty Threshold Constant 可编程空门限,将确认门限和失效门限设置为同一个值,用PROG_EMPTY_THRESH
- Multiple Programmbale Empty Threshold Constants 单独控制PROG_EMPTY_THRESH_ASSERT和PROG_EMPTY_THRESH_NEGATE
(5) Empty Threshold Assert Value 可编程空门限确认,可选端口,用于设置可编程空标记使用的确认门限,可以在复位时动态设置;prog_empty拉高,当FIFO中存的数据数量小于这个值。
(6) Empty Threshold Negate Value可编程空门限失效,可选端口,用于设置可编程满标志使用的失效门限,可以在复位时动态设置;prog_empty拉低,当FIFO中存的数据数量大于这个值。
四、Data Counts:数据数量
尽量不采用读/写计数,因为它采用二级制划分存储容量,会导致区分的颗粒度较大。
五、Summary 总结
对FIFO进行读写的逻辑设计:
module fifo_read_write(
input wire sclk ,
input wire rst_n ,
input wire [7:0] datain ,
input wire data_v ,
input wire r_flag ,
output wire [7:0] dout
);
wire almost_full,wr_ack,overflow,almost_empty,valid,underflow,prog_full,prog_empty;
wire w_full,r_empty;
wire wr_en;
wire rd_en;
assign wr_en = (~w_full) & data_v;
assign rd_en = (~r_empty) & r_flag;
fifo_generator_0 U1(
.clk ( sclk ) ,
.srst ( rst_n ) ,
.din ( datain ) ,
.wr_en ( wr_en ) ,
.rd_en ( rd_en ) ,
.dout ( dout ) ,
.full ( w_full ) ,
.almost_full ( almost_full ) ,
.wr_ack ( wr_ack ) ,
.overflow ( overflow ) ,
.empty ( r_empty ) ,
.almost_empty ( almost_empty ) ,
.valid ( valid ) ,
.underflow ( underflow ) ,
.prog_full ( prog_full ) ,
.prog_empty ( prog_empty )
);
endmodule
testbench
`timescale 1ns/1ns
module tb_fifo_read_write;
reg sclk,rst_n;
reg data_v;
reg r_flag;
reg [7:0] datain;
wire [7:0] dout;
initial begin
sclk = 0;
rst_n = 1;
#10000;
rst_n = 0;
end
initial begin
data_v = 0;
datain = 0;
r_flag = 0;
#20000;
send_data();
#60;
read_data();
end
always #10 sclk = ~sclk;
fifo_read_write tb_fifo_read_write(
.sclk ( sclk ) ,
.rst_n ( rst_n ) ,
.datain ( datain ) ,
.data_v ( data_v ) ,
.r_flag ( r_flag ) ,
.dout ( dout )
);
task send_data;
integer i;
begin
for(i=0; i<400; i=i+1)
begin
@(posedge sclk)
data_v = 1'b1;
datain = i;
end
@(posedge sclk)
data_v = 1'b0;
datain = 0;
end
endtask
task read_data;
integer i;
begin
for(i=0;i<259;i=i+1)
begin
@(posedge sclk)
r_flag = 1'b1;
end
@(posedge sclk)
data_v = 1'b0;
end
endtask
endmodule
仿真波形图: