概述
本文主体翻译自C. E. Cummings and S. Design, "Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文,添加了笔者的个人理解与注释,文中蓝色部分为笔者注或意译。
摘要(ABSTRACT)
FIFO通常被用于将数据从一个时钟域传输到另一个异步时钟域。使用FIFO将数据从一个时钟域传递到另一个时钟域需要多异步时钟设计技术。设计一个合适的异步FIFO是很困难的。
本文将详细介绍一种异步FIFO的设计方法,读/写指针使用格雷码进行编码(即使用格雷码指针),在判断FIFO空/满之前,将读/写指针同步到对侧(对侧含义:读指针同步到写时钟域,写指针同步到读时钟域)。本文包括完全的编码、综合和分析的RTL Verilog模型。
1.0 引言(Introduction)
异步FIFO指的是:数据值从一个时钟域写入FIFO缓冲区,从另一个时钟域的同一FIFO缓冲区读出(这两个时钟域是异步的)一种FIFO设计。
异步FIFO用于将数据从一个时钟域安全地传递到另一个时钟域(也就是我们经常说的FIFO用于跨时钟域传输)。
异步FIFO的设计方法很多,但是其中包括很多错误的方法。大多数错误实现的FIFO设计仍然在90%的时间内正常运作,大多数几乎正确的FIFO设计能在99%+的时候正常运作。不幸的是,正常工作99%+的设计缺陷通常是最难检测和调试的,或者就是诊断和被召回时最昂贵的。
简单说就是设计异步FIFO的方法很多,但是很多设计方法是错误的,但是这些错误的设计能在大部分时间正确运行,可是一旦发生错误,弥补起来代价就很大。
本文讨论了异步FIFO设计风格和FIFO设计时必须考虑的重要细节。在本文的其余部分只是简单地将"异步FIFO"称为"FIFO"。
2.0 传递多个异步信号(Passing multiple asynchronous signals)
尝试将多个变化中的信号从一个时钟域同步到另一个新的时钟域,并且还要保证所有变化中的信号同步到新时钟域的相同时钟周期已经被证明是有问题的。FIFO用于将多位的数据从一个时钟域安全地传递到另一个时钟域。通过一个时钟域中的控制信号将数据放入FIFO缓冲存储器阵列中(写FIFO操作),并通过来自第二个时钟域的控制信号从同一FIFO缓冲存储器阵列的另一个端口取出这些数据字(读FIFO操作)。从概念上讲,这样去设计一个FIFO的似乎很容易。
FIFO设计的核心(难点)在于设计FIFO的指针(读/写指针,也有地方成为读/写地址),并且找到一种合适且稳定的方式来生成空/满信号(用来指示FIFO的full/empty状态)。
2.1 同步FIFO指针(Synchronous FIFO pointers)
对于同步FIFO设计(读写操作处于同一个时钟域下),可以通过一个计数器来获取FIFO当前数据数量信息(只读不写:计数器递减,只写不读:计数器递减,不读不写/既读又写:计数器不变)当FIFO计数器达到设定的阈值时,FIFO为满,而当FIFO计数器为零时,FIFO为空。
这种方法我们称之为计数法,关于这种设计方法的细节内容,可以阅读:同步FIFO的verilog实现(1)------计数法https://blog.csdn.net/apple_53311083/article/details/132381065?spm=1001.2014.3001.5501
不幸的是,对于异步FIFO设计,不能使用增量-递减的FIFO填充计数器(也就是我们上面讲的计数法),因为需要两个不同的异步时钟来控制计数器。如果要确定异步FIFO设计的满状态和空状态,则必须对写指针和读指针进行比较。
2.2 异步FIFO指针(Asynchronous FIFO pointers)
为了理解FIFO的设计,我们需要了解FIFO指针是如何工作的。写指针总是指向下一个要写入的位置;因此,在复位时,两个指针都被设置为零,这恰好也是下一个要写入的FIFO数据的位置。在FIFO写操作中,数据被写到写指针所指向的内存位置,然后增加写指针以指向下一个要写入的位置。
相似的,读指针总是指向当前要读取的FIFO数据。同样,在复位时,两个指针都重置为零,FIFO为空,读指针指向无效数据(因为FIFO为空,生效空标志)。一旦第一个数据被写入FIFO,写指针增加,清除空标志,读指针仍然寻址第一个FIFO内存的内容,并且立即将第一个有效数据驱动到FIFO数据输出端口,数据准备读出。读指针总是指向下一个要读取的FIFO数据,这意味着接收器逻辑不必使用两个时钟周期来读数据。如果接收方在读取FIFO数据之前必须先增加读指针,接收器将需要一个时钟周期来从FIFO输出数据,再通过一个时钟周期捕获数据到接收器中。这将造成不必要的效率降低。
当两个指针相等时,FIFO为空。这种情况发生在(1)复位操作,两个指针都被复位到0;(2)读指针追上写指针,读出了FIFO中的最后一个数据。
当两个指针再次相等时,FIFO为满,当写指针转了一圈折回来(wrapped around)又追上了读指针,此时FIFO为满。这就造成了一个问题,读写指针相等时,FIFO到达是空的还是满的?
有一种计数可以实现FIFO空和满的区分,方法是给每一个指针(读和写)都额外增加一位。当写指针的增量超过最终的FIFO地址时,写指针将增加未使用的MSB,同时将其余的位设置为零,如图1所示(FIFO已经卷起:wrapped并切换了指针的MSB)。读指针也是这样相同的操作。如果两个指针的MSB位不同,这意味着写指针比读指针多回卷(wrapped)了一次。如果两个指针的MSB相同,这意味着两个指针回卷的次数相同。这种方法,我们称之为高位扩展法,关于这部分的详细内容,可以阅读
使用n-bit指针,其中(n-1)是访问整个FIFO内存缓冲区所需的地址位数,当包括MSB在内的两个指针相等时,FIFO为空。当两个指针除了MSB外都相等时,FIFO是满的。
本文中的FIFO设计使用了一个具有可写位置的FIFO的n位指针来帮助处理满和空条件。相关内容将在后文中展示。