什么时异步fifo
FIFO,即First In First Out ,是一种先进先出的数据缓存器,异步FIFO 是指读写时钟不一致,读写时钟是互相独立的。数据从一个时钟域写入FIFO缓冲区,并从另一个时钟域的同一FIFO缓冲区中读取数据,使用异步FIFO可以将数据安全地从一个时钟域传递到另一个时钟域。
异步fifo的组成
异步FIFO的核心部件就是一个 Simple Dual Port RAM ;左右两边的长条矩形是地址控制器,负责控制地址自增、将二进制地址转为格雷码以及解格雷码;下面的两对D触发器 sync_r2w 和 sync_w2r 是同步器,负责将写地址同步至读时钟域、将读地址同步至写时钟域。
异步FIFO主要由以下几部分组成:双端口ram(即中心的部件)、左右两边的地址控制器,下面的两对D触发器。其中地址控制器负责控制地址自增加、将二进制地址转为格雷码以及解格雷码写指针产生逻辑、读指针产生逻辑及空满标志产生逻辑。两对D触发器 则负责将写地址同步至读时钟域、将读地址同步至写时钟域。我们知道,在异步FIFO,中读写操作是由两个完全不同时钟域的时钟所控制。在写时钟域部分,由写指针所产生逻辑生成写端口所需要的写地址和写控制信号;在读时钟域部分,由读指针产生逻辑生成读断口所需要的读地址和读控制信号。
FIFO的接口
宽度(width):FIFO一次读写操作的数据位,
深度(depth),FIFO可以存储多少个数据。
wfull:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
rempty:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
wr_clk:写操作的工作时钟,在每个时钟沿来临时写数据。
rd_clk:读操作的工作时钟,在每个时钟沿来临时读数据。
w_en:异步FIFO的写使能
r_en:异步fifo的读使能
读指针:指向下一个读出地址,读完后自动加1。
写指针:指向下一个要写入的地址的,写完自动加1。
读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。
如何判断空满?
我们知道,使用异步FIFO最重要的地方在于如何判断其空满。因为异步FIFO的读写使用了不同的时钟,因此不能采用常规的计数器方法来产生空满标志符。
当前的解决方法就是使用读写指针来判断,什么意思呢?当系统复位的时候读写指针全部清零,此时此刻读写指针相等时;当数据读出速率大于写入速率的时候,读指针赶上了写指针,FIFO为空,如图所示:
当读写指针指向了同一地址,但写指针超前整整一圈,这就说明整个fifo已经被写满了
为了区分到底是满状态还是空状态,我们可以在指针中添加在添加一位,这样的话,当写指针增加并越过最后一个FIFO地址时,将会直接进位,其他位置0;对读指针也进行同样的操作。因此对于深度为2^n的FIFO,其需要的地址宽度为n,需要的读写指针位宽为(n+1)位。举个例子:如对于深度为256的FIFO,需要采用8bit的地址宽度,读写指针则需要9位。
如果两个指针的最高位不同,其他位相同,说明写指针比读指针多折回了一次;如r_addr_ptr=0000,而w_addrr_ptr= 1000,为满。如果两个指针的MSB相同,其余位相等,则说明两个指针折回的次数相等,说明FIFO为空。
格雷码的使用
讲到这里,大家可能认为就没有什么大问题了,其实不然,我们知道,将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。
二进制转换为格雷码:二进制数的最高位保持不变, 后续位依次与前一位进行异或运算。判断读空时 :将写时钟域的写指针同步到读时钟,然后与读时钟域的读指针进行比较,每一位都完全相同才判断为读空;判断写满时:需要 写时钟域的格雷码wgray_next 和 被同步到写时钟域的读指针wr2_rp 高两位不相同,其余各位完全相同。
假空假满现象
这里有空两格问题需要讨论:使用格雷码进行空满判断一定正确么?如果不正确的话,是不是不应该使用该方法呢?我们一个一个问题来看。
首先我们先设想将读指针同步到写时钟域下:读指针同步到写时钟域需要时间T,在经过T时间后,可能原来的读指针会增加或者不变,也就是说同步后的读指针一定是小于等于原来的读指针的。写指针也可能发生变化,但是写指针本来就在这个时钟域,所以是不需要同步的,也就意味着进行对比的写指针就是真实的写指针。此时我们一定要记住,同步过来的读指针实际上是时间T之前的读指针,而并非此时此刻的读指针。如果我们进行写满判断的时候,此刻同步过来的读指针应该小于或等于此刻没有同步过来的读指针,就算出现了写满,那也是假满。还是可以往里面写的,但是我们不管真满假满,我们只知道满了。就不会往里面写数据了,那么这就不是一种错误设计。可以想象一下,假设一个深度为256的FIFO,在写到第254个数据的时候就报了“写满”,大不了FIFO的深度我少用一点点就是的。但是这是安全的
读空判断:如果我们在写时钟域下进行判空,即也就是同步后的读指针追上了写指针。但是原来的读指针是大于等于同步后的读指针的,所以实际上这个时候读指针实际上是超过了写指针。这种情况意味着已经发生了“读空”,却仍然有错误数据读出。所以这种情况就造成了FIFO的功能错误。这时候输出的是真空信号,但是会造成“读空”,所以不可取。因此万万不可以在写时钟域下进行判空。
接下来我们设想将写指针同步到读时钟域下:
写指针同步到读时钟域需要时间T,在经过T时间后,可能原来的写指针会增加或者不变,也就是说同步后的写指针一定是小于等于原来的写指针的。读指针也可能发生变化,但是读指针本来就在这个时钟域,所以是不需要同步的,也就意味着进行对比的读指针就是真实的读指针。
读空判断:我们知道同步过来的写指针是小于此时此刻真实的写指针的,也就是说这种情况是“假读空”。。可以想象一下,假设某个FIFO,在读到还剩2个数据的时候就报了“读空”,大不了我先不读了,等数据多了再读,但是这也是安全的。
写满判断:也就是同步后的写指针超过了读指针一圈。我们知道同步过来的写指针是小于此时此刻真实的写指针的,所以实际上这个时候写指针已经超过了读指针不止一圈,这种情况意味着已经发生了“写满”,却仍然数据被覆盖写入。所以是不行的
所以总结一下,判断读空在读时钟域,判断写满在写时钟域。那么假读空会不会造成有数据在FIFO里面读不出来?答案是不会,同步时虽然延迟了两个信号,但是最终还是会同步到跨时钟域,所以假读空信号不会一直有效,还是会在延后几个周期把数据读出来。