目录
异步FIFO是一种常用的跨时钟域的设计,应用非常广泛。在平常设计中,我们都是调用IP核,设置深度和宽度就可以用了,但是很少关注资源利用率,即使用最小深度来达到设计效果。
异步FIFO原理及其实现
在进行FIFO最下深度计算之前,需要知道,并不是所有跨时钟域情况都可以使用异步FIFO。
假设写入时钟比读取时钟块,并且数据连续写入不停止,就算也连续读,无论FIFO设计多大,都会被写满,那么FIFO的深度就无穷大了。所以使用异步FIFO的情况是:数据突发写入(长度称为Burst length)。
突发数据长度(Burst Length)
因为输入不能连续写入,这样会导致FIFO无穷大,但是数据可以集中在一段时间内写入,然后停止一段时间 ,等待读取数据一段时间 Ta 后,然后再写入数据(不会导致数据丢失)。突发的意思就是数据集中在一段时间写入,然后等待数据读取一段时间,才能继续写入,使得数据不丢失。
假设需要从一个传感器采集一些数据到处理器处理,传感器采集数据的速度大于处理器处理数据的速度,这时候就需要使用异步FIFO。在最小深度的情况下,传感器开始采集数据写入到FIFO,同时处理器开始读取数据,当传感器采集完一次数据(n个)后,等待一段时间 Ta,处理器也读取完数据,这时候的FIFO为空。在 Ta 时间内不允许写入数据,这就叫突发。假设写入需要10s,读取需要30s,那么在30s之后才能再次向FIFO写入数据,不然就会导致数据丢失。 这个n就是突发数据长度(Burst length)。
常见FIFO深度计算情况
一、写时钟 > 读时钟(写比读快)
写时钟大于读时钟是异步FIFO最常用的使用情况
1、读写都没有空闲周期
假设:
写入时钟 wr_clk = 50MHZ,
读取时钟 rd_clk = 20MHZ,
突发数据长度 Burst length = 100,
分析:
写入一个数据需要 1/50MHZ = 20ns;写入100个数据需要 100 * 20ns = 2000ns;
在2000ns内,只能读取 2000/(1/20MHZ) = 40个数据;
所以FIFO_DEPTH(min) = 100 - 40 = 60。
公式:FIFO_DEPTH >= Burst length - ( Burst length * rd_clk / wr_clk)
如果FIFO_DEPTH为小数(35.5),则取大于FIFO_DEPTH 的整数(36)
2、读写有空闲周期
假设:
写入时钟 wr_clk = 50MHZ,
读取时钟 rd_clk = 10MHZ,
写空闲周期 = a,
读空闲周期 = b,
假设 a = 2,b = 1,
突发数据长度 Burst length = 100,
因为读写都有空闲周期,写一个数据,等2个周期再写下一个数据,相当于每3个时钟周期写一个数据;而读一样,读一个数据,等待1个周期再读下一个数据,相当于每2个时钟周期读一个数据;
注意:读写空闲周期必须满足:(1+a)/wr_clk < (1+b)/rd_clk ,这样才能保证写比读快
分析:
写入一个数据需要 3*(1/50MHZ) = 60ns,写入100个数据需要 100 * 60 = 6000ns;
在6000ns内可以读取 6000/(2*1/10MHZ) = 30
所以所以FIFO_DEPTH(min) = 100 - 30= 70。
公式:FIFO_DEPTH >= Burst length - ( Burst length * rd_clk / wr_clk * (1+a) / (1+b )
如果FIFO_DEPTH为小数(35.5),则取大于FIFO_DEPTH 的整数(36)
二、写时钟 = 读时钟(写读一样快)
1、读写没有空闲周期,且相位相等
这种情况不需要设计FIFO
2、读写没有空闲周期,相位不等
设计一个深度为 1 的FIFO就足够了
3、读写有空闲周期,无相位差
假设:
写入时钟 wr_clk = 50MHZ,
读取时钟 rd_clk = 50MHZ,
写空闲周期 = a,
读空闲周期 = b,
假设 a = 1,b = 2,
突发数据长度 Burst length = 100,
分析:这种情况就相当于读写时钟变化了,根据变化后的时钟计算
因为读写都有空闲周期,写一个数据,等1个周期再写下一个数据,相当于每2个时钟周期写一个数据;而读一样,读一个数据,等2个周期再读下一个数据,相当于每3个时钟周期读一个数据;
写入一个数据需要 2*(1/50MHZ) = 40ns,写入100个数据需要 100 * 40 = 4000ns;
在4000ns内可以读取 4000/(3*1/50MHZ) = 66.6
所以所以FIFO_DEPTH(min) = 100 - 66.6 = 34。
公式:FIFO_DEPTH >= Burst length - ( Burst length * (1+a) / (1+b )
三、写时钟 < 读时钟(读比写快)
1、读写没有空闲周期
因为读比写快,所以不会发生数据丢失的情况,不需要设计FIFO
2、读写有空闲周期
假设:
写入时钟 wr_clk = 20MHZ,
读取时钟 rd_clk = 50MHZ,
写空闲周期 = a,
读空闲周期 = b,
突发数据长度 Burst length = 100,
因为读写都有空闲周期,写一个数据,等a个周期再写下一个数据,相当于每1+a个时钟周期写一个数据;而读一样,读一个数据,等b个周期再读下一个数据,相当于每1+b个时钟周期读一个数据;
1、当(1+a)/wr_clk >= (1+b)/rd_clk时
相当于 读时钟大于等于写时钟,如果没有相位差就不需要设计FIFO,如果有相位差,设计深度为1的FIFO就足够了。
2、当(1+a)/wr_clk < (1+b)/rd_clk时
相当于 写时钟大于读时钟,写比读快
假设a = 1,b = 5
因为读写都有空闲周期,写一个数据,等1个周期再写下一个数据,相当于每2个时钟周期写一个数据;而读一样,读一个数据,等5个周期再读下一个数据,相当于每6个时钟周期读一个数据;
分析:
写入一个数据需要 2*(1/20MHZ) = 100ns,写入100个数据需要 100 * 100 = 10000ns;
在10000ns内可以读取 10000/(6*1/50MHZ) = 83.3
所以所以FIFO_DEPTH(min) = 100 - 83.3= 27。
公式:FIFO_DEPTH >= Burst length - ( Burst length * rd_clk / wr_clk * (1+a) / (1+b )
四、最坏情况(背靠背)
1、背靠背
之前的写入和读取,相当于 是均匀写入和读取,而当写入操作没有说每几个时钟周期写入一个数据,而是在几个时钟周期内写入多少数据;在几个时钟周期内读出多少数据的时候。
2、背靠背计算
假设:
写数据时钟频率fa=50MHz
读数据时钟频率fb=40MHz
在写时钟周期内,每80个周期就有40个数据写入FIFO
在读时钟周期内,每10个周期可以有6个数据读出FIFO
在最坏的情形中,为了得到更安全的FIFO深度,我们需要考虑最坏情况,以防数据丢失,读写的速率应该相差最大,也就是说需要找出最大的写速率 和 最小的读速率,这样才能适配所有写入和读取的情况
可以看到,并没有给出突发长度,突发长度是40吗,其实不是,我们画个图就知道了:
写入的方式很多种,有均匀写入,有先写20再写20,方式很多。我们画出背靠背的情况
在背靠背(最坏)情况下,突发长度需要设置为 80,并且写速率设置为最大的50MHZ;而因为读时钟周期内,每10个周期可以有6个数据读出FIFO,假设均匀读出,所以读速率设置为最小的40MHZ * 6/10 = 24MHZ
由此可以根据上面各种情况分析:
写入 80个数据需要的时间: 80 * 1/50MHZ = 1600ns
在1600ns内可以读出 1600 / 1/24MHZ = 38.4个数据
所以所以FIFO_DEPTH(min) = 80- 38.4 = 42。
五、最常考(问)的情况
此处参考 https://blog.****.net/Reborn_Lee/article/details/100127937
条件不给完,需要自己计算得到各个参数(一般写比读快)
假设:
CLKA = 25MHz,CLKB = 100MHz;
Ta= 40ns, Tb = 10ns;
时钟B的使能信号 en_B = 100 * Ta,占空比为1/4;
一般写比读快,我们认为B为写时钟,写使能时间为4000/4 = 1000ns,则突发写长度为1000/10 = 100个数据;
在1000ns内读出数据为1000/40 = 25个数据,所以FIFO最小深度为100 - 25 = 75