异步FIFO设计

  • 概述

前面提及到同步FIFO的设计,同步FIFO。但是在大规模的数字电路设计中,多时钟域往往是不可避免的,这样就会产生了跨时钟域传输等问题,为了解决跨时钟域问题,我们常常采取的方法是握手协议和异步FIFO做数据缓冲区,异步FIFO既可以使相异时钟域数据传输的时序要求变得宽松,也提高了它们之间的传输效率。



  • 设计思路

如上图中所示,左边是写时钟域中的信号,右边则是读时钟域中的信号,中间存在两个同步时钟逻辑单元,它们的目的是分别为了使得写时钟域中的写时钟同步到读时钟域,从而产生或者消除读空信号,读时钟域中的读时钟同步到写时钟域,从而产生或者消除写满信号。


  • 异步FIFO常见问题

好的FIFO设计的基本要求是写满而不溢出,读空又不多读。在同步FIFO中,比较容易做到判断写满和读空两种状态,但是在异步FIFO中,因为读写时钟不一致,因此真正的满和空状态是很难判定的,我们一般情况下只能够选择近满和近空状态。

所谓异步是指读写时钟是完全独立并且不一致的或者不同频率或者同频但不同相。读地址和空标志是由读时钟产生的,而写地址和写满标志则由写时钟产生。当要产生FIFO的空、满状态标志时必须进行读写地址的比较时,问题就来临了:

如果直接采样地址比较的话地址线一般有多位,由于每个地址寄存器的物理空间位置的不一致性造成写地址的每一位在写时钟作用下跳变得不一致即产生毛刺,要过一小段时间才能稳定。如果在未稳定期内刚好读时钟进行采样写地址这时就出现误判断逻辑错误。下图所示:

在上图中,每次地址变化都会存在中间不稳定态,不稳定态的大小和时长则和变化的位数相关,在二进制数值的递增中,一般存在两位甚至更多位数值的变化,这将导致更大的不稳定态,为了减少这种影响,我们推荐使用格雷码。该码的优点是相邻两值只有一位跳变,其他不变。这样地址变化的时间较短极大提高比较精度。



  • 格雷码与二进制码

格雷码是一种做加一运算时只变化一位的编码,下表即为一个三位格雷码编码格式。


关于二进制转格雷码,其法则是保留二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。

用RTL代码表示如下:
function [ADDR_WIDTH-1:0] gray_conv;
input [ADDR_WIDTH-1:0] in;
begin
  gray_conv = {in[ADDR_WIDTH-1],
         in[ADDR_WIDTH-2:0] ^ in[ADDR_WIDTH-1:1]};
end
endfunction


关于格雷码转二进制,其法则是保留格雷码的最高位作为自然二进制码的最高位,而次高位自然二进制码为高位自然二进制码与次高位格雷码相异或,而自然二进制码的其余各位与次高位自然二进制码的求法相类似。

用RTL码表示如下:
integer i;
    always @ (Gry)
    begin    
        Bin[length-1]=Gry[length-1];    
        for(i=length-2;i>=0;i=i-1)        
            Bin[i]=Bin[i+1]^Gry[i];
    end


如下图所示:指向存储器的地址指针由二进制计数器产生,而用于跨时钟域传播的格雷码指针是对二进制指针的实时转换并用寄存器采集获得的。这里要注意的是,计数器的位宽比实际所需的位宽要多出一位,这样做的目的是方便判断FIFO的空或满,这一点下文中将会介绍。



  • 空满标志的产生

异步FIFO最核心的部分就是精确产生空、满标志位,这直接关系到设计的成败。本文采用比较读写指针来判断FIFO的空满,如果FIFO的深度是n-1位线所能访问到的地址空间,那么此设计所要用的指针位宽就比实际多出一位,也就是n位,这样做有助于判断FIFO是空还是满。

  • 空标志的产生

当FIFO中的读指针赶上了写指针,也就是rd_ptr完全等于wr_ptr时,可以断定,FIFO里的数据已被读空,而且只有在两种情况下,FIFO才会为空:第一种是系统复位,读写指针全部清零;另一种情况是在FIFO不为空时,数据读出的速率快于数据写入的速率,读地址赶上写地址时FIFO为空。空标志位的产生需要在读时钟域里完成,这样做的好处是精确度相对高,不会出现FIFO中的数据已经空了但是还没出现空标志的情况,但是在最坏的情况下,是有可能出现虚空的情况的,也就是实际上有数据但是FIFO的空标志还是产生了。主要是因为两种情况,一种是在格雷码的转换过程中,采样值采的还是原来的值导致的。第二种则是因为同步器的原因,我们需要把写指针从写时钟域同步到读时钟域,常用的方法是采样两级寄存器打两拍的方式去尽可能的避免亚稳态,这样产生的问题是得到的写指针比实际的写指针慢了两个周期,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错。如下图所示:

  • 满标志的产生

和空标志位产生机制一样,满标志位也是通过比较读写地址产生的。当写指针足足领先读指针一圈时,就会出现满标志,这时候在二进制码中,应该是最高位不同而其他位都相同。满标志的产生是在写时钟域,这样做的目的是为了避免实际上已经满了而未出现满标志的状况,但是有可能出现虚满状态。主要是因为两种情况,一种是在格雷码的转换过程中,采样值采的还是原来的值导致的。第二种则是因为同步器的原因,我们需要把读指针从读时钟域同步到写时钟域,常用的方法是采样两级寄存器打两拍的方式去尽可能的避免亚稳态,这样产生的问题是得到的读指针比实际的读指针慢了两个周期,而在这个同步的时间内有可能还会读出新的数据,因此同步后的读指针一定是小于或者等于当前实际的读指针,所以此时判断FIFO为满不一定是真满,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错。总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。



  • 同步逻辑的产生

为了把写指针同步到读时钟域以及把读指针同步到写时钟域,我们需要添加同步逻辑,最常用的同步逻辑则是利用两级触发器尽量减少亚稳态的产生。代码如下所示:



  • 格雷码如何产生比较空满

上面说到,二进制码中,读写指针的所有位相同为空,最高位不相同而其他位相同则为满,那么在格雷码中呢?我们通过查看如下图:


假设深度为8,那么可以看出当读写指针分别为0和8时,应该为满,此时的格雷码最高两位相反,其他位相同。同理,为空时,读写指针格雷码的所有位相同。


为了保险起见,我们设为当读写格雷码地址相等或者FIFO内还剩下一位深度时为空:


当写FIFO的格雷码地址等于上次读的格雷码地址,或者下次要写的格雷码地址等于上次读的格雷码地址时,为满:

解决了以上问题,异步FIFO应该就可以写出来了~

欢迎点赞,关注,分享~~ 本文原发于微信公众号【 数字ic小站 

全部评论
学到了,感谢楼主分享啊
点赞 回复 分享
发布于 2022-06-26 08:29
m
点赞 回复 分享
发布于 2022-07-22 12:59

相关推荐

2024-12-21 10:42
已编辑
江西软件职业技术大学 Java
新宿站不停:该提升学历就提升学历,菜了就多练。没事找牛马公司虐自己是吧? 谁没事说自己“经验少”,这不自己把自己塞剎鼻hr嘴里找🐴吗
点赞 评论 收藏
分享
评论
11
72
分享

创作者周榜

更多
牛客网
牛客企业服务