概述
本文主体翻译自C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文,添加了笔者的个人理解与注释,文中蓝色部分为笔者注或意译。前文链接:
异步FIFO设计的仿真与综合技术(4)https://blog.csdn.net/apple_53311083/article/details/132899707
5.0 空/满信号处理(Handling full & empty conditions)
FIFO满和FIFO空的实现方式依赖于设计。
本文中的FIFO设计假定如下:将在读时钟域中生成空标志,以确保在FIFO缓冲区为空时,立即检测到空标志,即读指针赶上写指针(包括指针MSBs)的瞬间。
本文中的FIFO设计假设将在写时钟域中生成满标志,以确保在FIFO缓冲区已满时,立即检测到满标志,即写指针赶上读指针的瞬间(不同的指针MSBs除外)。
5.1 空标志生成(Generating empty)
当读指针和同步写指针相等时,FIFO为空。产生空的比较方法很简单。我们使用比FIFO实际缓冲区所需位数多一位的指针(高位扩展法)。如果两个指针的额外位(指针的MSBs)相等,则指针回卷的次数相同,如果读指针的其余部分等于同步写指针,则FIFO为空。
格雷码写指针必须通过在sync_w2r模块中找到的一对同步器寄存器同步到读时钟域。由于使用格雷码指针一次只更改一个位,因此在时钟域之间同步多位转换没有问题。
为了有效地寄存rempty输出,同步的写指针实际上与rgraynext(将寄存到rptr中的下一个格雷码)进行了比较。空测试和附带的时序always块已从示例6的rptr_empty.v代码中提取,如下所示:
assign rempty_val = (rgraynext == rq2_wptr);
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
rempty <= 1'b1;
else
rempty <= rempty_val;
5.2 满标志生成(Generating full)
由于满标志是通过在写时钟域之间运行比较,在写时钟域中生成的,因此进行FIFO设计的一种安全技术要求在进行指针比较之前将读指针同步到写时钟域。
满比较并不像空比较那么简单。和空比较一样,使用比处理FIFO内存缓冲区所需的地址大一位的指针进行比较,但是仅仅使用具有额外位的格雷码计数器来进行比较,这对判断满条件是无效的。问题在于,除了MSB之外,格雷码是一个对称码。
考虑一下图6中所示的一个深度为8的FIFO的示例。在这个示例中,一个3位格雷码指针用于处理内存,并添加了一个额外的位(4位格雷码的MSB)来测试满条件和空条件。如果允许FIFO写满了前7个位置(word 0-6),然后通过读取相同的7个word将FIFO清空,则两个指针将相等,同时指向地址Gray-7(FIFO为空)。在下一个写操作中,写指针将增加4位格雷码指针(记住,只有3个LSB被用于地址内存),使4位指针上的MSB不同,但其余的写指针位将匹配读指针位,因此FIFO满标志将被生效。这是错的!不仅FIFO没有满,而且3个LSB也没有改变,这意味着被寻址的内存位置将覆盖被写入的最后一个FIFO内存位置。这也是错误的!
这就是为什么之前我们提到的,要使用双n位格雷码计数器。
执行完整比较的正确方法是通过将rptr(读指针)同步到wclk域(写时钟域)来完成的,然后FIFO完全需要三个条件:
(1)wptr(写指针)和同步的rptr(读指针)的MSB位并不相等(因为wptr必须比rptr多回卷一次)。
(2)wptr和同步的rptr第二个高位不相等(因为一个指针第二高位的反(反:原来是0,反就是1,原来是1,反就是0)必须和另一个指针的第二高位(未反的)进行比较测试,如果MSB也是反向的----见上面的图6)。
(3)所有其他的wptr位和同步的rptr位必须相等。
为了有效地寄存wfull输出,同步的读指针实际上与wgnext(将在wptr中寄存的下一个格雷码)进行了比较。下面从示例7的.v代码中提取的时序always块中所示:
assign wfull_val = ((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&
(wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&
(wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
wfull <= 1'b0;
else
wfull <= wfull_val;
其实看代码更加直观,判断满的3个条件:(1)写指针和同步后的读指针最高位不同(2)写指针和同步后的读指针的第二高位不同,其实不难理解,如果是相同的,其实意味着LSBs并没有变化,只是MSB变化了,这是的FIFO并不是满的状态,错误原因在上文有所解释了。(3)除了最高位和次高位外,写指针和同步后的读指针其他位完全相同。
5.3 不同时钟速率(Different clock speeds)
First question
Second question
在考虑一下以下的情况:一个快速的格雷码计数器在慢时钟的两个上升沿之间可以增加不止一次,有可能格雷码计数器在更快的时钟域可以增加到满状态和满+1-状态之前检测,导致没有发现FIFO曾经满,导致FIFO溢出吗?(这个问题同样适用于FIFO空问题)。
同样,对于本文中的FIFO设计方案来说,答案是否定的。首先考虑FIFO满的情况。当写指针赶上同步的读指针,并且在写时钟域中检测到FIFO-满状态时,FIFO将变满。如果wclk域比rclk域快,写指针最终将赶上同步的读指针,FIFO将被满,wfull位将被设置,FIFO将停止写入,直到同步的读指针再次前进。写指针不能提前超过wclk域中的同步读指针。
对空标志的检查也类似,当读指针赶上同步写指针,并且在读时钟域中检测到FIFO-空状态时,FIFO为空。如果rclk域比wclk域快,读指针最终将赶上同步的写指针,FIFO将为空,空响应位将被设置,FIFO将停止读取,直到同步的写指针再次前进。读指针不能提前超过rclk域中的同步写指针。
使用这个实现,当FIFO变满或空时,就会发生“满”或“空”的生效。但是“满”和“空”状态的移除是悲观的(但是其实不影响功能的正确,只是影响使用的效率)。
5.4 悲观的空和满(Pessimistic full & empty)
本文中描述的FIFO使用“悲观”方法实现了满移除和空移除。也就是说,“满”和“空”都是准时生效的,但移除得很晚。
由于写时钟用于生成FIFO-full状态,并且当写指针赶上同步的读指针时,发生FIFO-full,因此full检测是“准确的”和即时的。清楚“满”状态是悲观的,因为“满”比较是使用同步读指针完成的。当读指针出现增量时,FIFO不再满,但满生成逻辑将不会检测到变化,直到两个上升的wclk沿将更新后的rptr同步到wclk域中。这通常不是问题,因为这意味着数据发送硬件被“推迟”了,或者被告知FIFO在额外的几个wclk周期仍然是满的状态。重要的是是要确保FIFO不会溢出。向数据发送者发送信号,暂时停止写操作,只会让FIFO有时间为接收更多数据腾出空间。
类似地,由于读时钟用于生成FIFO-empty状态,而且当读指针赶上同步写指针时就会发生FIFO-empty,因此空检测是“准确的”和即时的。删除“空”状态是悲观的,因为“空”比较是使用同步的写指针完成的。当写指针增加时,FIFO不再为空,但是空生成逻辑将不会检测到更改,直到两个上升的rclk沿将更新后的wptr同步到rclk域。这通常不是问题,因为它意味着数据接收逻辑被“搁置”,或者被告知FIFO对于几个额外的rclk边仍然是空的。重要的细节是要确保FIFO不会下溢出。向数据接收器发出信号,暂时停止读操作,只会让FIFO填充更多的数据。
5.4.1 “Accurate” setting of full & empty
请注意,如果两个指针同时递增,则设置满标志或空标志可能不太准确。例如,如果写指针赶上同步读指针,则将设置满标志,但如果读指针与写指针同时增加,则满标志将提前设置,因为由于“写到满”操作同时发生的读操作,FIFO并没有真正满,但读指针尚未同步到写入时钟域中。满标志的设定有点太早,也有点悲观。但是这不是一个设计问题。
5.5 多位异步复位(Multi-bit asynchronous reset)
人们非常关注确保FIFO指针一次只变化一位。问题是,是否会出现与异步复位相关的问题,这通常会导致多个指针位同时发生变化?
答案是否定的。复位表示FIFO也已被重置,并且在FIFO中没有有效的数据。在复位时,所有的同步寄存器、wclk域逻辑(包括已寄存的满标志)和rclk域逻辑都会同时进行异步复位。同时,还设置了已寄存的空标志。更重要的问题是有序地去除复位信号。
请注意,本文中包含的设计对wclk和rclk域使用了不同的复位信号。本设计中使用的复位旨在使用Mills and Cummings[2]中描述的技术进行异步设置和同步删除。异步复位FIFO指针不是一个问题。
5.6 将满/将空(Almost full and almost empty)
许多设计需要通知一个未决的满或空状态,并生成“将满”和“将空”的状态位。有许多方法可以实现这两个状态位,并且每个实现都依赖于指定的设计要求。
一些FIFO设计需要可编程的FIFO-full和FIFO-空值,这样当两个指针之间的差小于编程差时,生效相应的将满或将空位。其他FIFO设计也许以固定的差异实现,以生成将满或空。当FIFO指针的MSB关闭时,其他FIFO可能会满足于松散地生成满和空的情况。然而,其他设计可能只需要知道FIFO何时超过,或不到一半。(简单说就是不同的FIFO设计需求,提出了对于FIFO将空/将满指示的不同的需求)。
记住,当wptr赶上同步rptr时,FIFO是满的,将满的条件可以描述为(wptr+4)赶上同步rptr的条件。(wptr+4)值可以在中所示的格雷码指针逻辑中生成
图3通过在格雷码到二进制组合逻辑之后放置第二个加法器,向二进制值添加4个并寄存结果。这个寄存值在wclk域中将同步rptr转换为二进制值后,对同步rptr进行减法,如果差小于4,则可以设置一个将满的位。当wptr在赶上同步rptr的0-4个计数范围内时,一个小于的操作确保将满位被设置为full range。类似的逻辑可以使用rclk域的逻辑来生成将空的标志。本文中显示的Verilog RTL代码中没有包含将满和将空。