多个控制信号跨时钟域 仅仅通过简单的同步器 同步有可能是不安全的 。
如何传递两个同时需要的信号(b_load和b_en)?
将 b_load和b_en同步至a_clk时钟域,
如果 b_load和b_en这两个信号有一个小的skew,
将导致在 a_clk时钟域中两个信号并不是在同一时刻起作用,与在b_clk中的逻辑关系不 同
将b_load和b_en信号在b_clk时钟域中合并成一个信号b_lden,然后同步至a_clk中。
问题:遇到不能合并的情况,怎么办?
例子:译码信号
如果bdec[0]、bdec[1]间存在 skew将导致同步至a_clk中后 译码错误,出现误码。
在这种情况下,建议加入另一个控制信号,确保bdec[0]、bec[1]稳定时采信号。
例如,在bdec[0]、bec[1]稳定输出后一到两个周期b_clk域输出一个en信号,通知 a_clk域此时可以采bdec[0]、bec[1]信号。
这样可确保正确采样。
问题:多bit数据流跨时钟域如何传输?
数据流和指示信号不同:
解决传输的两个方法:
FIFO( first in first out )是一种先进先出的储存结构
没有外部读写地址线 ,使用简单。
只能顺序写入数据,顺序的读出数据,其数据地址 由内部读写指针自动加1完成,不能像普通存储器那 样可以由地址线决定读取或写入某个指定的地址。
随着IC的发展,模块与模块之间的通信设计中,多时钟的情况已经不可避免;数据在不同时钟域之间的传输很容易引起亚稳态;异步FIFO就是一种简单、快捷的解决方案。
FIFO的功能类似于一个调节上下游水量的一个蓄水池。
FIFO 的上游结点是FIFO的数据输入端,
在写信号有效时,数据将被写入FIFO,由FIFO内部的写指针控制,并且在FIFO内部,写指针递增一个单元,同时FIFO的满信号(FIFO fulll Signal)将控制上游结点是否发送数据;
FIFO的下游节点是FIFO 的数据输出端,
当读信号有效时,FIFO中的数据将被读出,由FIFO内部的读指针控制,并且在FIFO内部读指针递增一个单元,同时FIFO空信号(FIFO empty Signal)将控制下游节点是否读出数据。
FIFO内部的空间已经被写满
如果FIFO内部的空间已经被写满,则实时生成满信号,以反压上游节点,上游节点停止写新的数据进来,否则就会把已经写好的数据冲掉。
FIFO内部的数据全部被读
如果FIFO内部的数据全部被读,则实时生成空信号,控制下游节点不再进行数据读操作。否则,下游节点就会将读过的数据重新再读一遍。
从这里也可以看出,空信号和满信号对于FIFO的控制非常重要。
FIFO主要用来调节上下游数据的吞吐量。
用途1:异步FIFO读写分别采用相互异步的不同时钟
在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟, 多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。
用途2:对于不同宽度的数据接口也可以用FIFO
例如,单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。
FIFO通常来说分为:
FIFO的宽度:即FIFO一次读写操作的数据位
FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。FIFO 的深度相当于蓄水池的容量,如果过小,则上游节点总是接收满信号,使得系统的数据吞吐量降低。如果过大,则浪费空间太多。因此,FIFO的深度是一个很关键的参数。
满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。(在FIFO中,读写两个时钟通常都不相同,而且基本上是异步时钟。)
双端口SRAM:
用来存储上游节点写入的数据wdata,下游节点用rdata将其读出。
SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出按顺序进行,写和读到最高地址后,重新返回零地址。
满信号生成电路
在上游节点和SRAM之间有一个满信号生成电路。
这个电路通过判断写时钟域下,写指针和读指针的关系,然后实时生成满信号wfull,以通知上游节点停止写操作。
空信号生成电路
在下游节点和SRAM之间有一个空信号生成电路,
这个电路通过判断读时钟域下,写指针和读指针的关系,然后实时生成空信号rempty,以通知下游节点停止读操作。
注意:
将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,因此,这里就涉及到如何处理信号传输的亚稳态问题。
正确的产生空满标志是任何FIFO设计的关键。
空满状态产生的原则是:写满而不溢出,能读空而不多读。
空满状态的判断:
空状态:
当读写地址相等时,说明已经写入的数据,已经全部被读走,此时,FIFO还尚未有新的数据写入,说明FIFO为空。
空状态情况发生:
判断条件:
如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空。
满状态:
当写地址超过读地址,写到最高地址后,重新从零开始写,再次追上了读地址。此时,读地址已经读过的地址空间,再一次被写地址写入。而读地址到最高地址之间的数据,还尚未被读。说明此时FIFO处于满的状态。
判断条件:
如果两个指针的最高有效位MSB不同,说明写指针比读指针多折回了一次;
如r_addr=0000,而w_addr = 1000,为满。
区分满状态、空状态方法:
在地址位中添加一个额外的位(extra bit)。
当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零.
对读指针也进行同样的 操作。
此时,对于深度为的FIFO,需要的读/写指针位宽为(n+1)位,
例子:深度为8的FIFO
需要采用4bit的计数器, 0000~1000、 1001~1111,
MSB作为折回标志位,而低3位作为地址指针。
满状态判断条件:
如果两个指针的MSB不同,说明写指针比读指针多折回了一 次。
例子:r_addr=0000,而w_addr = 1000,为满。
空状态判断条件:
如果两个指针的MSB相同,则说明两个指针折回的次数相等。 其余位相等,说明FIFO为空。
空满标志生成的前提:
读指针被传递到了写时钟域,写指针被传递到了读时钟域。
如下图电路所示,各自采用两级同步器来对信号进行同步。
二进制的读写指针位宽通常超过了1bit。
多bit信号是不可以使用两级同步器的。(比如刚开头的例子)
解决方法:使用格雷码
格雷码的特点:
引用格雷码之后,相邻数值只有1位发生翻转,1位翻转所引起的亚稳态的概率远 远要小于几位同时翻转所引起的概率。因此,格雷码能很好的亚稳态出现的概率。
所以,传输读写指针之前,需要将其先由二进制转为格雷码,然后再发送到对方的时钟域下。
二进制码和格雷码的转换:
二进制码转化为格雷码:
从最右边第一位开始,依次将每一位与左邻 一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。
格雷码转化为二进制码:
从左边第二位起,将每位与左边一位解码后的 值异或(XOR),作为该位解码后的值(最左边一位依然不变)。
“空”状态的判断:
空 :依据二者完全相等(包括MSB)(和二进制码判断一样)
“满”的判断:
由于 gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,注意地址增加到最大地址7时,然后会返回0,所以此处8和0虽然轮次不同,但是实际上是同一个地址。此时,除了MSB,其余位皆相同,就不能说它为满。
在gray码上判断为满必须同时满足以下3条:
设计之前要清楚:
分析清楚数据轻载和重载时数据的传输任务
如果FIFO能在重载的时候满足需求,轻载的时候,肯定可以胜任。
例子:
假设FIFO的写时钟为100MHz,读时钟为80MHz。在FIFO输入侧,每100个写时钟,写入80个数据;读数据侧,假定每个时钟读走一个数据。
请问FIFO深度设置多少可以保证FIFO不会上溢出和下溢出?
在FIFO的输入侧 每100个写时钟,写入80个数据。
其数据传输分布可能有如图所示的几种情况:
重载时数据的传输任务(要求缓存能力最高的情况):
背靠背的情况:
第一段的100个写时钟周期中,数据传输发生在后80个;
第二段的100个写时钟周期中,数据传输发生在前80个。
这样就会有160个周期,在连续传输数据。其他情形下,都没有这么重负荷的数据的传输任务。
分析重载情况:
连续传输数据的周期称为 burst长度。
重载情况下,burst=160。在 burst长度内,写入端写入160个数据。
burst持续时间=160*写时钟周期
这段时间内能读出数据的个数=burst持续时间/读时钟周期数
FIFO depth= burst长度 - 读出数据的长度
所以FIFO深度至少大于等于32才可以。
FIFO深度计算总结:
连续传输数据的周期称为 burst长度:burst_length表示这段时间写入的数据量
burst的持续时间:burst长度*写时钟周期 (burst_length / W LK )
读的实际速度:(RCLK*(X/Y)),
burst的持续时间这段时间读出的数据量:burst的持续时间*读的实际速度
(burst_length / W LK )*(RCLK*(X/Y))
理论上的 FIFO的最小深度: 写入和读出两者之差为FIFO中残留的数据
burst_length - (burst_length / W LK )*(RCLK*(X/Y))
FIFO设计中的两个关键问题:
这两个问题主要在FIFO结构中的下面红色方框模块中
为什么需要格雷码做读写指针?
由于异步FIFO是工作在两个不同的时钟域。
如果某个时刻,读地址从0111->1000转变
同时,写时钟要在这个时刻采样读地址,得到的值有可能是0000~1111中的任一个值
这个不确定的读地址值会导致空满状态判断错误。
格雷码的特点:
例子:
假设FIFO深度为8,则读写指针可采用格雷码进行编码
采用格雷码,指针每次只变化一次。地址7与地址0,也是每次只变化一次。
问题:
假设FIFO深度为6,如果读写指针继续采用格雷码,那么当前首尾指针的所有比特位都不相同。(首地址的指针 000 尾地址的指针 111)
此时,如果从尾部返回首部,则无法实现消除亚稳态的目的。
如何解决?
可将地址为5的指针设定为100,此时其与首地址的指针 “000”相差一个bit位,与地址为4的指针“110”也相差一 个bit位,满足消除亚稳态的要求。
问题1: 如何在2次幂和非2次幂深 度下设计相应的读写指 针?
问题2:什么情况下,需要用格雷 码来进行读写指针编码?
问题3:什么时候不可以使用格雷码
并不是一定要用格雷码做读写指针,而是当FIFO深度为2次幂的时候,刚好格雷码满足消除亚稳态的需求;在非2次幂FIFO深度情况下,格雷码已经不再适用。
非2次幂FIFO深度情况的解决方法 通常有:
① 若FIFO深度为偶数,可采用最接近的2次幂的格雷码编码,在此基础上修改;
② FIFO深度为一般数值时,可自行设计一种逻辑电路,或者查找表,以实现指针每次只跳变一次的功能;
③ 以上方法通常在设计层面较为复杂,若无特定需求,可将FIFO深度设置为2次幂,浪费一些存储空间,来化简控制电路的复杂度。
亚稳态不能从根本上消除,但可以通过采取一定的措施使其对电路造成的影响降低。
如果指针为格雷码,失效的后果?
格雷码一次只有一位数据发生变化,这样在进行地址同步的时候,只有两种情况
分析地址同步出错
假设写地址 000->001 读地址同步出错 写地址变成 000->000
也就是地址没有发生跳变。
但是用这个错误的写地址去做空判断不会出错, 最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。
gray码:
保证的是同步后的读写地址即使在出错的情形下依然能够保证 FIFO功能的正确性,当然同步后的读写地址出错总是存在的。
注意:
gray码 只是在相邻两次跳变之间才会出现只有1位数据不一致的情形 ,超过两个周期则不一定。
所以,地址总线bus skew一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。
将地址总线连续打两拍 ,为了避免亚稳态传播。
理论上打两拍不能消除亚稳态现象,因为时钟异步,亚稳态不可避免,但是可以极大降低亚稳态传播的概率。
多拍能够将亚稳态出现的概率进一步降低。
FIFO静态时序分析:
低频情况下
STA不需要分析这里的异步时序,因为寄存器都可以在一拍内将亚稳态消除,恢复到正常0/1态。
高频情况下(尤其在28nm工艺以下)
需要检查两级触发器的延迟,保证延迟低,这样可以系统的MTBF。
rptr同步两个“wclk”后,在wclk时钟域与wprt进行比较,生成full信号
wptr同步两个“rclk”后,在rclk时钟域与rprt进行比较,生成empty信号
分析FIFO空满标志:
假设,读指针rptr地址为3,在写时钟控制下,同步两个时钟进入写时钟域,即信号rptr_sync_2
这时,在写时钟域下,刚好满足“满”的条件,生成满信号。
但是,此时rptr的值已经更新为5。
即在FIFO认为自己已经满的时候,读地址,又从里面读走了两个数据。实际FIFO并没有真正的满,只是接近满“虚满”。
结论:
总结:
异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,此机制保证 了FIFO在空满极限情况下,依然留有余量,存在一定的冗余空间。
这种方法使得FIFO不会发生写满溢出、读空多读的情况。
从电路结构上分析, FIFO主要包括
在逻辑综合阶段,这几部分都需要单独处理。
逻辑综合阶段处理:
Memory:需要例化memory compiler生成的库文件。
写时钟域电路、读时钟域电路:分别定义不同的时钟。
静态时序分析:
布局布线:
P&R时,要注意将两级同步器放置在一起,不要被工具自动分开。
参考视频:
芯动力——硬件加速设计方法(3.2、3.3、3.4)