概述
文章参考Simulation and Synthesis Techniques for Asynchronous FIFO Design
一、简介
异步FIFO主要用于跨时钟域数据的传输,一些异步FIFO的错误设计也能实现90%的功能,错误较少的设计在99%的时间内也能正常工作,本文指明了一些异步FIFO设计中不能忽视的细节。
二、传输异步信号
FIFO设计的主要难点在于产生FIFO指针和可靠的标志FIFO空满的方法。
2.1 同步FIFO指针
在同步FIFO设计中,可以用计数器来标志FIFO的空满。同步FIFO计数器共有以下几种状态:增加(只写不读);减少(只读不写);保持(不读不写,或边读边写)。当计数器到达FIFO指定容量时,即为满;计数器为0时,即为空。
然而这种方法不能用于异步FIFO设计。在异步FIFO设计中,读指针和写指针需要对比。
2.2 异步FIFO指针
为了理解FIFO设计,首先需要了解FIFO指针是如何工作的。写指针指向需要写入的下一个地址,因此,当复位时,两个指针都指向0。在FIFO的写操作中,指针指向的地址被写入数据,然后指针会加一,指向下一个要写入的地址。
同样,读地址也指向下一个要读的地址。在复位时,两个指针都为0,FIFO此时为空,读指针指向一个无效数据(因为FIFO为空并且空标志位拉高)。一旦第一个数据写入FIFO,写指针增加,空标志位拉低,读指针依然在地址首位,并且立刻将第一个数据输出。将读指针指向下一个地址好处是不用两个周期来读出数据。如果接收者需要先将FIFO读指针拉高,然后再读出数据,就会浪费一个周期。
无论地址为空还是满,读指针和写指针都相同,那么如何区分呢?解决方法是加入额外1bit数据,当读指针或写指针到达FIFO最大值时,最高位bit翻转,当(n-1)bit相同,同时第一位相同时,FIFO为空;当(n-1)bit相同,同时第一位不同时,FIFO为满。
2.3 二进制FIFO指针思考
试图将二进制计数值从一个时钟域同步到另一个时钟域是有问题的,因为n位计数器的每个位都可以同时改变(例如二进制数中的7->8是0111->1000,所有位都改变了)。解决这个问题的一种方法是在保持寄存器中采样并保持周期二进制计数值,并将同步就绪信号传递到新的时钟域。当就绪信号被识别时,接收时钟域向发送时钟域发回同步确认信号。在从接收时钟域接收到确认信号之前,采样指针不得改变。使用这种技术,具有多个变化位的计数值可以安全地转移到新的时钟域。收到确认信号后,发送时钟域允许清除就绪信号并重新采样二进制计数值。
使用这种技术,二进制计数器值被定期采样,并且不是所有的二进制计数器值都可以被传递到新的时钟域。问题是,我们需要关心二进制计数器可能继续增加并且在采样的计数器值之间的先进先出溢出或下溢的情况吗?答案是否定的。
当写指针赶上同步和采样的读指针时,发生先进先出满。同步和采样的读指针可能不会反映实际读指针的当前值,但写指针不会试图计数超过同步的读指针值。不会发生溢出[8]。当读指针赶上同步和采样的写指针时,发生先进先出空。同步和采样的写指针可能不会反映实际写指针的当前值,但读指针不会试图计数超过同步的写指针值。不会发生下溢[8]。第7.0节在讨论了同步格雷码指针后,详细介绍了用同步就绪确认握手信号对二进制指针进行采样的技术。
先进先出计数器指针的常用方法是使用格雷码计数器。格雷码只允许每个时钟转换改变一位,消除了在同一时钟沿同步多个变化信号的问题。
2.4 FIFO测试问题
想要测试出FIFO中的微小问题几乎是不可能的,因为rtl级仿真时理想化的,即使这样,如果没能正确实现,在真是的设计中也会导致灾难性的失败。
在rtl级仿真中,如果二进制计数FIFO指针在设计中实现,那么所有的FIFO指针bit会同步变化;这样是无法发现同步和比较的问题的。在没有(back annotate)延迟的门级仿真中,发现的概率也很小。在更高速的设计中,发现问题的概率更小。发现FIFO设计问题最好的方法是带有(back annotate)的门级仿真,但发现的概率也很小。
显然发现FIFO设计的潜在问题的最好方法是在最开始就做好设计。
三、格雷码计数器
因为一些原因(后面会说),期望创建n位格雷码计数器和(n-1)位格雷码计数器。 分别创建两个计数器当然很容易,但是创建一个通用的n位格雷码计数器然后修改第二个MSB以形成一个共享的(n-1)位格雷码计数器也很容易且有效。在本文中,称之称为“双n位格雷码计数器”。
图2左边常用的格雷码中,除了MSB以外,其他3位是关于第7、8位镜像对称的。
为了将4位格雷码转化位3位格雷码,我们不希望后半部分的LSBs和前半部分是镜像关系,我们希望后半部分的后3位是前半部分的重复。
因此可将第二位MSB翻转。唯一的问题是第7到8位和第15到0位时,有两位翻转,因此并不是严格的格雷码。
3.2 格雷码计数器基础
格雷码有两个特点:1,相邻格雷码只有1位不同;2,有用的格雷码通常计数2的幂个数。格雷码也可以给普通偶数计数,但是通常比较复杂。格雷码不能给奇数计数。因此本文讲的FIFO深度都是2^n。
图3是类型1双n位格雷码计数器的框图。 类型格雷码计数器假定寄存器位的输出本身就是格雷码值(ptr,wptr或rptr)。 然后,将格雷码输出传递到格雷二进制转换程序(bin),将其传递给条件二进制值增量器,以生成下一个二进制计数值(bnext),并将其传递给二进制代码。 到格雷码转换器,生成下一个格雷码数值(gnext),该值传递到寄存器输入。 图3框图的上半部分显示了所描述的逻辑流程,而下半部分则显示了与第二个格雷码计数器相关的逻辑,如下一节所述。
3.3 双n位格雷码计数器
双n位格雷码计数器是一个格雷码计数器,它生成n位格雷码序列(在3.2节中介绍)和一个(n-1)位格雷码序列。
通过对n位格雷码的两个MSB进行异或运算,以生成(n-1)位格雷码的MSB,即可简单地生成(n-1)位格雷码。 它与n位格雷码计数器的(n-2)个LSB组合在一起,形成(n-1)位格雷码计数器。(目的是将镜像对称的格雷码改成像二进制一样的循环码)
3.4 附加格雷码计数器思考
如图3所示,对二进制值增量器进行“如果不满”或“如果不为空”测试,以确保适当的FIFO指针在FIFO满或FIFO空的情况下不会递增。 FIFO缓冲区的上溢或下溢。
如果在断定FIFO满状态时将,数据发送到FIFO的逻辑块可以可靠地停止发送数据,则可以通过从FIFO写指针中删除完整测试逻辑来简化FIFO设计。
FIFO指针本身并不能保护FIFO缓冲区不被覆盖,但是可以将其他条件逻辑添加到FIFO存储缓冲区中,以确保在FIFO满状态下不能激活write_enable信号。
可以将额外的“粘性”状态位ovf(上溢)或unf(下溢)添加到指针设计中,以指示在满期间发生了额外的FIFO写操作,或者在空期间发生了额外的FIFO读操作,则只能reset清除数据。
一个安全的通用FIFO设计将包括上述保护措施,但代价是实现的体积稍大一些,甚至可能更慢。这是一个好主意,因为将来的同事可能会在不了解当前设计所考虑的所有重要细节的情况下尝试在另一个设计中复制和重用代码。
四、格雷码计数器类型2
该类型实际上使用两组寄存器,消除将格雷指针值转换为二进制值的需要。 第二组寄存器(二进制寄存器)也可直接作为FIFO的地址,而无需将地址转换为格雷码。 n位格雷码指针仍然需要将指针同步到相对的时钟域,但是n-1位二进制指针可用于直接寻址内存。 如果需要,二进制指针还使运行计算更容易以生成“几乎全”和“几乎为空”的位(本文未显示)。
FIFO类型1的具体实现
为方便该设计的静态时序分析,该设计被分为以下功能和时钟域的六个模块:
1,fifo1(见6.1)。这是包括了所有时钟域的顶层设计。顶层模块仅用来实例化所有其他的FIFO模块。如果将此FIFO用作较大的ASIC或FPGA设计的一部分,则可能会舍弃此顶层封装程序,以允许将其他FIFO模块分组到各自的时钟域中,以改善综合和静态时序分析。
2,fifomem(见6.2)。这是一个FIFO存储,可以通过读和写时钟域操作。该存储是由双端口异步ram实现的。可以修改其他存储器样式以用作FIFO缓冲。
3,sync_r2w(见6.3)。这是一个同步器模块,用于将读指针同步到写时钟域。 wptr_full模块将使用同步的读取指针来生成FIFO已满条件。 该模块仅包含与写时钟同步的触发器。 此模块中没有其他逻辑。
4,sync_w2r(见6.4)。这是一个同步器模块,用于将写指针同步到读时钟域中。 rptr_empty模块将使用同步的写指针生成FIFO空条件。 该模块仅包含与读取时钟同步的触发器。 此模块中没有其他逻辑。
5,rptr_empty(见6.5)。该模块与读时钟域完全同步,并包含FIFO读指针和空标志逻辑。
6,wptr_empty(见6.6)。该模块与写时钟域完全同步,并包含FIFO写指针和全标志逻辑。
为了使用这种FIFO方式执行FIFO满和FIFO空测试,读写指针必须传递到相对的时钟域进行指针比较。
与其他FIFO设计一样,由于两个指针是从两个不同的时钟域生成的,因此指针需要“安全地”传递到相对的时钟域。本文展示的技术是同步格雷码指针,以确保一次只能改变一个指针位。
5.0 处理空满条件
FIFO满和FIFO空的具体实现方式取决于设计。
本文的FIFO设计假设在读时钟域产生空标志,以确保当先进先出缓冲区为空时,即读指针赶上写指针(包括指针MSB)的瞬间,立即检测到空标志。
本文中的FIFO设计假设在写时钟域中产生满标志,以确保当FIFO缓冲器满时,即写指针赶上读指针的时刻(除了不同的指针MSB之外),立即检测到满标志。
5.1 产生空信号
如图1所示,当读指针和同步写指针相等时,FIFO为空。
空比较很简单。使用比寻址FIFO内存缓冲区所需的大一位的指针。如果两个指针的额外位(指针的MSB)相等,指针覆盖的次数相同,如果读指针的其余部分等于同步写指针,则先进先出为空。
格雷码写指针必须通过sync_w2r模块中的一对同步器寄存器同步到读时钟域。由于使用格雷码指针一次只能改变一位,因此同步时钟域之间的多位转换没有问题。
为了有效地寄存rempty输出,同步的写指针实际上是与rgraynext(将寄存到rptr中的下一个格雷码)进行比较的。空值检测和相关程序已从示例6的rptr_empty.v代码中提取,如下所示:
【注】为什么跨时钟域传输时,每次只有一个bit变化会更安全?因为当有两个或以上信号同时跳变时,容易由于信号到达时间不完全相同而产生毛刺,在跨时钟域传输时,会带来严重不良后果。跨时钟域传输要求不能有毛刺,因此最好每次只有1bit变化。
5.2产生满信号
由于通过在写指针和读指针之间进行比较在写时钟域中生成满标志,因此一种安全的进行FIFO设计的技术要求在进行指针比较之前将读指针同步到写时钟域中。
满比较不像空比较那样简单。 比寻址FIFO存储缓冲区所需的地址大一点的指针仍用于比较,但是仅使用带有额外位的格雷码计数器进行比较并不能确定完全条件。 问题在于,格雷码是除MSB之外的镜像对称码。
考虑图6所示的深度为8的FIFO的例子。在本例中,使用一个3位格雷码指针来寻址内存,并添加一个额外的位(4位格雷码的MSB)来测试满状态和空状态。如果允许FIFO填充前七个位置(字0-6),然后如果先进先出通过读回相同的七个字而被清空,两个指针将相等,并将指向地址格雷-7(FIFO为空)。在下一次写操作中,写指针将递增4位格雷码指针(请记住,只有3个LSB用于寻址内存),使4位指针上的MSB不同,但其余写指针位将与读指针位匹配,因此将断言先进先出满标志。这是不对的!不仅FIFO没有满,3个LSB也没有改变,这意味着被寻址的存储位置将重写被写入的最后一个FIFO存储位置。这也是不对的!
这也是4.0中双n位格雷码被应用的原因。(严格意义上的格雷码有以上问题)。
正确方法是将rptr同步到wclk域,然后有三个条件是FIFO为满所必需的:
(1)wptr和同步rptr的MSB不相等(因为wptr必须比rptr多覆盖一次)。
(2)wptr和同步rptr的第二位MSB不相等(也就是最高位和次高位都不同,根据图6上半部分和下半部分对比即可得出)。
(3)所有其他wptr和同步rptr位必须相等。
为了有效地寄存wfull输出,同步的读指针实际上与wgnext(将在wptr中寄存的下一个格雷码)进行比较。从例7的wptr_full.v代码中提取的代码如下;
在上面的代码中,检测了检查FIFO满的三个必要条件,并将结果赋给wfull_val信号,然后将该信号寄存在后续的always块中。
wfull_val的连续赋值可以使用如下所示的连接来进一步简化:
5.3 不同时钟速率
由于异步FIFO是从两个不同的时钟域计时的,显然时钟以不同的速度运行。当将较快的时钟同步到较慢的时钟域时,由于较快的时钟将在较慢的时钟沿之间半周期地递增两次,因此会跳过一些计数值。这引发了对以下两个问题的讨论:
第一个问题:注意到一个同步的格雷码增量两次,但只被采样一次,将显示同步值的多位变化,这会导致多位同步问题吗?
答案是否定的。当多个位在同步时钟的上升沿附近发生变化时,同步多位变化只是一个问题。格雷码计数器可以在较慢的同步时钟沿之间增加两次(或更多)的事实意味着第一个格雷码变化将发生在较慢时钟的上升沿之前,并且只有第二个格雷码转变可以在时钟上升沿附近变化。格雷码计数器不存在多位同步问题。
第二个问题:再次注意到,在较慢时钟信号的上升沿之间,较快的格雷码计数器可能增加不止一次,是否可能在检测到满之前,来自较快时钟域的格雷码计数器可能增加到满状态和满+1状态,导致先进先出溢出而不识别先进先出是否已满?(这个问题同样适用于FIFO空)。
同样,使用本文描述的实现,答案是否定的。先考虑FIFO满信号的产生。当写指针赶上同步读指针并且在写时钟域中检测到FIFO满状态时,FIFO变满。如果wclk域比rclk域快,写指针将最终赶上同步读指针,FIFO将满,wfull位将被置位,FIFO将停止写入,直到同步读指针再次前进。写指针不能前进超过wclk域中的同步读指针。
对空标志的类似检测表明,当读指针赶上同步写指针并且在读时钟域中检测到FIFO空状态时,FIFO变为空。如果rclk域比wclk域快,读指针将最终赶上同步写指针,FIFO将为空,rempty位将被置位,FIFO将停止读取,直到同步写指针再次前进。在rclk域中,读指针不能超过同步写指针。
用这种方法,“满”或“空”的跳变恰好在FIFO变满或变空时发生。“满”和“空”状态的去除是有延迟的。
5.4 保守的满空信号
本文描述的FIFO采用了一种保守的方法实现了满清除和空清除。也就是说,“满”和“空”拉高时时间是准确的,但移除是有延迟的。
由于写时钟用于产生FIFO满状态,并且由于FIFO满信号在写指针赶上同步读指针时跳变,所以满检测是即时的。移除“满”状态是悲观的,因为“满”比较是通过同步读取指针完成的。当读指针增加时,FIFO不再满,但产生满的逻辑电路不会检测到变化,直到两个wclk上升沿将更新后的rptr同步到wclk域。这通常不是问题,因为这意味着数据发送硬件被“搁置”两个额外的wclk周期,FIFO仍然是满的。重要的是确保FIFO不溢出。发信号通知数据发送器在两个额外的wclk周期不发送数据,只会给FIFO留出时间来接收更多数据。
类似地,由于读取时钟用于产生FIFO空状态,并且由于FIFO空信号在读取指针赶上同步写入指针时拉高,因此空检测是即时的。移除“空”状态是悲观的,因为“空”比较是用同步的写指针完成的。当写指针增加时,FIFO不再为空,但空生成逻辑不会检测到变化,直到两个rclk上升沿将更新的wptr同步到rclk域。这通常不是问题,因为这意味着数据接收逻辑被“延迟”两个额外的rclk周期,FIFO仍为空。重要的是确保FIFO不会下溢。向数据接收器发送信号,要求其在几个额外的rclk边沿停止从先进先出中移除数据,这只会给FIFO提供更多数据填充的时间。
5.4.1 空满信号的准时跳变
请注意,如果两个指针同时递增,设置满标志或空标志可能不太准确。例如,如果写指针赶上同步的读指针,将设置满标志,但是如果读指针与写指针同时递增,则满标志将被提前设置,因为由于与“写满”操作同时发生的读操作,先进先出并没有真正满,但是读指针还没有同步到写时钟域。满设置的有点太早,有点悲观。但不是问题。
5.5 多比特异步复位
人们非常注意确保先进先出指针一次只改变一位。问题是,异步复位通常会导致多个指针位同时改变,这是否会有问题?
答案是否定的。复位表示FIFO也已复位,FIFO中没有有效数据。复位置位时,所有同步寄存器、wclk域逻辑(包括满标志寄存器)和rclk域逻辑同时异步复位。同时还拉高了空标志寄存器。更重要的问题涉及复位信号的有序移除。
请注意,本文中的设计针对wclk和rclk域使用不同的复位信号。本设计中使用的复位使用异步复位和同步移除。
FIFO指针的异步复位不是问题。
5.6 几乎满和几乎空
许多设计要求通过产生“几乎满”和“几乎空”状态位来通知挂起的满或空状态。有许多方法可以实现这两个状态位,并且每个实现都取决于指定的设计要求。
一些FIFO设计需要可编程的FIFO满和FIFO空差值,这样当两个指针之间的差值小于编程的差值时,相应的几乎满或几乎空位被置位。其他FIFO可以用一个固定的差异来实现,以产生几乎满或空。当FIFO指针的MSB接近时,其他FIFO可能满足于宽松生成的几乎满和空。然而,其他设计可能只需要知道FIFO何时满了一半或更多。
请记住,当wptr赶上同步rptr时,FIFO已满,几乎满的情况可以描述为(wptr+4)赶上同步rptr的情况。(wptr+4)值可以在中所示的格雷码指针逻辑中生成。
图3在格雷-二进制组合逻辑之后放置第二个加法器,将二进制值加4并记录结果。在wclk域中被转换为二进制值后,该注册值将用于对同步rptr进行减法运算,如果差值小于4,则可以设置一个几乎满位。小于操作确保当wptr在0-4个计数内追赶同步rptr时,几乎_full位被设置为全范围。在rclk域中可以使用类似的逻辑来生成几乎为空的标志。
几乎满的和几乎空的没有包括在本文显示的Verilog RTL代码中。
6.0 FIFO类型1的RTL代码
代码及注释可参考https://www.cnblogs.com/aslmer/p/6114216.html
7.0 二进制指针和格雷码指针的对比
如第2.3节所述,如果对指针进行采样,并且在两个时钟域之间使用握手控制信号来安全地传递采样的二进制计数值,则可以使用二进制指针进行先进先出设计。
二进制指针相对于格雷码指针的一些优点:
1、将多位值采样到保持寄存器中并使用同步握手控制信号将多位值传递到新的时钟域中的技术可以用于在时钟域之间传递任意多位值。这种技术可以用来传递先进先出指针或任何多位值。
2、每个同步格雷码指针需要2n个触发器(每个指针位2个)。采样的多位寄存器需要2n+4个触发器(每个时钟域中每个保持寄存器位1个,2个触发器同步一个就绪位,2个触发器同步一个应答位)。这两种指针类型经历亚稳态的几率没有明显的差别。
3、采样的多位二进制寄存器允许任意指针改变。格雷码指针只能递增和递减。
4、采样多位寄存器技术允许任意FIFO深度;而格雷码指针需要2的幂次方的FIFO深度。如果设计需要至少132个字的FIFO深度,使用标准格雷码指针将使用256个字的FIFO深度。由于大多数实例化的双端口内存块是2的幂,这可能不是问题。
5、使用二进制指针使得使用指针值之间的简单二进制运算来计算“几乎空”和“几乎满”状态位变得容易。
与格雷码指针相比,使用二进制指针的一个小缺点是:
1、采样并保持二进制FIFO指针,然后在时钟边界上握手,可以将新的采样延迟接收至少两个时钟周期和发送时钟域的另外两个时钟周期。这种延迟通常不是问题,但它通常会增加满和空跳变的悲观性,并可能需要额外的FIFO深度来补偿增加的悲观性。由于大多数FIFO通常被指定为超深度,因此不太可能需要额外的寄存器或更大的双端口FIFO缓冲区大小。
在选择实现FIFO设计的方法时,上述比较值得考虑。
8.0 结论
异步FIFO设计需要仔细关注从指针生成到满和空产生的细节。忽视重要的细节通常会导致设计容易被验证,但也是错误的。查找FIFO设计错误通常需要仿真实际延迟门级FIFO设计,还要靠运气!
FIFO指针到相对时钟域的同步是使用格雷码指针安全完成的。
产生FIFO满状态可能是FIFO设计中最困难的部分。双n位格雷码计数器有助于将n位指针同步到相对的时钟域,并使用(n-1)位指针进行“满”比较。在进行FIFO设计时,使用7.0节中描述的技术同步二进制FIFO指针是另一种值得使用的技术。
通过比较相等的n位读指针和同步的n位写指针,很容易产生FIFO空状态。
本文描述的技术应该适用于速度差异从小到大的异步时钟。
沿时钟边界仔细划分FIFO模块,并寄存所有输出,有助于两个异步时钟域内的综合和静态时序分析。
最后
以上就是优雅西牛为你收集整理的异步fifo设计FIFO类型1的具体实现的全部内容,希望文章能够帮你解决异步fifo设计FIFO类型1的具体实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复