4. FIFO空满判断逻辑

好,咱们今天聊聊FIFO里最核心、也最容易出bug的地方——空满判断。说实话,我见过太多工程师在这上面翻车了。我自己刚入行那会儿,也踩过格雷码同步的坑,差点导致芯片功能失效。嗯,咱们一个一个来拆解。

4.1 同步FIFO的空满判断

同步FIFO,读写时钟是同一个。判断空满,说白了就是比较读写指针。

空标志:读指针追上写指针,说明没数据可读了。

满标志:写指针追上读指针,说明没空间可写了。

但有个问题——指针回绕。你想想看,如果FIFO深度是8,指针从0走到7,再走就回到0了。这时候怎么区分空和满?

核心技巧:多用一个bit位

比如深度8的FIFO,指针用4位表示。最高位用来区分回绕次数。

  • 读写指针最高位相同 → 在同一圈 → 指针相等就是空
  • 读写指针最高位不同 → 写指针多绕了一圈 → 指针相等就是满
// 同步FIFO空满判断示例
wire [3:0] wr_ptr, rd_ptr;  // 4位指针,最高位是回绕标志
wire empty = (wr_ptr == rd_ptr);
wire full  = (wr_ptr[3] != rd_ptr[3]) && 
             (wr_ptr[2:0] == rd_ptr[2:0]);

我在项目中遇到过,有人直接用3位指针判断空满,结果深度8的FIFO,明明还有空间却报满。原因就是没处理回绕。这个坑,踩一次就记住了。

4.2 异步FIFO的空满判断

异步FIFO,读写时钟不同。这时候不能直接比较指针,因为跨时钟域会出问题。

解决方案:格雷码 + 同步器

为什么用格雷码?因为格雷码相邻值只变化1位。跨时钟域同步时,即使出现亚稳态,最多错1位。二进制码就不行,比如从0111变到1000,4位同时跳变,同步后可能得到任何值。

我的习惯做法:

  1. 写指针转格雷码 → 同步到读时钟域 → 判断空
  2. 读指针转格雷码 → 同步到写时钟域 → 判断满
// 二进制转格雷码
wire [N:0] gray_wr = (bin_wr >> 1) ^ bin_wr;

// 格雷码转二进制(用于比较)
wire [N:0] bin_rd;
assign bin_rd[N] = gray_rd[N];
genvar i;
generate
  for(i = N-1; i >= 0; i = i-1) begin
    assign bin_rd[i] = bin_rd[i+1] ^ gray_rd[i];
  end
endgenerate

这里有个细节——格雷码判断空满。空标志很简单,同步后的读写格雷码完全相等就是空。满标志就有点讲究了。

我曾经踩过的坑:

格雷码判断满,不能直接比较最高位不同。因为格雷码的最高位和次高位有特殊关系。正确做法是:

  • 写指针格雷码的最高位 ≠ 读指针格雷码的最高位
  • 写指针格雷码的次高位 ≠ 读指针格雷码的次高位
  • 其余位相等

说白了,就是写指针比读指针多绕了一圈,而且是在格雷码空间里判断的。

4.3 指针回绕处理

指针回绕,是FIFO设计的底层逻辑。不管是同步还是异步,都得处理。

回绕的本质:指针从最大值回到0,同时回绕标志位翻转。

FIFO深度 指针位数 回绕标志位 最大地址
8 4 (3位地址+1位回绕) bit[3] 7 (3'b111)
16 5 (4位地址+1位回绕) bit[4] 15 (4'b1111)
32 6 (5位地址+1位回绕) bit[5] 31 (5'b11111)

你想想看,如果深度不是2的幂次方怎么办?比如深度12。这时候就不能用简单的回绕标志了。我建议的做法是:

  • 用计数器记录写入/读取次数
  • 比较计数器的差值来判断空满
  • 或者用两个指针,一个指向当前地址,一个指向边界

不过说实话,大部分场景下FIFO深度都是2的幂次方。这样硬件实现最简单,面积也最小。

4.4 知识体系总览

下面这张图,是我自己总结的FIFO空满判断的核心逻辑。你看一眼就能明白整体脉络。

FIFO空满判断知识体系 同步FIFO 同一时钟域 直接比较读写指针 多1位回绕标志位 空:指针完全相等 满:最高位不同,低位相等 异步FIFO 跨时钟域 格雷码编码 双级同步器同步 空:格雷码完全相等 满:最高2位不同,其余相等 核心:指针回绕 + 跨时钟域同步 = 可靠的空满判断

嗯,这张图把同步和异步FIFO的核心区别都标出来了。你写代码的时候,对着这张图检查,基本不会漏掉关键点。

避坑指南:

  • 同步FIFO:别忘了指针位宽要比地址多1位
  • 异步FIFO:格雷码转二进制时,小心组合逻辑环路
  • 深度非2的幂次方:别硬套回绕标志,用计数器更靠谱

好了,空满判断这块就聊到这儿。记住一句话:空满判断的本质,就是比较指针的圈数和位置。圈数用回绕标志,位置用地址位。同步FIFO直接比,异步FIFO先转格雷码再比。就这么简单。

可可精品资料库