FIFO分为两种:同步FIFO和异步FIFO,两者的却别在于读写时钟是否是同一个时钟。换句话说,异步FIFO是同步FIFO的强化版设计,更加的灵活。特征就是:地址+1
内部结构:双端口RAM、写控制逻辑、写地址、状态产生、读控制逻辑产生、读地址、格雷码同步。其中状态产生分为空信号、将空信号、满信号、将满信号。格雷码同步分为写地址格雷码从写时钟域到读时钟域的同步、读地址格雷码从读时钟域到写时钟域的同步。
设计思想:状态信号的产生和跨时钟域的转换及同步。
(1)格雷码同步
格雷码同步在异步fifo中的作用:用于状态产生。
格雷码实现多比特跨时钟域转换的原理:因为fifo中地址是逐次+1的,而格雷码相邻两位数据一次只有一位数据发生变化。因此,将写地址(读地址)变换成格雷码,虽然地址是多比特的,但是每次变化只有1bit发生变化。这就是将二进制的地址转换成格雷码的好处了。格雷码能最大限度的降低垮时钟域带来的风险(亚稳态)。(注意:先转换成格雷码再跨时钟域)
转换原则:
二进制转格雷码:
格雷码转二进制码:
将格雷码最高位作为二进制码最高位,然后二进制最高位与格雷码次高位异或运算得到二进制次高位,依此类推计算。如:
(2)空满状态的判断
要判断fifo的状态,就需要知道读写地址。通过判断读写地址的差值来判断状态。其中存在一直特殊情况:读写地址指针指向同一个地址,但是可能是满,也可能是空。
具体分析:因为fifo是回卷式的写和读,这也就是说如果fifo深度为1023,如果写地址在1023,写地址指针会重新回到0地址,如果此时读地址还在地址0,就会存在读写地址都在地址0。而空的时候,读写地址也存在都在0地址的问题。
这就带来了一个问题,当读地址和写地址都在0的时候,可能是空,也可能是满。怎么区分是满和空呢?
那就是将读写地址扩展一位,当读写地址的值相等时,为空;读写地址除最高位不同,其余相同为满。
<1.写满读空信号如何产生?
在复位的时候,读指针和写指针相等,读空信号有效(这里所说的指针其实就是读地址、写地址)当读指针赶上写指针的时候,写指针等于读指针意味着最后一个数据被读完,此时读空信号有效。写满信号:当写指针比读指针多一圈时,写指针等于读指针意味着写满了,此时写满信号有效。
问题a:我们会发现 读空的条件是写指针等于读指针,写满的条件也是写指针等于读指针,到底如何区分呢?
解决办法:将指针的位宽多一位,表示读写指针是否在同一轮,注意我们这里讨论的是2进制递增数。
举个例子说明:假设要设计深度为 8 的异步FIFO,此时定义读写指针只需要 3 位(2^3=8)就够用了,但是我们在设计时将指针的位宽设计成 4 位,最高位的作用就是区分是读空还是写满,具体理论 1 如下当最高位相同,其余位相同认为是读空;当最高位不同,其余位相同认为是写满。
这里还隐藏着一个问题,读写指针在不同时钟域的比较问题,如何解决亚稳态的问题?
<2.由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
跨时钟域的问题:上面我们已经提到要通过比较读写指针来判断产生读空和写满信号,但是读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后进行比较。
解决方法:两级寄存器同步 + 格雷码
同步的过程有两个:
(1)将写时钟域的写指针同步到读时钟域,将同步后的写指针与读时钟域的读指针进行比较产生读空信号;
(2)将读时钟域的读指针同步到写时钟域,将同步后的读指针与写时钟域的写指针进行比较产生写满信号;
异步FIFO的写指针和读指针分属不同时钟域,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大,怎么办呢?到了格雷码发挥作用的时候了,而格雷码的编码特点是相邻位每次只有 1 位发生变化, 这样在进行指针同步的时候,只有两种可能出现的情况:1.指针同步正确,正是我们所要的;2.指针同步出错,举例假设格雷码写指针从000->001,将写指针同步到读时钟域同步出错,出错的结果只可能是000->000,因为相邻位的格雷码每次只有一位变化,这个出错结果实际上也就是写指针没有跳变保持不变,我们所关心的就是这个错误会不会导致读空判断出错?答案是不会,最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。所以gray码保证的是同步后的读写指针即使在出错的情形下依然能够保证FIFO功能的正确性。在同步过程中的亚稳态不可能消除,但是我们只要保证它不会影响我们的正常工作即可。
<3.由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢?
异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,
将写指针同步到读时钟域再和读指针比较进行FIFO空状态判断,因为在同步写指针时需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错,同理将读指针同步到写时钟域再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满,这样更保守,这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之后不能继续写入。总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。
举个例子:大多数情形下,异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,慢的时钟域同步到快的时钟域不会出现漏掉指针的情况,但是将指针从快的时钟域同步到慢的时钟域时可能会有指针遗漏,举个例子以读慢写快为例,进行满标志判断的时候需要将读指针同步到写时钟域,因为读慢写快,所以不会有读指针遗漏,同步消耗时钟周期,所以同步后的读指针滞后(小于等于)当前读地址,所以可能满标志会提前产生,满并非真满。进行空标志判断的时候需要将写指针同步到读指针 ,因为读慢写快,所以当读时钟同步写指针 的时候,必然会漏掉一部分写指针,我们不用关心那到底会漏掉哪些写指针,我们在乎的是漏掉的指针会对FIFO的空标志产生影响吗?比如写指针从0写到10,期间读时钟域只同步捕捉到了3、5、8这三个写指针而漏掉了其他指针。当同步到8这个写指针时,真实的写指针可能已经写到10 ,相当于在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去,这样在判断它是不是空的时候会出现不是真正空的情况,漏掉的指针也没有对FIFO的逻辑操作产生影响。
<4.在传递读写时钟域的指针使用格雷码来传递,如何把二进制转换为格雷码,格雷码是如何判断读空写满呢?
二进制码转换成二进制格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。
这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,verilog代码实现就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext;
因为格雷码与二进制计数的有区别,我们可以得出以下的结论: