1. 背景
现在的芯片(比如SOC,片上系统)集成度和复杂度越来越高,通常一颗芯片上会有许多不同的信号工作在不同的时钟频率下。比如SOC芯片中的CPU通常会工作在一个频率上,总线信号(比如DRAM BUS)会工作在另一个时钟频率下,而普通的信号又会工作在另外的时钟频率下。这3个不同时钟频率下工作的信号往往需要相互沟通和传递信号。
不同时钟域下的信号传递就涉及到跨时钟域信号处理,因为相互之间的频率、相位不一样,如果不做处理或者处理不当,可能导致:
- 数据丢失,无法采到预期中的信号;
- 亚稳态的产生。
跨时钟域的信号可以分为单bit信号和多bit信号,处理方法有所不同。
2. 单bit
2.1 快时钟域到慢时钟域
2.1.1 问题
下图显示了当在一个时钟域(aclk)中生成的信号adat被送到了另一个时钟域(bclk)中采样,由于采样时间太靠近第二个时钟的上升沿时,发生的同步失败。同步失败是由于输出bdat1变为亚稳态,而在bdat1再次被采样时没有收敛到合法的稳定状态。
这里注意一下,如果是电平信号进行CDC, 那么不用考虑时钟快慢,直接用同步器就可以了,因为总能被采样到。所以,下面考虑的主要是信号位宽有限的CDC。
快时钟到慢时钟的(单bit)信号处理,主要问题就是信号在快时钟域中,可能会多次改变,这样慢时钟可能来不及采样,导致丢失数据。这个问题被称为信号宽度问题,在CDC检查工具中,如果快时钟的信号宽度不足,会报出CDC违例。
快时钟到慢时钟的(单bit)信号处理分为两种:
- 采样丢失是被允许的。单bit信号一般不会是这种情况,如果是这种情况,直接用同步器同步就可以了。
- 采样丢失不被允许。这样就要采样其他手段来保证数据不丢失。主要原理是保证快时钟域的信号宽度满足一定的条件,使得慢时钟域有足够时间采样到。
2.1.2 信号宽度的“三时钟沿”要求
那么信号在快时钟域到底需要多宽,才能保证在慢时钟域安全的被采样到呢?比较安全的宽度是,快时钟域的信号宽度必须是慢时钟域时钟周期的1.5倍以上。也就是要持续3个时钟沿以上(上升沿和下降沿都算)。这个被称为:“三时钟沿”要求。
下面是一个CDC信号只持续一个周期的例子。发送时钟域的频率高于接收时钟域,而CDC脉冲在发送时钟域中只有一个周期宽,这样CDC信号可以在慢时钟上升沿之间变动,不会被捕获到慢时钟域,如下图所示
如果CDC信号宽度超过慢时钟周期,但是不足1.5个周期,也会发生问题。如下图所示
如上图所示,信号宽度超过了一个慢时钟周期,但是可能会setup和hold time违例,导致信号采样失败。
2.1.3 信号宽度问题的解决方法
一种最简单的方法是,通过保证信号宽度满足超过慢时钟的时钟周期1.5倍,来解决这个问题。这种方法是最直接,也是跨时钟最快的方法。可以通过system Verilog加“断言”的方式来检测是否满足条件。
但是实际中很少用这种方式,因为设计可能会变,设计人员在改变设计时,可能会忘记这个限制,以为是一个通用的解决方法。所以,常用的还是通过“握手”的方式来保证数据被采样到。
通常的做法是:是发送一个使能控制信号,将它同步到新的时钟域,然后通过另一个同步器将同步信号作为确认信号传回发送时钟域。如下图所示:
优点:
同步反馈信号是一种非常安全的技术,可以识别第一个控制信号并将其采样到新的时钟域。
缺点:
在允许控制信号改变之前,在两个方向上同步控制信号可能会有相当大的延迟。也就是说,在应答信号到来之前,是不允许源信号改变的。
在实际的芯片设计中,脉冲(宽度有限)信号的同步都是采用这种握手机制来处理。
2.2 慢时钟到快时钟域
慢时钟域到快时钟域的CDC, 直接使用信号同步器就可以了。很多人可能会问,为什么是两级DFF呢?一级或者三级DFF行不行呢?这里有一个平均失效间隔时间MTBF(Mean Time Between Failure)的考虑。MTBF时间越长,出现亚稳态的概率就越小,但是也不能完全避免亚稳态。注意采样时钟频率越高,MTBF可能会迅速减小。
有文献给出的数据:对于一个采样频率为200Mhz的系统,如果不做同步MTBF是2.5us,一级DFF同步的MTBF大概是23年,两级DFF同步的大约MTBF大概是640年,MTBF越长出错的概率越小。所以一级看上去不太稳,二级差不多够用了,至于三级可能会影响到系统的性能,而且增加面积,所以看上去没什么必要。
但是,这里有一点要指出来,那就是怎么才算慢时钟域到快时钟域的CDC呢?这里和平常理解的有点不一样。
目标时钟频率必须是源时钟频率1.5倍或者以上,才能算慢时钟到快时钟的CDC.
这也很好理解,只有满足快1.5倍以上,才能满足“三时钟沿”的要求,才能保证快时钟域保证能够采样到慢时钟域的脉冲。如果目标时钟域只快一点,比如1~1.5倍之内,为了保险起见,请按照1.1中快时钟到慢时钟域的处理方法来处理。
另外,有的设计中为了保险和以后修改的方便;或者还不清楚时钟之间的关系;都会按照1.1中的方式来进行单bit的CDC处理。
3. 多bit
在两个时钟域之间传递多个信号,简单的同步器已经不能满足要求。工程师经常容易犯的错误是,直接用简单同步器来同步多个信号。
这里列出两个信号分别通过同步器同步后,在目标时钟域聚合后使用的场景。问题是两个同步器,跨时钟的延时可能不一样,比如信号从2’b00->2’b11, 上面的同步器花了2个周期同步到了目标时钟域;下面的同步器花了3个周期才同步到目标时钟域。那么在第二和第三周期之间,就出现了2’b10的值了,即出现了错误的采样信号,这样功能有可能就不正确了。
另外,不同同步器的芯片上的走线也可能不同,导致延时不一样。即使我们完美的控制后端不同bit同步信号的走线长度一样,同一个die上面不同芯片之间或者不同制程之间的偏差,都可能引入差异,导致多bit信号的延时不同。而且这样对后端走线难度增大。
考虑到以上两点,多个信号的CDC一般不用简单同步器的方法。在CDC检查时,会有专门的规则来检查是否采样了多bit信号用同步器同步聚合使用的情况。
为了避免多位CDC倾斜采样的情况,多个信号CDC策略可以分为三种:
- 多周期路径法。使用同步负载信号安全地传递多个CDC位。
- 使用格雷码传递多个CDC位。
- 使用异步FIFO来传递多位信号。
3.1 多周期路径(Multi-Cycle Path, MCP)
下图中显示了在时钟域之间传递的两个编码控制信号。如果这两个编码信号在采样时略有偏差,则在接收时钟域中的一个时钟周期内可能会产生错误的解码输出。
多位数据问题可以用“多周期路径法(MCP)”来解决。MCP方法是指直接不同步将数据发送到目标时钟域,但是同时送一个同步过的控制信号到目标时钟域。数据和控制信号同时发送,允许数据在目标寄存器的输入端进行设置,同时控制信号在到达目标寄存器的负载输入端之前做同步。
MCP方法的优点:
- 不需要在发送时钟域计算适当的脉冲宽度
- 发送时钟域只需要将enable toggle到接收时钟域,表示数据已经被传递完成,已经准备好被加载。使能信号不需要返回到初始逻辑电平。
MCP方法的实质就是,不同步多位的数据,只同步一位的控制信号,通过握手保证控制信号能够正确传输,然后在目标时钟域通过控制信号来采样数据。MCP需要用到“同步脉冲器”:
同步脉冲器的符号表示如下:
多周期路径法有两种方法来传递多位信号:
3.1.1 带反馈的MCP
3.1.2 带应答反馈的MCP
多周期路径法的思想十分有用,但是实际中用来传递多位信号比较少见,因为逻辑过于复杂。但是MCP方法用来传递单bit的信号却十分有用。这里就不展开讲了,有兴趣的可以参考最后的参考文档中的描述。
3.2 格雷码
对于计数器的CDC, 大部分是不必要的。如果一定需要,那么可以使用格雷码。格雷码每次只允许更改一个位,从而消除了跨时钟域同步更改多个CDC位所带来的问题。格雷码和二进制码之间的转换是一种很成熟的技术,很容易就能找到现成的代码,这里就不在详细描述。
需要注意的是:格雷码必须是计数到2^n才是每次改变一个bit。
如果计数器是从0~5计数,那么从5->0的计数,不止一个bit改变,就失去了只改变一个bit的初衷。格雷码最常见的应用是在异步FIFO中,通常异步FIFO的深度都是2^N,原因就是上面说的。所以,就算浪费面积,也需要把FIFO深度设置为2^N。
3.3 通过AFIFO进行多位信号CDC
多位信号CDC的工程上的一般做法都是采用异步FIFO,这里有一个特殊的应用,那就是深度为2的AFIFO,来进行多bit数据的CDC. 如下图所示:
2个寄存器搭建的AFIFO,地址只需要一位。相比MCP方法,逻辑简单,可以复用AFIFO代码,而且延时也比MCP方法小。所以多bit仅仅跨时钟域,不需要进行数据吞吐率匹配(FIFO的重要功能之一)的情况,推荐用深度为2的AFIFO来实现,而不是MCP方法。