IP之间交互的信号分为总线信号以及其他数据/控制信号,总线信号如AXI和AHB协议,所有采用AXI协议的总线时钟都是ACLK,所有的总线信号都同步到ACLK,所以总线信号不存在跨时钟域的问题,而对于其他数据/控制信号,当一个时钟域的数据/控制信号传输到另一个时钟域时,其可能在建立时间或保持时间窗口内发生跳变,从而可能发生亚稳态。如下图所示,两个DFF分属两个不同的时钟域a和b,时钟域a的数据adat(其有效电平持续一个时钟周期)需要传输到时钟域b,由于adata对于bclk是异步的,所以有可能adata在DFF的建立时间的窗口期内发生跳变,造成bdat1是一个不确定的状态,也就是DFF1出现了亚稳态,注意,跨时钟域并不一定会出现亚稳态,只是有可能会出现。解决跨时钟域亚稳态的方案有很多,主要有两级同步和异步FIFO。
如下图所示,利用两级同步解决跨时钟域的亚稳态问题,如果第一级触发器可以在一个bclk周期内从亚稳态回到稳定态,那么第二级就不会出现亚稳态,否则亚稳态将向后传播,实际应用中,两级同步便可以阻断亚稳态的传播,使用两级同步后,亚稳态继续向后级传播概率极其低,因此两级同步并不是消除了亚稳态,而是降低了亚稳态发生的概率,使其在可接受范围内。显然,使用三级同步甚至更多级同步可以进一步降低亚稳态发生的概率,那么为什么还使用两级同步呢?有两个方面的原因,一是对于大多数芯片而言,使用两级同步后,亚稳态发生的概率在可接受的范围内,二是两级同步是打两拍,三级同步是打三拍,更多级同步意味着芯片的速度将受到影响,但对于车规级芯片,其对可靠性要求较高,在处理跨时钟域时需要使用三级同步甚至四级同步。
在上面的例子中,第一级触发器如果在一个时钟周期内从亚稳态回到稳定态,那么亚稳态就不会继续向后级传播,但其回到稳定态输出0或1是不确定,从而可能出现数据错误。在实际设计中,一个时钟域的数据/控制信号传输到另一个时钟域时,其有效电平往往会持续多个时钟周期,即使在第一个时钟沿采到的是不稳定的值,在下一个时钟沿可能就采到稳定的值。
对于单比特信号,其两级同步需要额外消耗一个触发器,对于16比特的数据线,其两级同步需要额外消耗16个触发器,而触发器的面积是比较大的,因此,为了节省芯片面积,对于多比特信号的两级同步,通常采用如下方案,该Data bus有一个Data enable A,当Data bus有效时,Data enable A同样有效,那么可以对Data enable A进行两级同步,当Data enable A为稳定的高电平时,Data bus也应当是稳定的。
如果读时钟和写时钟频率相同,那么FIFO是同步FIFO,否则FIFO是异步FIFO,FIFO有以下特性:
FIFO的宽度:数据的位宽
FIFO的深度:可存储数据的个数
满标志:FIFO已满或即将满时送出的标志信号,以阻止继续向FIFO写数据而造成溢出(overflow)
空标志:FIFO已空或即将空时送出的标志信号,以阻止继续从FIFO中读数据而读出无效数据(underflow)
FIFO可以产生三种中断,overflow中断、underflow中断和watermark中断,可能有种需求是向FIFO写入一个数据就可以产生中断,可以设置watermark为1,当写入一个数据时,便可以触发watermark中断。
FIFO设计的关键的产生可靠的满标志信号和空标志信号,当向FIFO写入一个数据时,写指针加1,当从FIFO中读一个数据时,读指针加1,一个深度为8的FIFO,读写指针宽度为3,复位后,读写指针都为3'b000,FIFO为空,产生空标志信号,考虑一种极端情况,当写完最后一个数据,写指针折回为3'b000,而还没开始读操作,读指针依然为3'b000,虽然此时读写指针相同,但FIFO写满了,显然,读写指针相同并不能区分空满状态。为了便于区分空状态和满状态读写指针分别加一个额外的位(MSB),依然考虑极端情况,当写完最后一个数据时,写指针为4'b1000,说明写操作已经折回了,MSB为折回标志位,而还没开始读操作,读指针依然为4'b0000,但写指针的折回标志位为1,而读指针的折回标志位为0,因此,很显然FIFO是满状态。
显然,在写操作时,要比较读写指针,在读操作时,也需要比较读写指针,需要把读指针传递到写操作侧,同时也需要把写指针传递到读指针侧,对于同步FIFO,读写指针的传递当然不会有任何问题,而对于异步FIFO,读写指针的传递是跨时钟域传输,可能出现亚稳态,为了降低亚稳态发生的概率,必须对读写指针的跨时钟域传输做两级同步处理,且读写指针在被传输前转换成格雷码,由于格雷码两个相邻数之间只有一位发生变化,因此可以进一步降低亚稳态发生的概率。
二进制码和格雷码的优缺点
二进制码是计算机中数据的自然形式,数据运算和存储都是以二进制的形式,格雷码相邻两个数之间只有一位发生变化,因此格雷码相比二进制码降低了误码的可能性,且降低了功耗,但二进制码转换成格雷码需要额外的逻辑资源。
二进制码转换成格雷码如下图所示
格雷码转换成二进制码如下图所示
怎么计算FIFO的最小深度?
1、写时钟快于读时钟
假如写时钟频率为60MHz,读时钟频率为30MHz,一个时钟周期可以读写一个数据,写完一个数据需要等待1个时钟周期,读完一个数据需要等待2个时钟周期,一次突发burst长度为120,实际上,写一个数据需要2个时钟周期,读一个数据需要3个时钟周期,写完120个数据需要2*120*(1000/60)=4000ns,4000ns可以读4000ns/3/(1000/30)=40,那么FIFO的深度至少为120-40=40,才能保证来不及读的数据不会丢失。
2、读时钟快于写时钟
假如写时钟频率为20MHz,读时钟频率为60MHz,一个时钟周期可以读写一个数据,写完一个数据需要等待2个时钟周期,读完一个数据需要等待1个时钟周期,一次突发burst长度为120,实际上,写一个数据需要3个时钟周期,读一个数据需要2个时钟周期,写完120个数据需要3*120*(1000/20)=18000ns,18000ns可以读18000/2/(1000/60)=450,显然永远不会出现来不及读而导致数据丢失,因此FIFO的最小深度为1。
3、读写时钟频率相同
假如写时钟频率为50MHz,读时钟频率为50MHz,一个时钟周期可以读写一个数据,写完一个数据需要等待1个时钟周期,读完一个数据需要等待2个时钟周期,一次突发burst长度为120,实际上,写一个数据需要2个时钟周期,读一个数据需要3个时钟周期,写完120个数据需要2*120*(1000/50)=4800ns,4800ns可以读4800/3/(1000/50)=80FIFO的深度至少为120-80=40,才能保证来不及读的数据不会丢失。
4、读写时钟频率相同
假如写时钟频率为50MHz,读时钟频率为50MHz,一个时钟周期可以读写一个数据,写完一个数据需要等待2个时钟周期,读完一个数据需要等待1个时钟周期,一次突发burst长度为120,实际上,写一个数据需要3个时钟周期,读一个数据需要2个时钟周期,写完120个数据需要3*120*(1000/50)=7200ns,7200ns可以读7200/2/(1000/50)=180,显然永远不会出现来不及读而导致数据丢失,因此FIFO的最小深度为1。
对于异步FIFO,写时钟频率必须大于读时钟频率,或者写操作必须快于读操作,使用异步FIFO才有意义,而对于同步FIFO,写操作必须大于读操作,使用同步FIFO才有意义。对于上面的第2和4种情形,其实是不需要使用FIFO的,使用FIFO的本质原因是防止写入的数据来不及读而丢失。
下图是利用异步FIFO解决跨时钟域的亚稳态,与两级同步相比,异步FIFO的可靠性高,但异步FIFO速度比较慢,且面积大,在实际应用中,不推荐使用dual port ram,通常使用触发器搭。
同步复位和异步复位
异步复位是指不管时钟沿是否到来,只要复位信号有效,就可以复位,而同步复位则是指复位信号在有效时钟沿时才有效,因为标准单元库中的DFF有异步复位端口,因此异步复位可以直接实现,而实现同步复位需要额外的逻辑(复位信号和输入通过一个与门给到触发器的D端),因此异步复位更节省资源,但异步复位可能因为复位信号中的毛刺而出现假复位,在SOC芯片中,整个芯片的复位信号是由专门的复位控制器产生的,可以避免复位信号中出现毛刺,因此为了节省资源,SOC芯片通常采用异步复位。
异步复位有去除时间和恢复时间的概念,假设低电平复位
去除时间(removal time): 在有效时钟沿到来之后,异步复位信号继续保持低电平的最短时间,类似于保持时间
恢复时间(recovery time): 在有效时钟沿到来之前,异步复位信号恢复为高电平的最短时间,类似于建立时间
由于异步复位信号和与目标时钟的时序关系无法确定,如果异步复位信号在有效时钟沿附近释放,那么触发器可能出现亚稳态,避免这个问题的思路是将异步复位信号的释放同步到目标时钟,即异步复位,同步释放。
如下的电路,DFF1和DFF2使用异步复位信号rst_async_n进行复位,DFF1的D端为1,在复位之前,rst_sync_n为1,当rst_async_n为0时,DFF1和DFF2复位,rst_sync_n为0,在clk的有效上升沿附近,rst_async_n释放,此时DFF1可能出现亚稳态,因为此时到底执行的是异步操作还是同步操作,是不确定的,如果执行的是异步操作,DFF1的Q端为0,如果执行的是同步操作,DFF1的Q端为1;但DFF2不可能出现亚稳态,因为如果执行的是异步操作,DFF2的Q端为0,而如果执行的是同步操作,DFF2的Q端为0,即rst_sync_n总是为0,因此DFF2不可能出现亚稳态,在clk的下一个有效上升沿,DFF1的Q端为1,而DFF2的D端为0或1,如果DFF2的D端为0,rst_sync_n为0(在下一个有效上升沿,rst_sync_n为0),如果DFF2的D端为1,rst_sync_n为1,即rst_sync_n延迟1个或或2个clk周期后,同步到clk的有效上升沿释放。
阻塞赋值和非阻塞赋值
阻塞赋值用于组合逻辑,而非阻塞赋值用于时序逻辑,阻塞赋值和非阻塞的代码如下图所示
第一张图是阻塞赋值,第二张图是非阻塞赋值,在阻塞赋值中,b被更新为a后,才能执行c=b,因此是阻塞赋值。而在非阻塞赋值中,假设b的当前值为0,a的当前值为1,在第一个有效时钟沿,b被赋1,c被赋0(而不是1),不是等b更新为1,才能执行c<=b,相反b<=a和c<=b是同时执行的,因此是非阻塞赋值,在第二个有效时钟沿,c被赋为1。