凡亿教育-龙艳
凡事用心,一起进步
打开APP
公司名片
凡亿专栏 | FPGA必考:异步FIFO的格雷码为什么要比深度多一位?
FPGA必考:异步FIFO的格雷码为什么要比深度多一位?

面试的时候,面试官问了个看似简单的问题:"异步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位相同但最高位不同时,就能区分空和满。

edb94f5841fbe7aab6118458df20f1.png

图1:FIFO空满状态判断示意,读写指针相等时通过最高位区分

二、格雷码的独门绝技

跨时钟域传数据最怕什么?——多位同时翻转产生毛刺

举个例子,二进制从0111(7)跳到1000(8),低三位全变了:1→0、1→0、1→0。如果这几位没有严格同步到达采样沿,就会出现错误的中间值,采样电路可能读到7、6、0等各种值。

格雷码就聪明多了——相邻两个码之间只有1个bit不同。看下面的对比:

b605b3b6cd34d70b8b33c03904ee18.png

图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跳变。

9b7de8d6ba07964d16279652062adf.png

图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。这个公式面试的时候直接写出来,面试官都得高看你一眼。

c4e69f416c687da285c809988f43bf.png

图4:异步FIFO结构框图,双时钟域通过格雷码同步器传递指针

五、实际设计时的注意事项

有些同学可能会想:"既然格雷码是为了解决亚稳态,那我读写两侧都用格雷码,是不是就没问题了?"

不完全是。格雷码只在单bit变化时有天然优势,如果编码设计不当,照样会出幺蛾子。常见的坑:

① 格雷码表必须自己做,不能瞎编。网上有些帖子直接给个表就完事了,根本没解释原理。自己推导一遍印象才深。

② 二进制转格雷码的公式要记牢:格雷码 = (二进制 >> 1) ^ 二进制。这个异或运算很巧妙,右移一位再和原值异或,得到的正好是格雷码。

③ 同步器级数要足够。格雷码跨时钟域后,通常要用两级触发器做同步,防止亚稳态传播。不过这个是另一个话题了,今天先不展开。

总结一下

异步FIFO里格雷码多一位的原因,说白了就两点:

第一,指针必须能区分空和满两种状态。FIFO深度是2^n时,指针要遍历0到2^(n+1)-1的所有值才能完成循环计数,这需要n+1位。

第二,格雷码是二进制的编码形式,位宽必须和二进制指针保持一致。格雷码的价值是单bit跳变,但如果位宽不够,连完整的地址范围都表示不了,还谈什么亚稳态优化?

这个问题在面试和笔试里出现频率极高,但凡你以后想从事FPGA相关的工作,这个知识点必须彻底搞懂。觉得有收获的话,转发给你身边还在死记硬背的同学吧。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表凡亿课堂立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。
相关阅读
进入分区查看更多精彩内容>
精彩评论

暂无评论