4、同步FIFO空满判断:指针相等判空、指针差值为深度判满、格雷码指针判空满

同步FIFO的设计,说白了就是两个核心问题:数据怎么存和什么时候能读能写。上一章我们聊了存储体和读写指针,这一章咱们重点攻克第二个问题——空满判断。

空满判断要是搞错了,后果很严重。读空了你还在读,拿到的就是垃圾数据;写满了你还往里写,旧数据就被覆盖了。我在项目中就见过一次这样的bug,仿真跑得好好的,上板就随机挂,查了三天才发现是空满标志出了边界情况。

4.1 指针相等判空——最直观的方法

先说说最简单的判空逻辑。

当写指针和读指针指向同一个位置时,FIFO就是空的。这个很好理解吧?你想想看,写指针追上了读指针,说明所有写进去的数据都已经被读走了。

判空条件:写指针 == 读指针

代码实现也很直接:

assign empty = (wr_ptr == rd_ptr);

嗯,这里要注意一个细节。复位之后,两个指针都指向0,此时FIFO确实是空的。所以复位后empty信号拉高,这个行为是对的。

我个人习惯在判空逻辑里再加一个同步处理,避免跨时钟域的问题。不过同步FIFO的读写时钟是同一个,所以直接比较就行,不用额外操心。

4.2 指针差值为深度判满——小心边界

判满比判空稍微复杂一点。

最朴素的想法是:当写指针比读指针多走了N步(N是FIFO深度),那就满了。用公式表达就是:

判满条件:写指针 - 读指针 == FIFO深度

但这里有个坑——指针是会回绕的。如果FIFO深度是16,写指针从15写到0,读指针还在5,这时候差值怎么算?

我刚开始做设计时就踩过这个坑。直接做减法,15 - 5 = 10,不对啊,实际上写指针已经绕了一圈,差值应该是16 + 0 - 5 = 11。

所以正确的做法是:

assign full = ((wr_ptr - rd_ptr) == DEPTH);

前提是你的指针位宽比深度多一位。比如深度16,指针就用5位。这样减法自然就能处理回绕的情况。

注意:指针位宽必须比深度多1位!否则无法区分「全空」和「全满」——这两种情况下指针数值都可能相等。

4.3 格雷码指针判空满——跨时钟域的利器

上面说的两种方法,在同步FIFO里完全够用。但如果你以后要做异步FIFO(跨时钟域),就得用格雷码了。

格雷码有个特性:相邻两个数之间只有1位不同。这个特性在跨时钟域同步时特别有用——即使采样出现亚稳态,最多也就错一个bit,不会出现大范围错误。

格雷码判空和二进制一样:

assign empty = (gray_wr_ptr == gray_rd_ptr);

判满就有点技巧了。格雷码不能直接做减法,得用另一种方式:

格雷码判满条件:写指针和读指针的最高两位相反,其余位相同。

举个例子,深度8的FIFO,指针用4位格雷码:

  • 写指针:1_100(格雷码,对应二进制1_111)
  • 读指针:0_100(格雷码,对应二进制0_111)

最高位不同,次高位不同,其余位相同——这就是满了。

代码实现:

assign full = (gray_wr_ptr[WIDTH-1] != gray_rd_ptr[WIDTH-1]) &&
              (gray_wr_ptr[WIDTH-2] != gray_rd_ptr[WIDTH-2]) &&
              (gray_wr_ptr[WIDTH-3:0] == gray_rd_ptr[WIDTH-3:0]);

我曾经在一个高速数据采集项目里用过格雷码指针。当时时钟频率跑到500MHz,二进制指针的跨时钟域同步老是出问题,换成格雷码之后,一次搞定。

4.4 三种方法对比

方法 适用场景 优点 缺点
指针相等判空 同步FIFO 简单、直观 只能判空
指针差值判满 同步FIFO 逻辑清晰 需要额外位宽
格雷码判空满 异步FIFO 跨时钟域安全 判满逻辑稍复杂

4.5 知识体系总览

下面这张图帮你理清同步FIFO空满判断的完整逻辑:

同步FIFO空满判断知识体系 空满判断方法 指针相等判空 wr_ptr == rd_ptr 指针差值判满 wr_ptr - rd_ptr == DEPTH 格雷码判空满 判空:指针相等 判满:最高两位相反 核心要点 同步FIFO用二进制指针即可 格雷码是为异步FIFO准备的

4.6 避坑指南

最后分享几个我踩过的坑:

  • 指针位宽不够:深度16的FIFO,指针只用4位,结果满标志永远拉不高。后来发现是回绕后差值算错了。
  • 格雷码转换时机:格雷码要在写入FIFO之前就转换好,不能等到跨时钟域同步时再转。否则中间状态会出问题。
  • 空满同时为高:正常情况下空和满不会同时为高。如果仿真中出现这种情况,八成是指针逻辑写错了。

小技巧:调试空满标志时,可以在仿真波形里同时拉出wr_ptr和rd_ptr,手动算一下差值,跟空满信号对比。这个方法帮我抓出过好几个隐蔽的bug。

好了,同步FIFO的空满判断就聊到这儿。说白了就是三种方法,对应不同的场景。你只要记住:同步用二进制,异步用格雷码,指针位宽多留一位,基本就不会出大问题。

可可精品资料库