跨时钟域传输总结

时间:2024-05-23 09:05:44

前言

虽然以前也看过一些关于跨时钟域传输(下称CDC)的文章,但那时由于认识所限,对于不同类型的CDC更像是照本宣科,没有进行深入的思考。现在看的资料多了,再回过头来看这些关于CDC的文章,又有新的收获。因此决定写一篇博客总结一下我目前对于CDC的理解。

强烈推荐大家先看IC_Learner的这篇关于CDC的文章:https://www.cnblogs.com/IClearner/p/6485389.html

讲得非常全面,我的总结会以这篇文章为基础,做一些引申和补充。

同步时序电路中的亚稳态

首先明确一点:组合逻辑中的竞争与冒险会导致电路中出现毛刺,因此我们采用时序逻辑来过滤掉这些毛刺,避免其直接对电路的输出产生影响。只要我们使用了满足时序约束的时序电路,因组合逻辑的竞争与冒险而产生的毛刺是一定不会被传输到下一级的。

然而时序逻辑的使用也带来了这样一个约束:时钟采样时必须满足建立时间和保持时间的要求,否则就会使得采样到的数据进入亚稳态。

参见下图,传统的同步时钟域中的各个时钟要么是同频的,要么是不同频但有固定的相位关系的。对于这种情况,只要保证数据满足建立时间和保持时间的要求,那么就一定不会产生亚稳态。

然而对于异步时钟域,写时钟和读时钟之间没有固定的相位关系,那么如果不采取任何措施,随着工作时间的推移,电路中就一定会出现亚稳态。在读时钟的每个时钟有效沿,产生亚稳态的概率为:(建立时间 + 保持时间) / 时钟周期。亚稳态有可能会被下一级的寄存器捕捉到,再传递向更下一级的各模块,造成亚稳态的传播,这是我们所不能容忍的。因此,我们必须采取某些措施来防止亚稳态传播向下一级。
跨时钟域传输总结

单 bit 传输

对于单 bit 数据的传输,我们的关注点主要在这两个方面:

  1. 如何保证数据被检测到?
  2. 如何防止出现亚稳态?

单 bit 的数据很多都是控制信号,保证其能够传输到下一级,正确地传输到下一级是十分重要的。

保证数据的传输

保证数据被检测到,也即是保证数据能顺利地从一个时钟域被传输到另一个时钟域。对于慢时钟域对快时钟域的传输,快时钟域的时钟是一定可以检测到慢时钟域想要传输的数据的,如下图所示,因此可以不用担心这些问题。
跨时钟域传输总结
对于快时钟域向慢时钟域的传输,是无法保证慢时钟域一定可以采样到快时钟域想要传输的数据的。比如说下图的这种情况,clk1的第2个上升沿把 Signal 拉高,与 clk1 处于同一个时钟域的慢时钟 clk2就刚好错过了这个 Signal ,造成了数据的漏采。
跨时钟域传输总结
为了防止出现漏采,在快时钟域向慢时钟域进行传输时,我们通常采用的方式是对想要传输的数据进行展宽,具体步骤为:

  1. 快时钟域检测到将要传输的 data_a,产生一个展宽信号 pulse_a。
  2. 慢时钟域检测到 pulse_a,将 pulse_a 在慢时钟域打两拍后生成的 pulse_b_rr 输出。
  3. 快时钟域检测到 pulse_b_rr 为高电平,将其在快时钟域打两拍,生成 pulse_a_rr。
  4. 快时钟域检测到 pulse_a_rr 被拉高,知道传输已经结束了,于是拉低展宽信号 pulse_a,传输结束。

具体的代码请见这篇博客

IC_Learner 的博客中的展宽方式与上面讲的稍有不同,但他们的思想是相同的。

避免亚稳态的传播

上面说到了,异步时钟域的数据传输不可避免地会出现亚稳态。亚稳态在读时钟的有效沿产生,持续时间不定。但如果我们给数据一个周期的时间缓冲,等到下个读时钟的有效沿再对数据进行采样,那么这次采样到亚稳态的概率就会大大减小。如果给数据两个周期的时间缓冲,那么采样到亚稳态的概率就会更小。这就是我们避免亚稳态传播的第一个方法:电平同步器。

clk2 接收到 clk1 发来的数据后并不直接输出,而是使用两级寄存器打两拍后再输出,大大降低了输出亚稳态的概率。
跨时钟域传输总结另一种避免传播亚稳态的方法是采用如下图的脉冲同步器,图中 output = input_r & !input_rr,效果是产生一个时钟周期宽度的脉冲信号。
跨时钟域传输总结

多 bit 传输

如果仔细观察,就会发现上面的例子往往仅适用于单 bit 传输。对于多 bit 传输,有以下几种方法:

FIFO

使用异步 FIFO 可以很好地解决多 bit 数据跨时钟域传输的问题。由于 FIFO 本身有存储功能,因此如果写时钟域是快时钟域,读时钟域没采到的那些数据也会存在 FIFO 里,就不用担心漏采的问题了。

需要注意的一点就是异步 FIFO 中判断空满时地址数据也要进行读写时钟域之间的相互传输,这时候要先将地址转换为格雷码,防止空满判断出现错误。

握手

握手法的基本流程可以概括为:

  1. 写时钟域发送一个开始信号,读时钟域检测到后准备进行数据传输。
  2. 进行写时钟域向读时钟域的数据传输,这个过程可以是串行的,也可以是并行的。
  3. 数据传输完毕后,读时钟域向写时钟域发送一个 ack 信号,表示自己已经成功收到了数据。
  4. 写时钟域停止本次数据传输,等待下一次的数据传输。

准备进行数据传输时,读时钟域检测到写时钟域的开始信号,可能也需要向写时钟域返回一个开始的反馈信号。这是一个两个时钟域之间进行交互的方法,因此被称为握手法。

寄存器

也有人认为异步时钟域中多 bit 数据的传输也可以采用打两拍的方式,前提是数据要提前转换为格雷码。