数字系统萌新
深度非2的幂次的情况我遇到过。标准格雷码只适用于2的幂次深度。如果深度不是,比如深度6,有几种办法:1. 还是用2的幂次(如8)的格雷码计数器,但只使用其中6个状态,空满判断逻辑要调整,比较麻烦。2. 用普通的二进制计数器,但同步前转换为格雷码,同步后再转回二进制比较,这样复杂度高。3. 直接用二进制计数器加握手信号,但可能性能低。我建议笔试时先说明难点,然后给出一种方案,比如第一种,并指出可能需要额外的状态判断逻辑来避免误判。
深度非2的幂次的情况我遇到过。标准格雷码只适用于2的幂次深度。如果深度不是,比如深度6,有几种办法:1. 还是用2的幂次(如8)的格雷码计数器,但只使用其中6个状态,空满判断逻辑要调整,比较麻烦。2. 用普通的二进制计数器,但同步前转换为格雷码,同步后再转回二进制比较,这样复杂度高。3. 直接用二进制计数器加握手信号,但可能性能低。我建议笔试时先说明难点,然后给出一种方案,比如第一种,并指出可能需要额外的状态判断逻辑来避免误判。
我补充下亚稳态的处理。这是异步FIFO最头疼的地方。指针同步必须用两级或多级触发器打拍,这是经典做法。但要注意,打拍会引入延迟,可能导致“假满”或“假空”,但不会导致数据丢失。为了更可靠,有些设计会用三级触发器。另外,格雷码本身能减少亚稳态传播,因为即使同步时采到变化中的指针,也只会错一位,对应的空满判断误差较小。实际写代码时,同步模块要单独写,用always块和对方时钟。
异步FIFO的关键点确实挺多,我面试时也被问过。空满判断最核心的是用格雷码计数器比较读写指针。注意,比较是在指针同步到对方时钟域后进行的。满标志是写指针追上读指针(但方向相反),空标志是读指针追上写指针。具体来说,满条件:wptr同步后与rptr高两位不同,其余位相同。空条件:同步后的rptr与wptr完全相等。格雷码的设计要保证每次只变一位,通常用二进制转格雷码:gray = (bin >> 1) ^ bin。
简单说几点。空满判断:用格雷码指针,加一个额外位(MSB)来区分是否绕了一圈。满的条件是:指针的MSB不同,但其他位相同。空的条件是:所有位(包括MSB)都相同。
同步就用两级DFF,虽然延迟但可靠。注意同步后的指针用来产生空满信号,可能会有“假满”或“假空”,但这是安全的,不会溢出或读空。
深度不是2的幂次?那格雷码的循环特性没了,比较麻烦。通常做法是调整深度到2的幂次,或者用二进制指针加同步FIFO(但面积大)。笔试时按2的幂次设计就行,面试时可以说知道这个限制。
我面试时被问过好几次。我的经验是,别光背代码,要理解为什么。1. 空满逻辑:空是读写指针相等(包括扩展位),满是写指针比读指针多一圈(看扩展位不同,低N位相同)。格雷码计数器就是在二进制计数器基础上转成格雷码输出。2. 亚稳态:两级同步器是必须的,但同步后的指针会滞后,所以判断空满时要小心,可能“保守”一点(比如早点报满),但不会出错。3. 深度非2幂:我一般直接避免,如果非要实现,可以用双端口RAM加二进制指针,但同步时得多花心思,或者用异步握手,笔试时就说补齐到2的幂次最简单。
异步FIFO的关键点确实很多,笔试面试常考。空满判断的核心是比较读写指针,但因为是异步时钟,直接比较二进制指针会出问题,所以要用格雷码。格雷码相邻码字只有一位变化,能降低亚稳态传播的概率。具体设计时,读写指针各自用格雷码计数器,写指针同步到读时钟域判断空,读指针同步到写时钟域判断满。满的判断是:写指针追上读指针一圈,但格雷码不能直接像二进制那样比较最高位,通常用额外一位作为扩展位来区分。
指针同步就是两级触发器打拍,这是经典做法,虽然会带来延迟但能有效降低亚稳态概率。深度非2的幂次时,格雷码的循环特性会被破坏,一般建议将深度补齐到2的幂次,或者用别的同步方法比如握手,但那样效率低。
1. 空满判断:读写指针都扩展一位。当读写指针的高位不同,低位相同时,表示写比读多跑了一圈,是满。当读写指针完全相等时,是空。指针用格雷码,比较前需要同步。
2. 亚稳态:两级触发器同步是标准做法。更稳健的话可以在同步前让指针寄存器保持稳定一段时间(但通常不必要)。注意同步后的指针会延迟,所以判断空满时要用“同步后的指针”和“本地的当前指针”比较,这会导致保守的空满判断(比如可能提前报满),但保证了正确性。
3. 深度非2的幂次:标准格雷码不能用。可以自己构造一种单位距离码,但很麻烦。常见做法是,如果深度是N,就做一个深度为大于N的最小2的幂次M的FIFO,但逻辑上只使用N个位置。这样指针还是可以用格雷码,只是地址空间有浪费。另一种方法是使用基于LFSR的计数器,也能产生单位距离码,但设计复杂。笔试时把前因后果说清楚就行。
我面试被问过好几次。我的经验是,先别急着写代码,把框图划清楚。两个时钟域,中间是双口RAM。读写地址指针用格雷码计数器生成,然后同步到对方时钟域。
格雷码计数器的设计要小心,不是简单二进制转格雷码,而是直接按格雷码顺序计数。网上有现成的转换公式,但自己推一下更好理解。
深度非2的幂次这个问题很实际,我工作中遇到过。确实,标准格雷码只适用于2的幂次。这时候要么用2的幂次深度(浪费一点空间),要么就用二进制指针加握手信号来跨时钟域,但性能会下降。笔试时如果遇到,可以先按2的幂次来答,然后提一句非2的幂次的情况需要特殊处理。
异步FIFO的关键点确实挺多,笔试面试常考。空满判断的核心是读写指针的比较,但因为是异步时钟,直接比较二进制指针会出错,所以要用格雷码。格雷码相邻码字只有一位变化,能降低跨时钟域同步时出错的概率。满标志是写指针追上了读指针(一圈后),空标志是读指针追上了写指针。具体实现时,通常把读写指针都扩展一位最高位作为绕回标志位,比如深度8,指针用4位,最高位不同而低三位相同时就是满。
指针同步就是两级触发器打拍,写指针同步到读时钟域判断空,读指针同步到写时钟域判断满。亚稳态没法完全消除,但两级触发器能极大降低概率。深度非2的幂次时,格雷码的特性会被破坏,一般建议用2的幂次深度,或者用其他同步机制,比如用异步双口RAM加握手信号,但那样复杂度就上去了。
这个问题我实际做过。除了楼上说的,还有几个细节:指针宽度要比地址多一位,最高位用来指示是否绕回了一圈。比如深度8,地址用3位,指针用4位。满的判断是:写指针比读指针多了一圈(即最高位不同,低地址位相同)。空的判断是:写指针和读指针在同一圈(最高位相同,低地址位相同)。同步时,一定要把格雷码指针同步到对方时钟域,而不是二进制码。深度非2的幂次时,格雷码序列不闭合,可能得用双寄存器同步二进制码,但空满判断会更复杂,容易出错。笔试时通常假设深度是2的幂次。
我来说说我的理解。异步FIFO最麻烦的就是亚稳态和空满判断。1. 格雷码计数器设计:读写指针都用格雷码表示,比如深度8,指针宽度4位(因为要区分满和空,需要多一位)。写时钟域生成写指针格雷码,同步到读时钟域用于空判断;读指针类似。2. 减少亚稳态:打两拍同步器是基本操作,但格雷码本身已经降低了亚稳态传播的风险。3. 深度非2的幂次:强烈建议避免,因为格雷码要求循环特性。如果必须,可以考虑用二进制指针加握手同步,但性能会下降。空满逻辑:满=(wptr_gray同步后与rptr_gray比较,高位相反,其余相同);空=(同步后的rptr_gray与wptr_gray完全相等)。注意比较的是同步后的指针。
异步FIFO的关键点确实挺多,我面试时也被问过。空满判断的核心是比较读写指针,但因为是异步时钟,不能直接比二进制数,要用格雷码。格雷码计数器每次只变一位,同步过去即使有亚稳态,也只会错一个周期,不会出现灾难性错误。满标志是写指针追上了读指针(考虑绕回),但要注意区分是真满还是空。深度非2的幂次时,格雷码的对称性会破坏,一般建议用2的幂次深度,或者用别的同步方法。