面试的时候,面试官问了个看似简单的问题:"异步FIFO的深度是8,那它的读写指针各需要几bit?"很多人脱口而出"3bit",结果直接被挂掉了。原因很简单——FIFO深度为2^n时,指针需要n+1位。这个问题没答对,后面的异步处理、格雷码编码全是白搭。
说起来,格雷码在异步FIFO里的作用大家都清楚:减少跨时钟域数据传输时的亚稳态风险。但为什么要多出一位来存这个指针,很多人其实没真正理解。今天咱们就来把这个点彻底讲透。
一、先搞清楚指针到底在干啥在FIFO里,读指针指向下一个要读的位置,写指针指向下一个要写的位置。这俩指针本质上就是地址计数器,用来标记"现在FIFO里有多少数据"。
假设FIFO深度是8(也就是2^3),那地址范围是0到7,一共8个位置。从0数到7,用二进制表示就是000到111,确实只需要3个bit。
问题来了——FIFO的空状态和满状态怎么判断?
当FIFO为空时,读指针等于写指针;当FIFO为满时,读指针和写指针也相等。等等,俩指针相等,既可能代表空,也可能代表满,这不就矛盾了吗?
其实这里有个关键设计:我们在地址的最高位再加1位,用来区分"转了一圈回来"的情况。对于深度8的FIFO,指针从0数到15,用4bit才能完整表达。当指针高3位相同但最高位不同时,就能区分空和满。

图1:FIFO空满状态判断示意,读写指针相等时通过最高位区分
二、格雷码的独门绝技跨时钟域传数据最怕什么?——多位同时翻转产生毛刺。
举个例子,二进制从0111(7)跳到1000(8),低三位全变了:1→0、1→0、1→0。如果这几位没有严格同步到达采样沿,就会出现错误的中间值,采样电路可能读到7、6、0等各种值。
格雷码就聪明多了——相邻两个码之间只有1个bit不同。看下面的对比:

图2:二进制码与格雷码对照表,注意每次跳转只有1bit变化
二进制 → 格雷码对照(以4位为例): 0000 → 0000 1000 → 1100 0001 → 0001 1001 → 1101 0010 → 0011 1010 → 1111 0011 → 0010 1011 → 1110 0100 → 0110 1100 → 1010 0101 → 0111 1101 → 1011 0110 → 0101 1110 → 1001 0111 → 0100 1111 → 1000
注意到了吗?每一步都只有1个bit变化。即便是最大跨度的跳转(比如0111→1000,格雷码是0100→1100),也只需要1bit翻转。亚稳态问题?不存在的。
三、为什么格雷码也要多一位铺垫完了,回到正题。格雷码的位数,必须和二进制指针的位数一致——因为格雷码本质上只是二进制的一种编码形式,它的位宽反映的是被编码数值的范围。
深度8的FIFO,二进制指针需要0~15共16个值,所以格雷码也得能表示16个值。16个值需要4bit编码,3bit只能覆盖8个值,根本不够用。
这里可能有人会问:"我只用3bit格雷码来表示0~7,8个值,不也能覆盖地址范围吗?"
按我的经验,这种想法在理论推导时好像行得通,但实际会有个致命问题:当指针从7回到0的时候,格雷码的变化可不是只有1bit翻转。看看上表,7的格雷码是0100,0的格雷码是0000,看起来没问题。但7和0之间没有直接的格雷码映射关系,循环使用时会出现多bit跳变。

图3:格雷码的循环特性,相邻码之间保持单bit跳变
更根本的原因是:格雷码多一位的根本原因,是为了覆盖完整的循环计数范围,让"空"和"满"能用不同的最高位状态来区分。没有这多出的一位,空满判断逻辑根本没法实现。
【核心结论】
FIFO深度为2^n时:
• 二进制指针需要n+1位
• 格雷码作为二进制的编码形式,也需要n+1位
• 多出的一位用于区分"空"和"满"两种状态
深度8的FIFO:地址范围0~7,二进制指针4bit(0000~1111),格雷码也4bit。能表示16个值,空满状态用最高位区分。
深度16的FIFO:地址范围0~15,二进制指针5bit(00000~11111),格雷码也5bit。能表示32个值。
深度32的FIFO:地址范围0~31,二进制指针6bit,格雷码也6bit。能表示64个值。
发现规律了吧?格雷码位宽 = ⌈log₂(深度)⌉ + 1。这个公式面试的时候直接写出来,面试官都得高看你一眼。

图4:异步FIFO结构框图,双时钟域通过格雷码同步器传递指针
五、实际设计时的注意事项有些同学可能会想:"既然格雷码是为了解决亚稳态,那我读写两侧都用格雷码,是不是就没问题了?"
不完全是。格雷码只在单bit变化时有天然优势,如果编码设计不当,照样会出幺蛾子。常见的坑:
① 格雷码表必须自己做,不能瞎编。网上有些帖子直接给个表就完事了,根本没解释原理。自己推导一遍印象才深。
② 二进制转格雷码的公式要记牢:格雷码 = (二进制 >> 1) ^ 二进制。这个异或运算很巧妙,右移一位再和原值异或,得到的正好是格雷码。
③ 同步器级数要足够。格雷码跨时钟域后,通常要用两级触发器做同步,防止亚稳态传播。不过这个是另一个话题了,今天先不展开。
总结一下异步FIFO里格雷码多一位的原因,说白了就两点:
第一,指针必须能区分空和满两种状态。FIFO深度是2^n时,指针要遍历0到2^(n+1)-1的所有值才能完成循环计数,这需要n+1位。
第二,格雷码是二进制的编码形式,位宽必须和二进制指针保持一致。格雷码的价值是单bit跳变,但如果位宽不够,连完整的地址范围都表示不了,还谈什么亚稳态优化?
这个问题在面试和笔试里出现频率极高,但凡你以后想从事FPGA相关的工作,这个知识点必须彻底搞懂。觉得有收获的话,转发给你身边还在死记硬背的同学吧。
暂无评论