FIFO相关问题汇总(一)
1. 什么是FIFO
FIFO是英文First In First Out的缩写,是一种先进先出的数据缓存器;与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,缺点是只能顺序写入数据,顺序读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
2. FIFO用在什么地方
FIFO一般用于不同时钟域之间的数据传输,可以采用FIFO来作为数据缓冲。另对于不同宽度的数据接口也可以用FIFO。
3. FIFO的分类和区别
根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO:
- 同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。
- 异步FIFO是指读写时钟不一致,读写时钟是相互独立的。
4. FIFO的参数
- 宽度(THE WIDTH):FIFO一次读写操作的数据位,如8,16,32位等。FIFO的宽度在单片成品IC中是固定的,也有可选择的(实现时宽度可配置)。
- 深度(THE DEEPTH):指的是FIFO可以存储多少个N(FIFO宽度)位的数据。FIFO的深度可大可小,在实际工作中,其数据的满/空标志可以控制数据的继续写入或读出。
- 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
- 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。
- 读指针:指向下一个读出地址,读完后自动加1。
- 写指针:指向下一个写入地址,写完后自动加1。
- 满标志:FIFO已满或将要满时由FIFO的状态电路送出一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成的溢出(overflow)。
- 空标志:FIFO已空或将要空时由FIFO的状态电路送出一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
5. FIFO设计难点
FIFO设计的难点/核心在于怎么判断FIFO的空/满状态。为了保证数据的正确写入或读出,而不发生溢出或读空的状态,必须保证FIFO在满的情况下,不能进行写操作,在空的状态下不能进行读操作。在用到触发器的设计中,不可避免会遇到亚稳态问题。亚稳态无法彻底消除,只能想办法将其发生的概率降到最低,其一方法是使用格雷码(Gray Code),其二是使用冗余触发器。
6. 异步FIFO中为什么要用格雷码
异步FIFO中由于读写时钟不同,读写指针的数据涉及到跨时钟域传输(CDC,Clock Domain Conversion),此时数据容易产生亚稳态。格雷码是一种相邻编码只有一位不同的编码方式,格雷码编码方式可以减少相邻数据编码的跳变位,这种编码方式有效降低了亚稳态发生的概率,使得数据传输正确。
7. 如何判断同步/异步FIFO的空满状态
同步fifo中一般使用严格的空满判断:w_ptr
== r_ptr
,且读写回环标志位相同时为空;w_ptr
== r_ptr
,且读写回环标志位不同时为满。
在异步fifo中一般要做悲观的空/满判断:设定了FIFO容量的75%作为上限,设定FIFO容量的25%为下限。当方向标志超过上下限阈值时便输出空/满标志,其实这时输出空/满标志时FIFO并不一定真的空/满。
8. 对于异步FIFO,如何处理空满时的同步问题
- 将格雷码转换成二进制码进行空满判断:首先将读写指针的格雷码转化位二进制码,当读写指针(不包括最高标志位)相等时,根据读写指针的最高标志位可以分为两种情况,当最高标志位不同时FIFO为写满状态,最高位相同时为读空状态。
- 直接通过格雷码进行空满判断:读指针的格雷码和被同步到读时钟域的写指针每一位都相同,此时为读空状态。写指针的格雷码和被同步到写时钟域的读指针的高两位(可以简单理解为二进制的最高标志位)不同,其余各位完全相同时,此时为写满状态。
9. FIFO中为什么需要同步读写指针
为了对FIFO满/空状态进行判断。当进行读操作时读指针加1,当进行写操作时写指针加1。此外需要一位额外的标志位来表示FIFO状态,当读/写到最大的数值时,标志位翻转;这样当读指针等于写指针,且标志位相同时,FIFO为空状态;当读指针等于写指针,且标志位不同时,FIFO为满状态。
10. 为什么在写时钟域判断是否满,而在读时钟域判断是否空
如果在写时钟判空:
- 当w_clk(写时钟)比较快时,r_ptr(读指针)很快就能同步到w_clk时钟域。在同步过程中认为不会再有读数据操作。但是由于r_ptr同步过来仍需要时间,如果被同步的r_ptr刚好是一个符合空条件的值,那么在同步过来这段时间内,r_ptr仍是上一个符合非空条件的值。也就是说此时FIFO实际上已为空,但是还是会报非空,影响FIFO正常工作。
- 当w_clk比较慢时,在r_ptr同步到w_clk时钟域之前,可能又会有读数据的操作。如果同步的这个r_ptr不符合FIFO条件,那么在r_ptr同步过来之后就会判断为非空。然而在将r_ptr同步过来之前的这段时间里,由于又有了读操作,r_ptr已经变化了,此时FIFO可能是空的。也就是说此时FIFO实际已经空了,但是还是会报非空,影响FIFO正常工作。
如果在读时钟判满:
- 当r_clk(读时钟)比较快时,w_ptr(写指针)很快就能同步到r_clk时钟域,在同步过程中认为不会再有写数据的操作。但是由于w_ptr同步过来仍需要时间,如果被同步的w_ptr刚好是一个符合满条件的值,那么在同步过来之前的这段时间内,w_ptr仍是上一个符合非满条件的值。也就是说此时FIFO实际已经满了,但是还是会报非满,影响FIFO的正常工作。
- 当r_clk比较慢时,在w_ptr同步到r_clk时钟域之前,可能又会有写数据的操作。如果同步的这个w_ptr不符合FIFO满条件,那么在w_ptr同步过来之后就会判断为非满。然而在将w_ptr同步过来之前的这段时间里,由于又有了写操作(写时钟较读时钟快),w_ptr已经变化,此时FIFO有可能是满的。也就是说此时FIFO实际已经满了,但是还是会报非满,影响FIFO正常工作。
当在w_clk时钟域判断空或者在r_clk时钟域判断满时,会出现以上几种影响FIFO正常工作的情况(简单来说就是,异步FIFO判满/空需要同步指针,以上两种判断方式会产生被同步的指针在同步的时间内发生变化,造成空/满边界误判),所以我们是在w_clk时钟域判断是否为满,而在r_clk时钟域判断是否为空。
11. FIFO为什么会有假满假空现象,会对系统产生什么影响
无论是w_clk和r_clk谁快谁慢,都可能产生假空假满现象。之所以会产生假空假满现象,主要是因为w_ptr和r_ptr的同步问题导致的,因为无论是将w_ptr同步到r_clk时钟域还是将r_ptr同步到w_clk时钟域都有一定的延迟。
假设w_clk比r_clk快:
- 假满现象:当w_clk比较快时,r_clk很快就能同步到w_clk,在同步过程中认为不会再有读数据的操作。但是如果同步的这个r_ptr刚好是FIFO满之后变化的新值,也即FIFO满后又有了读数据操作,此时FIFO已经不满了。那么由于在r_ptr同步过来的这段时间,w_clk时钟域的r_ptr的值仍然是上一次同步过来的,也就是符合满条件的值,此时就会出现假满现象。
- 假空现象:当w_clk比较快时,在将w_ptr同步到r_clk之前,可能又会有写操作。如果同步的这个w_ptr刚好符合条件空条件,那么在w_ptr同步过来之后就会判断为空。在将w_ptr同步过来这段时间里,由于又进行了写操作,w_ptr其实已经变化了,此时FIFO是不空的,此时就出现了假空现象。
假设w_clk比r_clk慢:
- 假满现象:当w_clk比较慢时,在r_ptr同步到w_clk时钟域之前,可能又会有读数据操作。如果同步的这个r_ptr刚好符合FIFO满条件,那么在r_ptr同步过来之后就会判断为满。在将r_ptr同步过来之前的这段时间里,由于又有了读操作,r_ptr其实已经变化了,此时FIFO是不满的,此时就会出现了假满现象。
- 假空现象:当r_clk比较快时,w_ptr很快就能同步到r_clk时钟域,在同步过程中认为不会再有写数据的操作。但是如果同步的这个w_ptr刚好是FIFO空之后变化的新值,也即FIFO空之后又有了写数据操作,此时FIFO已经不空了,那么由于在w_ptr同步过来之前的这段时间,r_clk时钟域的w_ptr的值仍是上一次同步过来的,也就符合空条件的值,此时就会出现假空现象!
假空假满都不影响FIFO正常工作,只影响其性能。因为假满就是提前告诉写数据端不要再写数据,由于这段时间不能写数据,所以w_ptr不变,当过一会r_ptr同步过来时,再由w_ptr和r_ptr判断的结果就是FIFO不满了,就又可以写数据了。而假空是告诉读数据端不要再读数据了,由于这段时间不能再读数据,所以r_ptr不变,当过一会w_ptr同步过来时,再由w_ptr和r_ptr判断的结果就是FIFO不空了,就又可以读数据了。
12. FIFO深度如何选择
在一个具体的应用中也不可能由一些实际的参数算出精确的所需FIFO深度为多少。在写速度大于读速度的理想状态下FIFO的深度可以设为1,在实际用到的FIFO深度往往要大于计算值。当读数据速度小于写数据速度,就需要使用FIFO将数据缓存下来。FIFO开大了造成资源浪费,开小了造成数据丢失,因此需要选择合适大小的FIFO。一般来说根据电路的具体情况,在兼顾系统性能和FIFO成本的情况下估算一个大概的宽度和深度就可以了。
当写操作速度大于读操作速度时,若写操作是连续的数据流,那么再大的FIFO都无法保证数据不溢出。因此可以认为这种情况下写数据的传输是突发burst
的,即写操作是不连续的。要确定FIFO的深度,关键在于计算出在突发读写这段时间内有多少个数据没有被读走,即FIFO的最小深度就等于没有被读走的数据个数。
12.1 写时钟频率大于读时钟频率,且读和写过程中没有空闲周期
例:写数据时钟率fa
=80MHz,读数据时钟频率为fb
=50MHz,突发长度(burst transfer)
= 120,在突发传输过程中,数据都是连续读写的。最小FIFO深度为:
120
−
1
/
80
∗
120
/
(
1
/
50
)
=
45
120-1/80*120/(1/50)=45
120−1/80∗120/(1/50)=45
注:可以看出此情形FIFO长度由突发长度决定。
12.2 写时钟频率小于读时钟频率,且读和写过程中没有空闲周期
此时FIFO最小深度应比1中略大即可。
12.3 写时钟频率大于读时钟频率,但读写过程中存在空闲周期
例:写数据时钟频率fa
=80MHz,读数据频率fb
=50MHz,突发长度
=120,连续写入之间的空闲周期为1,连续读取之间的空闲周期为3。最小FIFO深度:
120
−
(
1
/
80
∗
(
1
+
1
)
∗
120
)
/
(
1
/
50
∗
(
3
+
1
)
)
=
82.5
≈
83
。
120-(1/80*(1+1)*120)/(1/50*(3+1))=82.5≈83。
120−(1/80∗(1+1)∗120)/(1/50∗(3+1))=82.5≈83。
12.4 写时钟频率小于读时钟频率,且读写过程中没有空闲周期
例:写数据频率fa
=30MHz,读数据频率fb
=50MHz,突发长度
=120,在突发传输过程中,数据都是连续读写的。由于读数据比写数据要快,这种情况下永远也不会发生数据丢失,因此FIFO只起到时钟域的作用,FIFO的最小深度为1即可。
注:与场景12.2相似。
12.5 写时钟频率大于读时钟频率,给定wr_end和rd_end的占空比
例:写数据时钟频率fa
=80MHz,读时钟频率fb
=50MHz,突发长度
=120,写使能信号占整个burst时间比重为1/2,读使能信号占整个burst时间比重为1/4。与场景12.3中相似,4中数据项将在2时钟周期内写入,而一个数据项将在4个时钟周期内读取。因此FIFO的最小深度也为83。
12.6 写时钟频率小于读时钟频率,给定wr_enb和rd_enb的占空比
例:写数据时钟频率fa
=40MHz,读数据时钟频率fb
=50MHz,突发长度
=120,连续写入之间空闲周期为1,连续读取之间空闲周期为3。最小FIFO深度:
120
−
(
1
/
40
∗
(
1
+
1
)
∗
120
)
/
(
1
/
50
∗
(
1
+
3
)
)
=
45
120-(1/40*(1+1)*120)/(1/50*(1+3))=45
120−(1/40∗(1+1)∗120)/(1/50∗(1+3))=45
12.7 写时钟频率等于读时钟频率,且读写过程中没有空闲周期
例:写时钟频率fa=50MHz,读时钟频率fb=50MHz,突发长度=120,读和写都没有空闲周期,这意味着突发中的所有相都将以连续的时钟周期写入和读取。如果此CLKA和CLKB之间没有相位差,则不需要FIFO;如果CLKA和CLKB之间存在相位差,深度为1的FIFO就足够了。
12.8 写时钟频率等于读时钟频率,给定wr_end和rd_end的占空比
例:写时钟频率fa
=50MHz,读时钟频率fb
=50MHz,突发长度
=120,连续写入之间的空闲周期为1,连续读取之间的空闲周期3。最小FIFO深度:
120
−
(
1
/
50
∗
(
1
+
1
)
∗
120
)
/
(
1
/
50
∗
(
1
+
3
)
)
=
60
120-(1/50*(1+1)*120)/(1/50*(1+3))=60
120−(1/50∗(1+1)∗120)/(1/50∗(1+3))=60
12.9 给出数据在一段时间内的读写速率,读写完全随机
这种情况我们需要考虑最坏的一种情况避免数据丢失。对于最坏的情况,读写之间的数据速率差异应该是最大的。因此,对于写操作,应该考虑最大数据速率,对于读操作,应该考虑最小数据速率。
总结FIFO深度求解的要点:
- 在求解之前需要验证在允许的最大时间长度内写入的数据量是否等于读出的数据量,保证有解。
- 求FIFO深度需要考虑最坏的情形,读写的速率应该相差最大,也就是说找出最大的写速率和最小的读速率。
- 不管什么场景,要确定FIFO深度,关键在于计算出在突发读写这段时间内有多少个数据没有被读走。
- 由于FIFO空满标志位的判断延迟,在实际应用中需要预留一些余量。
13.乒乓FIFO是怎么工作的(了解)
就像打乒乓球一样,一个球(数据流),两个拍子(缓存),两个拍子相互击球(轮流读写数据,写1读2,写2读1)这样就可以做到球不停一直移动(数据流不会停,数据无丢失)。
第一段乒乓缓冲周期:将数据流缓存到数据缓冲模块1。
第二段乒乓缓冲周期:将数据流缓存到数据缓冲模块2,并从数据缓冲模块1读取数据送入数据流运算处理模块。
第三段乒乓缓冲周期:将数据流缓存到数据缓冲模块1,并从数据缓冲模块2读取数据送入数据流运算处理模块。
接下来就是将第二段,第三段乒乓缓冲周期的循环往复。乒乓FIFO是一种典型的空间换效率设计思路。
14.单口RAM实现同步FIFO的设计方法
在内存空间中开辟出一段固定大小的内存用于存储数据,每一个数据所占的bit位称为位宽
,这段内存空间中数据的总数称为深度
。在这段内存空间中,每一个数据分配一个地址,可以用8bit的地址来表示所有的数据,外部信号通过固定的时钟节拍,通过使能信号及地址信号来读取RAM中特定位置的数据或者向RAM中特定位置写入数据。
参考文献:
https://blog.csdn.net/moshanghongfeng/article/details/108640548
https://zhuanlan.zhihu.com/p/166177480
https://blog.csdn.net/qq_39507748/article/details/122008520
https://blog.csdn.net/buzhiquxiang/article/details/103287220
https://blog.csdn.net/qq_44816673/article/details/116330945
https://blog.csdn.net/zhangningning1996/article/details/107373027