FIFO是一种前辈先出数据缓存器,它与普通存储器的差异是没有外部读写地址线,利用起来非常大略,缺陷是只能顺序读写,而不能随机读写。
2. 利用场景
数据缓冲:也便是数据写入过快,并且间隔韶光长,也便是突发写入。那么通过设置一定深度的FIFO,可以起到数据暂存的功能,且使得后续处理流程平滑。

时钟域的隔离:紧张用异步FIFO。对付不同时钟域的数据传输,可以通过FIFO进行隔离,避免跨时钟域的数据传输带来的设计和约束上的繁芜度。比如FIFO的一端是AD,另一端是PCI;AD的采集速率是16位100KSPS,每秒的数据量是1.6Mbps。而PCI总线的速率是33MHz,总线宽度是32位
用于不同宽度的数据接口。例如单片机是8位,DSP是16。
3. 分类
同步FIFO:指读时钟和写时钟是同一个时钟
异步FIFO:指读写时钟是不同的时钟。
4. FIFO的常见参数
FIFO的宽度:即FIFO一次读写操作的数据位;
FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个旗子暗记,以阻挡FIFO的写操作连续向FIFO中写数据而造成溢出(overflow)。
空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个旗子暗记,以阻挡FIFO的读操作连续从FIFO中读出数据而造成无效数据的读出(underflow)。
读时钟:读操作所遵照的时钟,在每个时钟沿来临时读数据。
写时钟:写操作所遵照的时钟,在每个时钟沿来临时写数据。
5. FIFO设计
5.1 空满标志天生
FIFO设计的关键是产生可靠的FIFO读写指针和天生FIFO空/满状态标志。
当读写指针相等时,表明FIFO为空,这种情形发生在复位操作时;或者当读指针读出FIFO中末了一个字后,追遇上了写指针时,这时FIFO处于满的状态。
为了区分到底是满状态还是空状态,可以采取以下方法:
方法1:在指针中添加一个额外的位(extra bit),当写指针增加并超越末了一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对付深度为2^n的FIFO,须要的读/写指针位宽为(n+1)位,如对付深度为8的FIFO,须要采取4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。
如果两个指针的MSB不同,解释写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
如果两个指针的MSB相同,则解释两个指针折回的次数相等。别的位相等,解释FIFO为空;
5.2 异步FIFO的设计还要把稳跨时钟域问题
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很随意马虎涌现问题,由于采取二进制计数器时所有位都可能同时变革,在同一个时钟沿同步多个旗子暗记的变革会产生亚稳态问题。而利用格雷码只有一位变革,因此在两个时钟域间同步多个位不会产生问题。以是须要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行比拟,作为空满状态的检测
5.3 gray码如何判断空满
对付“空”的判断:依然依据二者完备相等(包括MSB);
对付“满”的判断:如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,别的位皆相同,不能说它为满。因此不能纯挚的只检测最高位了,在gray码上判断为满必须同时知足以下3条:
wptr和同步过来的rptr的MSB不相等,由于wptr必须比rptr多折回一次。
wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同解释多折回一次,111相同代表同一位置。
剩下的别的位完备相等
6. 同步FIFO实现
代码的核心部分紧张是data_count,并且full 旗子暗记是当data_count == DATA_DEPTH时拉高,除了data_count之外,还可以同过判断r_ptr和w_ptr两个指针是否相等来判断空,满旗子暗记
ssign empty = (w_ptr == r_ptr) ? 1 : 0;
assign full = (w_ptr[2:0] == r_ptr[2:0] && (w_ptr[3] == ~r_ptr[3])) ? 1 : 0;
这里还有一个把稳的点,也便是w_ptr++会越界,同步FIFO就算用data_cnt来判断空满条件,在存数据和写数据时还是该当用w_ptr来表示,如果直接用data_cnt来表示,那么设计的便是一个栈而不是FIFO了。
module syn_fifo(
clk,
rst_n,
data_in,
w_en,
full,
data_out,
r_en,
empty);
parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 16;
parameter ADDR_WIDTH = 4;
input wire clk, rst_n;
input wire [DATA_WIDTH-1:0] data_in;
input wire w_en, r_en;
output wire empty, full;
output reg [DATA_WIDTH-1:0] data_out;
reg [ADDR_WIDTH : 0] data_count;
reg [ADDR_WIDTH-1 : 0] w_ptr, r_ptr;
reg [DATA_WIDTH-1 : 0] mem[0 : DATA_DEPTH-1];
assign empty = (data_count == 'd0) ? 1 : 0;
assign full = (data_count == DATA_DEPTH) ? 1 : 0; //data_count == DATA_DEPTH
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
data_count <= 'd0;
else if (w_en && r_en)
data_count <= data_count;
else if (!full && w_en)
data_count <= data_count + 1'b1;
else if (!empty && r_en)
data_count <= data_count - 1'b1;
else
data_count <= data_count;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
mem[w_ptr] <= 'd0;
else if (!full && w_en)
mem[w_ptr] <= data_in;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
w_ptr <= 'd0;
else if (w_en && !full)
w_ptr <= w_ptr + 1'b1;
else
w_ptr <= w_ptr;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
r_ptr <= 'd0;
else if (r_en && !empty)
r_ptr <= r_ptr + 1'b1;
else
r_ptr <= r_ptr;
end
always @ (posedge clk or negedge rst_n) begin
if (!rst_n)
data_out <= 'd0;
else if (!empty && r_en)
data_out <= mem[r_ptr];
end
endmodule
7. 异步FIFO实现
设计难点:
1.跨时钟域数据比较,须要用到同步器,减少亚稳态的通报
2.用到gray码,进一步减少亚稳态的产生
3.gray码相等旗子暗记的比较 空:两个gray码相等 满:高两位相反,别的位相同。
4.指针计数须要比ADDR的位宽多一位,这一点和同步FIFO的设计是一样的。
module asyn_fifo(
clk_w,
clk_r,
rst_n,
r_en,
w_en,
data_in,
data_out,
full,
empty
);
input wire clk_r, clk_w, rst_n;
input wire r_en, w_en;
input wire [7:0] data_in;
output wire full, empty;
output reg [7:0] data_out;
parameter DATA_DEPTH = 8;
parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 3;
reg [3:0] w_ptr, r_ptr;
reg [7:0] mem[DATA_DEPTH-1 : 0];
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n)
w_ptr <= 'd0;
else if (w_en && !full)
w_ptr <= w_ptr + 1'b1;
else
w_ptr <= w_ptr;
end
wire [3:0] w_ptr_gray, r_ptr_gray;
assign w_ptr_gray = w_ptr ^ (w_ptr >> 1);
assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
//
reg [ADDR_WIDTH:0] rd1_wp, rd2_wp;
always @ (posedge clk_r or negedge rst_n) begin
if (!rst_n) begin
rd1_wp <= 'd0;
rd2_wp <= 'd0;
end else begin
rd1_wp <= w_ptr_gray;
rd2_wp <= rd1_wp;
end
end
assign empty = (rd2_wp == r_ptr_gray) ? 1 : 0;
always @ (posedge clk_r or negedge rst_n) begin
if (~rst_n)
r_ptr <= 'd0;
else if (r_en && !empty)
r_ptr <= r_ptr + 1'b1;
else
r_ptr <= r_ptr;
end
//wire [ADDR_WIDTH:0] r_ptr_gray;
assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
reg [ADDR_WIDTH:0] wd1_rp, wd2_rp;
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n) begin
wd1_rp <= 'd0;
wd2_rp <= 'd0;
end
else begin
wd1_rp <= r_ptr_gray;
wd2_rp <= wd1_rp;
end
end
assign full = ({(~wd2_rp[ADDR_WIDTH:ADDR_WIDTH-1]),wd2_rp[ADDR_WIDTH-2:0]} == w_ptr_gray) ? 1:0;
always @ (posedge clk_w or negedge rst_n) begin
if (~rst_n)
mem[w_ptr] <= 'd0;
else if (!empty && rd_en)
mem[w_ptr] <= data_in;
end
always @ (posedge clk_r or negedge rst_n) begin
if (~rst_n)
data_out <= 'd0;
else if (!full && w_en)
data_out <= mem[r_ptr];
end
endmodule
8. 对FIFO进行约束
set_false_path
在设计中,不须要知足setup/hold时序的数据路径须要设置成false path
set_disable_timing:可以使库单元的韶光弧(timing arc)无效
总的来说,set_false_path 只对data path起浸染, EDA 工具还会剖析打算这条时序路径, 只是不报出来是否有时序违例。
set_disable_timing 对timing arc起浸染,完备不去剖析这条timing arc
9. 关于异步FIFO最小深度的打算
FIFO仅在数据突发时才有效,不该用与连续的数据输出和输入。如果存在连续的数据流,那么所须要的FIFO大小因该是无限的。因此须要知道突发速率,突发大小,频率等,才能确定FIFO的深度。
最小深度的打算流程
确定读时钟fr和写时钟的频率fw, 一样平常情形fw>fr的
根据fr和fw打算读写一次数据的周期Tr 和Tw,根据T= 1/f
根据突发写长度的大小,打算这么多数据须要写多少韶光 tw = Twlen
根据写的韶光tw打算读了多少数据 n = tw/Tr
FIFO的最小深度即是 len-n
9.1 写时钟快于读时钟,写和读的过程中没有空闲周期
剖析过程:
写时钟周期Tw = 1000/80 ns = 12.5ns;同理读时钟周期为20ns;
突发写长度为120个数据,写120个数据耗时120 12.5 = 1500ns;
1500ns韶光内读出数据1500/20ns = 75个;
故最小FIFO深度为120 - 75 = 45;
9.2 写时钟频率大于读时钟频率,但在读写的过程中存在空闲周期
剖析:
写时钟周期T_A = 12.5ns,读时钟周期为T_B = 20ns;
两个写时钟写一个数据,也便是写一个数据须要韶光2T_A = 25ns,那么由于突发写数据个数为120个,写这么多数据须要韶光120 25ns = 3000ns;
4个读时钟周期读一个数据,因此读一个数据须要韶光80ns,3000ns读了3000/80 = 37.5个数据(0.5不算一个数据,没读完全),约即是37个数据。
以是,FIFO的最小深度为120 - 37 = 83;
9.3 写时钟慢于读时钟,且读写过程中没有空闲周期
剖析:
这种情形下永久也不会发生数据丢失的情形;
fifo的深度为1
9.4 写时钟频率小于读时钟频率,但读写过程中存在空闲周期
剖析:
写时钟周期1000/30 ns = 100/3 ns;读时钟周期 20ns;
写一个数据须要2个时钟,也便是200/3 ns;读一个数据须要4个时钟,也便是80 ns;
写120个数据须要韶光8000ns,这段韶光内读出数据8000/80 = 100个;
因此,FIFO的最小深度为120 - 100 = 20;
9.5 读写时钟速率相同,且无空闲时钟
剖析:
如果读写时钟之间没有相位差,则不须要FIFO就可以进行读写;
如果二者存在相位差,只须要FIFO的深度为1即可。
9.6 读写时钟频率同等,但在读写过程中存在空闲周期
剖析:
两个时钟写一个数据,须要韶光40ns;
4个时钟读一个数据,须要80ns;
由于突发长度为120,须要12040 = 4800ns写完;这段韶光读出数据个数:4800/80 = 60;
以是,FIFO最小深度为120 - 60 = 60;
9.7 特定条件下,最坏情形剖析FIFO最小深度
首先,早年提可知,写频率即是读频率;
其次,读写可以在如下限定下的任意时候发生:
为了得到更安全的FIFO深度,我们须要考虑最坏的情形,以防数据丢失;
对付最坏的情形,写入和读取之间的数据速率之间的差异该当是最大的。因此,对付写操作,应考虑最大数据速率,对付读操作,应考虑最小数据速率。从上表可以看出,最快的写数据速率该当为第4种情形,写操作在最小的韶光内完成;
由于突发写长度为160,以是160个时钟写160个数据;
由于读速率为10个时钟读8个数据,因此一个数据须要10/8个时钟;
以是160个时钟读了1608/10 = 128个数据;
以是FIFO的最小深度为160-128=32.
9.8 条件拐弯抹角的给出,须要自己提取关键信息
如果clkA = 25MHz,则CLKB = 100MHz;
TA= 40ns, TB = 10ns;
en_B = 10040 = 4000ns;占空比为1/4;
我们认为B为写时钟,写使能韶光为4000/4 = 1000ns,则突发写长度为1000/10 = 100个数据;
在1000ns内读出数据为1000/40 = 25个数据,以是FIFO最小深度为100 - 25 = 75
9.9 其它情形
输入时钟频率炜250MHz,输入数据率8Gbps,输出的时钟频率200MHz,输出的数据率为5Gbps,单位韶光内输入的数据总量为4Gb,在担保数据不丢失的情形下,最少须要多大的缓冲空间,并给出剖析步骤。
解析:解答1,不考虑两个包之间的背靠背的情形
4Gb/8Gbps 5Gbps = 2.5Gb,因此缓冲空间=4Gb-2.5Gb = 1.5Gb;
解答2:考虑背靠背的情形
突发长度=8G, 写该当是0440的情形,读用最慢的情形,2.5, 2.5,0.5, 2.5,因此FIFO= 8-3 = 5G
9. Vivado FIFO IP核利用
在IP Catalog中搜索FIFO,会涌现各种各样的FIFO,一样平常选择FIFO generator。
点击IP之后,会涌现FIFO配置的一些选项,包括Basic Native ports, flag等
在Basic中我们可以掌握FIFO的接口形式和FIFO的类型
FIFO 的接口分为两类,一类是 Native 接口,这类接口利用比较大略,另一类是 AXI 协议接口,这类协议口线比较多,操作相对繁芜。
FIFO 的类型紧张差异:1.读写是否利用一个时钟 2.利用何种硬件资源
个中差异1紧张是通过common clk和 independent clk来确定,也便是同步FIFO和异步FIFO
差异2硬件资源:分为3种。BRAM:即块RAM资源,这是FPGA内嵌的一种主要的专用RAM资源,可以在读写两端利用不同的数据宽度,可以利用 ECC (一种数据校验特性),支持 First-World Fall Through ,以及支持动态缺点注入。;分布式RAM:Distributed RAM,即将FPGA中的LUT用作RAM,仅支持 First-World Fall Through 功能;专用FIFO,专用FIFO会供应很小的延迟。BRAM 是一种比较主要的资源,如果设计的 FIFO 对延时不敏感,可以利用分布式的 RAM 以节约 BRAM 资源。
Stand FIFO 和 First Word Fall Through的差异
standard FIFO 读取数据时会延迟一个周期,也即会在使能旗子暗记拉高后延迟一个周期才有数据输出,而First word fall through会和使能旗子暗记同时输出。造成这种差异的缘故原由在于FWFT模式下,第一个写入的数据将从RAM中提前读出到数据线。
FIFO 的复位利用的高电平复位,如果设计中系统复位旗子暗记是低电平有效的,那么不要忘却要将系统复位电平取反后再接入 FIFO 复位电平。一样平常我们在同步系统设计中利用异步复位。
status flag
包括 almost Full/Empty 旗子暗记,这两个旗子暗记,顾名思义,便是在 FIFO 险些要满或者险些要空的情形下置起,所谓的“险些“便是指还差一个数据满或者空
这个页面上还供应握手选项,但一样平常我们在低级设计中不会须要 FIFO 具有这种“交互”特性,本色上 AXI 协议接口也会供应握手特性。
第四个页面 Data Count,顾名思义便是供应一个旗子暗记来表示当前 FIFO 中的数据总数
在顶层文件实例化IP
在IP Source中打开Instation Template目录下的veo文件,里面就有实例化的例子
异步FIFO IP的利用
把稳同步化synchronization stages
这个值用于表示FIFOempty拉低的韶光长度,同时要把稳FIFO的读一定要有empty掌握,并且创造empty并不是一写入数据就拉低的。
10. FIFO IP利用把稳事变
如果读写位宽不一样的情形,比如写位宽8, 读位宽32,那么当写入三次是, empty旗子暗记仍旧为高电平,也就意味着是读不出数据的。
FIFO的复位旗子暗记是高电平有效
standard FIFO 和FWFT的差异便是读的时候须要延时一个周期和不须要延时
output register:嵌入式输出寄存器可用于增加性能并向宏添加流水线寄存器,紧张用于改进时序情形,但是只对Standard FIFO模式有用,添加output register, 读延时会增加1个周期
Valid: This signal indicates that valid data is available on the output bus (dout).因此在FWFT模式下,只要FIFO有数据,valid旗子暗记就会拉高;而在Standard FIFO模式下,只有在读旗子暗记使能之后,valid旗子暗记才会拉高
原文链接:https://blog.csdn.net/mu_guang_/article/details/108647477