CDC(3) 异步FIFO

异步FIFO(Asynchronous FIFO)

FIFO就是一个存储的管道,有进的口,有出的口。同步FIFO就是说进口(写入端)和出口(读出端)是同一个时钟域。FIFO一般深度多于1,就需要两个指针: write pointer和read pointer。对于write pointer和read pointer我们一般用2进制,写入操作(Push)使得write pointer + 1,读出操作(Pop)使得read pointer + 1。

满不能写,空不能读。读的这一侧判断FIFO是否空,在写的这一侧来判断FIFO是否满。

那么当我们要把pointer同步到另外的时钟域进而去比较的时候,我们就遇到了上一讲讨论的multi-bit 同步的问题,即binary counter不能直接利用double flop来同步。

那么我们上一篇讲到的带反馈的asynchronous load模块可以用来同步pointer吗?可以是可以,但是缺点也很明显,即反馈的话要跨两次时钟域,对于效率很有影响。

 

更快的解决方法:格雷码Gray Code

二进制转格雷码:最左一位保留不变,第二位开始每位和左边一位(二进制的)做异或

格雷码转二进制:从左边第二位起,将每位与左边一位解码后的值异或,作为该位解码后的值(最左边一位依然不变)

每个时钟沿来的时候只会有1个bit发生了翻转,其余所有bit都是稳定的!这样即使这一个bit在用2flop synchronizer同步到另外一个时钟域时,可能需要1个周期发生变化,或者2个周期,在发生变化前,另一个域的值就是之前的稳定值,变化后就是新的值,而不会出现其他不该出现的值。省去了反馈,把read pointer同步到write domain来判断满,把write pointer 同步到read domain来判断空,只需要跨一次domain,就可以判断,这样可以提高push和pop的效率。

而对于memory的取址还是得用2进制编码,我们需要做的就是在同步pointer的时候把binary pointer 转化为gray code pointer,然后用2flop synchronizer同步到对面时钟域之后,再来判断空满。

 直接利用gray code来判断空满

FIFO空比较好判断,write pointer == read pointer,用binary或者gray code都行,要求每位都相同。

FIFO满最高的两位相反,最低的两位相同:assign full = (write_ptr_gray[N:N-1] == ~read_ptr_gray[N:N-1])&&(write_ptr_gray[N-2:0] == read_ptr_gray[N-2:0]);

wptr和rptr都是gray code,在上一篇我们已经讨论过gray code是可以直接利用2flop synchronizer来同步的。而用来读写实际的memory必须是binary address,在FIFO write control和FIFO read control 里面我们进行binary to gray code的转换。异步FIFO的框图:

 

 

问题:慢时钟域同步快时钟域格雷码时候,在慢时钟域的一个周期中,经历了两次或多次快时钟域的上升沿,那么对应的格雷码就会有两个或多个bits发生变化,这个不会产生多个bits同步的问题吗?
我们说多个bit发生变化其实是针对source clock的每一个edge来说的,因为不同bit之间发生翻转的时间不能严格对齐,所以会导致destination clock可能看到不同的值,导致最后synchronizer输出会出现错误的值,
从而影响FIFO的空满判断。而gray code在每个source clock的沿只会有一个bit发生翻转,其余bit保持稳定,这样每个destination clock edge来的时候最多也只可能碰到1bit在翻转,这个翻转的bit可能会
给synchronizer的第一级引入metastable,但是最后synchronizer的输出无非就是保持前值或者是更新后的值,而这两个值都是合理的值,不会出现一个错误的值从而导致FIFO空满判断逻辑错误。
虽然慢时钟域同步过来的值可能和之前的值相比有多个bit发生变化,但是这些bit的翻转不是同时发生的,这是回答这道题的关键。

https://mp.weixin.qq.com/s/zjaEQdh4zFUOnfX0In0lng

 

问题:假设wclk速度比rclk快,那么当raddr+1,再同步到wclk后,如果这期间有了push操作,那会不会使得wptr超过了rptr,造成FIFO overflow呢?

回答:不会,当rptr在传过去之前,如果wptr已经追上了rptr-1,那么wfull已经是1了,FIFO是不允许在FIFO 满的时候进行push操作的(在实际工程中我们通常要利用assertion来check保证在wfull为1的时候push不能为1)。而如果这个时候有了pop操作,raddr+1,这个时候实际上FIFO有了一个free entry,但是push这一侧看到的FIFO依然是满的。这就是我们所说的异步FIFO的假满。相应的,FIFO的empty为1时,也可能FIFO此时有个push操作,导致FIFO为假空。假空和假满并不会影响FIFO的正确性,无非就是早一点告诉push side停止push,或者早一点告诉pop side停止pop,但是FIFO是不会产生overflow和underflow的。如果要说有什么缺点的话,就是在性能上有一些损失,当FIFO的深度很大的时候,这通常不是什么问题。

 

问题:如何判断FIFO是真空/真满呢?

回答:判断假空假满刚好相反,在push side我们来判断空,在pop side来判断满

 

问题:设计一个depth=1的异步FIFO

回答:只需要考虑一个问题,只有1个entry,那么需要几位的address 或者pointer呢?当然是1位就够了,那我们真的还需要一个pointer吗?因为只有一个entry,当一次push,FIFO就满了,一次pop,FIFO就空了。1个bit用来表示满和空就足够了。其实这样的FIFO我们已经见过了,带反馈的asynchronous load其实就是depth=1的异步FIFO!

问题:如何设计depth不是2的幂次的异步FIFO?

回答:我们在上一讲里面看到的gray code,只有当depth=2的幂次个数的时候,才能做到wrap around时继续保持gray code的性质:即连续两个码之间只有1位不同。解决这个办法的诀窍其实就是老李上一篇提到的gray code的第二个性质:gray code每一位是有个对称轴的。我们可以这样编码,addr==0的时候gray code不从4'b0000开始,而是从4‘b0001开始,直到4’b1001来wrap around,这样从4'b1001->4'b0001依然只有一个bit翻转。同理,如果是depth=6,那么我们继续往里收缩1位,只利用gray code关于对称轴两侧的部分编码,从4'b0011到4'b1011,我们可以看到,这样的编码依然可以保证相邻两个码之间只会有1位变化。

利用这种编码,FIFO的满判断逻辑就不是简单的高两位取反,低位相同了

 

https://mp.weixin.qq.com/s/x-G-qv1znOdZS4kEQNRnrQ

 

posted @ 2022-04-08 10:17  drutil  阅读(488)  评论(0)    收藏  举报