大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看完后喜欢的话就请关注我吧~谢谢~
在这篇文章中,我们来聊聊多比特信号跨时钟域的一大神器,异步fifo。这也是我们在面试中,最常被问到的一个知识点,需要牢牢掌握。
一、fifo的结构
fifo( First in First out)即先入先出的数据管道,有同步fifo和异步fifo之分。在这里我们讨论的主要是异步fifo,其大体结构如下所示:
可见fifo内部有一个memery用于储存数据,此外从端口上分,其可分为读端和写端两个部分,共有如下信号:
写端:
wclk: 写时钟
wr_inr:写使能
wr_data:写数据
full:fifo写满指示信号
读端:
rclk:读时钟
rd_inr:读使能
rd_data:读数据
empty:fifo读空指示信号
上面的时钟,使能,数据信号都是比较常规的信号,唯二特别的是full和empty信号,这两个信号也是fifo中比较重要的信号。要理解这两个信号,我们就首先要从fifo的原理出发。
二、读写指针
fifo的一端用于读数据,一般用于写数据。两端的读写时钟并不相同。为了对读数据和写数据的数量进行统计,我们采用读时钟域下的读指针(read pointer)和写时钟域下的写指针(writer pointer)进行统计,如下图所示:
该图展示的是一个深度为8的异步fifo读写指针示意图,至于为什么深度为8,似乎用3比特指针即可,但图中的指针却有4比特呢?这是因为,如果我们少了这一标志位,我们就无法判断fifo,到底是“空”,还是“满”!首先先来接受fifo空的具体含义:
fifo空,顾名思义即fifo中的数据已经被读端读空了,因此表现在指针上则是读指针==写指针,即:
assign empty =(rd_ptr==wr_ptr)
空情况的示意图如下图所示:
fifo满,顾名思义即fifo中的数据已经写满了,写入的数据量已经达到了fifo容量的极限,因此表现在指针上是写指针超过了读指针一整圈!如果我们指针的宽度和fifo实际容量相同的话,那么我们就无法区分到底是“空”还是“满”,因此我们需要一个标志位来代表这超出的一圈!
也就是说“满”的判断条件是最高位相反,其他位相同时,即:
assign full =(rd_ptr[n-1:0]==wr_ptr[n-1:0])&& (rd_ptr[n]== !wr_ptr[n])
示意图如下图所示:
三、格雷码
在得到了读指针和写指针与空满信号的关系之后,我们还需要讨论一下,读写指针的跨时钟域问题,由于指针是一个多比特信号,因此需要采用特殊的方式进行跨时钟域,如果采用多周期路径法的话,固然可以将指针传递到另一个时钟域,但是由于在数据传输过程中数据需要保持稳定,因此fifo的传输效率就要大大折扣。
但是我们如果直接使用打两拍的方式进行同步的话,虽然可以提高效率,但是也可能采集到错误的数据,从而产生严重的后果。那么我们有什么方式可以解决这个问题吗?当然有!由于我们的指针是按顺序跳变的,因此,我们可以使用一个“神器”——格雷码(Gray Code),它有如下特点:
每相邻两位数据,只有一位不同。
基于该特点,如果在采用格雷码编码的指针进行跨时钟域时,即使发生了采样错误,那么最多也只会采集到变化之前的数据,而不会采集到错误的数据!这样一来,我们使用格雷码编码的指针使用打两拍的方式进行跨时钟域,可以提升fifo的工作效率。
以下是格雷码和二进制码的转换关系:
有二进制码:B[N-1]B[N-2]B[N-3]…B[1]B[0]
格雷码:G[N-1]G[N-2]G[N-3]…G[1]G[0]
-
二进制码转格雷码
最高位保留:G[N-1]=B[N-1]
其他位:G[i]=B[i+1] ^ B[i] (i=0,1,2…n-2)
-
格雷码转二进制码:
最高位保留:B[N-1]=G[N-1]
其他位:B[i]=B[i+1] ^ G[i] (i=0,1,2…n-2)
在得到了二进制转格雷码的关系式后,我们就有了新的格雷码编码下的空满判断条件:
空:依然是读写指针相同:
assign empty = rd_ptr_gray == wr_ptr_gray
满:最高两位相反,低位相同:
assign full = (wr_ptr_gray[N:N-1] == ~rd_ptr_gray[N:N-1]) &&
(wr_ptr_gray[N-2:0] == rd_ptr_gray[N-2:0])
四、真满空与假满空
最后,我们还需要提一下,虽然我们通过使用格雷码和打两拍的方式,将指针跨时钟域的效率提到了最高,但是还是需要花费2-3个周期进行同步的。这就导致了,我们在进行空满比较时,实际上是用现在的读写指针和之前的写读指针进行比较,即:
- 判断假满:在写时钟域下,使用写时钟域下的写指针wr_ptr_gray和同步过来的读指针rd_ptr_gray_sync进行比较。rd_ptr_gray_sync相比真正的rd_ptr_gray要滞后,因此当fifo接近满的时候,full信号就会为1,从而阻止对FIFO继续写入。
- 判断假空:在读时钟域下,使用读时钟域下的读指针rd_ptr_gray和同步过来的写指针wr_ptr_gray_sync进行比较。同样的,wr_ptr_gray_sync相比真正的wr_ptr_gray要滞后,当FIFO接近空,但是实际可能还没空的时候,Empty信号就会为1,从而阻止对FIFO数据的读取。
这就是假满与假空,这种假满,假空只会造成fifo所能使用的空间少了一层,但是并不会造成数据读写出错。
那么,什么是真正的满空呢?
我们只要与判断假空满相反,在写时钟域判断空,在读时钟域判断满,就可以得到真正的满空信号!
这是通过利用同步后的指针的滞后性得到的,比如:
- 判断真空:在写时钟域,通过滞后的读指针rd_ptr_gray_sync都得到了空,那fifo必然空了。
- 判断真满:在读时钟域,通过滞后的写指针wr_ptr_gray_sync,都得到了满。那fifo比如真正的满了。
在实际运用中,我们主要使用的还是假满空信号,因为,假满空更加安全,能确保数据不会出错。
最后再提示一下,格雷码的单比特变化,只会发生在相邻的格雷码之间,因此数据不能使用格雷码的方式进行夸时钟域传递。此外,我们也可以在进行状态机的编码时采用格雷码的方式,以减少数据翻转,从而降低动态功耗。
如果你喜欢这篇文章的话,请关注我的公众号-熊熊的ic车间,里面还有ic设计和ic验证的学习资料和书籍等着你呢~欢迎您的关注!
标签:gray,比特,05,fifo,wr,ic,时钟,ptr,指针 From: https://blog.csdn.net/demayiya/article/details/136691105