写在前面

         本文参考自《Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog》—Clifford E. Cummings。

        主要讲述了如何进行跨时钟域设计。正文中,黑字为内容,浅蓝色字体是我的啰嗦。如果需要英文原文文章,可以评论留邮箱给我。


        系列文章:
        跨时钟域处理解析(一)(Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog)


        跨时钟域处理解析(二)(Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog)


        跨时钟域处理解析(三)(Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog)




5.0 在时钟域之间传递多bit信号

        在时钟域之间传递多bit信号时,简单的同步器并不能保证数据的安全传输。  

        工程师在进行多时钟设计时经常犯的一个错误是将同一事务中所需的多个 CDC 位从一个时钟域传递到另一个时钟域,而忽视了 CDC 位同步采样的重要性。  

        问题是同步到一个时钟的多bit信号会经历小的数据变化偏斜(skews),偶尔会在第二个时钟域的不同时钟上升沿上采样。即使我们可以完美地控制和匹配多个信号的走线长度,上升和下降时间的差异以及芯片上的工艺变化可能会引入足够的偏斜,从而导致对原本精心匹配的走线的采样失败。  

        所以必须采用多位 CDC 策略来避免多bit的偏斜采样。


        多bit信号的CDC问题主要来自信号之间的skew(简单地理解为到达目的时钟域的时间无法控制一致),可能的后果就是某些bit可能还没到达,但是某些bit已经被采样了。


5.1 多bitCDC策略

为了避免多bit CDC 偏斜采样情况,我将多位bit CDC 策略分为三大类:

                (1) 多bit 信号合并。在可能的情况下,将多个 CDC 位合并为 1 位 CDC 信号。

                (2) 多循环路径公式。使用同步负载信号安全地传递多个 CDC 位。

                (3) 使用格雷码传递多个 CDC 位。  


        本节的其余部分详细介绍了这些策略。  


5.2 多bit 信号合并 

        在可能的情况下,将多个 CDC 信号合并为一个 1 位 CDC 信号。问问自己这个问题,我真的需要多个位来控制跨 CDC 边界的逻辑吗?  

        如以下示例所示,简单地在所有 CDC 位上使用同步器并不总是足够好。  

        如果控制信号的顺序或对齐很重要,则必须注意将信号正确传递到新的时钟域。本节中显示的所有示例都过于简单,但它们与实际设计中经常出现的情况非常相似。


5.3 问题 — 同时需要两个控制信号。

        在图 12 所示的简单示例中,接收时钟域中的寄存器需要加载信号和使能信号,以便将数据值加载到寄存器中。如果负载和使能信号在同一发送时钟边沿上驱动,则控制信号之间的小偏差可能会导致两个信号在接收时钟域内同步到不同的时钟周期。在这些条件下,数据不会被加载到寄存器中。


        在这个示例中,需要2bit信号来做逻辑控制。跨时钟域传输这2bit信号的过程中,由于信号到达目的时钟域的时间不一致(存在skew),导致2个信号无法在同一个时钟周期进行采样造成了数据漏采,从而使得预期功能无法实现。  


5.3.1 解决方案 - 整合

        5.3节问题的解决方法很简单,整合控制信号。如图 13 所示,仅从一个加载启用信号驱动接收时钟域中的加载和启用寄存器输入信号。合并将消除两个及时转移到达的控制信号的可能性。  


        把控制信号合并不失为一个好办法。


5.4 问题 - 两个相位偏差的控制信号。

        图 14 中的图表显示了两个使能信号 aen1 和 aen2,它们依次从发送时钟域驱动到接收时钟域,以控制流水线数据寄存器的使能输入。问题是在第一时钟域,aen1控制信号可能在aen2控制信号产生前稍稍终止,在aen1和aen2控制信号脉冲之间的微小间隙中可能出现接收时钟的上升沿,导致在接收时钟域的使能控制信号链中形成一个周期间隙。这将导致第二个寄存器丢失 a2 数据值。


5.4.1 解决方案 - -合并和额外的触发器

        这个问题的解决方案,如图 15 所示,是只向接收时钟域发送一个控制信号,并在接收时钟域内生成第二个相移流水线使能信号。  


5.5 问题 - 多bit CDC 信号

        图 16 中的图表显示了在时钟域之间传递的两个编码控制信号。如果两个编码信号在采样时略微偏斜,则可能会在接收时钟域中的一个时钟周期内生成错误的解码输出。 


5.5.1 传递多个CDC信号的解决方案

        多循环路径 (MCP) 公式和 FIFO 技术可用于解决与传递多个 CDC 信号相关的问题。 MCP 公式的描述和定义在第 5.6 节中给出  

        至少有两种多循环路径 (MCP) 公式可用于解决此问题:

                (1) 闭环 - 带有反馈的 MCP 公式。

                (2) 闭环 - 带有确认反馈的 MCP 公式。


        MCP 制定实施技术将在下一节开始描述。  

        还有至少两种 FIFO 策略可以作为这个问题的闭环解决方案:

                (1) 异步 FIFO 实现。

                (2) 2-deep FIFO 实现。

        FIFO 实现技术在第 5.8 节开始描述。  

5.6 多循环路径 (MCP) 公式

        使用 MCP 公式(Multi-Cycle Path (MCP))是安全传递多个 CDC 信号的常用技术。

        MCP 公式是指将非同步数据发送到与同步控制信号配对的接收时钟域。数据和控制信号同时发送,允许数据在目标寄存器的输入上建立,同时控制信号在到达目标寄存器的加载输入之前同步两个接收时钟周期。          

        好处:

                (1) 发送时钟域不需要计算在时钟域之间发送的适当脉冲宽度。

                (2) 发送时钟域只需要将一个使能切换到接收时钟域以指示数据已通过并准备好加载。使能信号不需要返回其初始逻辑电平。  


        该策略不同步地传递多个CDC信号,同时将同步的使能信号传递到接收时钟域。接收时钟域不允许对多位CDC信号进行采样,直到同步使能通过同步到达接收寄存器。

        这种策略被称为多周期路径公式[8],因为未同步的数据字直接传递到接收时钟域并保持多个接收时钟周期,从而使使能信号同步并识别到接收时钟域,然后才允许更改未同步的数据字。  

        因为未同步的数据在被采样之前会被传递并保持稳定多个时钟周期,所以采样值不会有亚稳态的危险。  


5.6.1 使用同步使能脉冲的 MCP 公式

        或许在时钟域之间传递同步使能信号的最常见方法是使用一个切换使能信号,该信号传递到同步脉冲发生器以指示可以在下一个接收时钟边沿捕获未同步的多周期数据字,如图所示在图 18 中。  


        这种同步使能脉冲生成的一个关键特性是输入信号的极性无关紧要。在图 18 中,d 输入在周期 1 中切换为高电平,到周期 4 时,高电平信号已通过三个同步触发器传播。在周期 3 中,q2 和 q3 触发器的输出具有不同的极性,导致在同一个周期内在异或门的输出上形成同步使能脉冲。类似地,d 输入在第 7 周期切换为低电平,到第 10 周期,高信号已通过三个同步触发器传播。再次在周期 9 中,q2 和 q3 触发器的输出具有不同的极性,导致在异或门的输出上形成同步使能脉冲。 


        这里的脉冲信号会作为后面的握手过程的反馈信号。  


        由于第 5.0 节中描述的所有 MCP 公式都使用同步使能脉冲生成电路,因此创建和使用较小的等效符号来表示同步使能脉冲生成电路被认为是有用的。等效符号如图 19 所示。 

        除了生成任何 d 输入极性的脉冲之外,同步使能脉冲生成电路还有一个 q 输出,它跟随延迟三个时钟周期的 d 输入。 q 输出经常用作反馈信号,并作为确认信号通过发送时钟域中的另一个同步使能脉冲生成电路传递。  

        图 20 显示了典型的发送-接收触发脉冲生成设计。

        使用这种技术时,要求接收时钟域具有适当的逻辑来在检测到脉冲时捕获数据,因为对于每个多周期数据字,脉冲仅在一个接收时钟周期内有效。


5.6.2 闭环 - 带反馈的 MCP 公式

        使用 MCP 公式时的一项重要技术是将启用信号作为确认信号传回发送时钟域,如图 21 所示。 


        对于图 21 中的示例,确认反馈信号 (b_ack) 生成确认脉冲 (aack),该脉冲用作小型 READY-BUSY、1 状态 FSM 模块的输入,该模块生成就绪信号 (aready) 以指示现在可以安全地再次更改数据输入 (adatain) 值。一旦aready 信号变高,发送方就可以自由发送新数据(adatain)和伴随的asend 控制信号。  。

        这是一条自动反馈路径,假定接收时钟域将始终准备好接收通过 MCP 公式同步的下一个数据字。 


5.6.3 闭环 - 带有确认反馈的 MCP 公式

        5.6.2 节中描述的技术的一个完全响应变化使用 MCP 公式是只有在接收时钟域确认接收到带有 bload 脉冲的数据之后,才将使能信号作为确认信号传递回发送时钟域,如图所示在图 22 中。 

        对于图 22 中的示例,接收时钟域有一个小的 WAIT-READY、1-state FSM,当数据寄存器输入端的数据有效时,它会向接收逻辑发送一个有效信号 (bvalid)。在接收逻辑确认应通过置位 bload 信号加载数据之前,数据并未真正加载。在数据加载之前没有反馈到发送时钟域,然后 b_ack 信号被发送回与具有自动反馈的 MCP 公式相同。  这是一条反馈路径,需要在捕获数据和发送反馈之前对接收时钟域部分采取行动。 

        这个MCP好像就是握手反馈法,可以参考:跨时钟域(CDC)设计方法之单bit信号篇(一)


5.7 同步计数器

        如前所述,在时钟域之间传递多个信号时,要问的一个重要问题是,我是否需要对从一个时钟域传递到另一个时钟域的信号的每个值进行采样?对于这种情况,答案经常是,不!  

        参考文献 [1] 详细介绍了 FIFO 设计技术,其中格雷码计数器在时钟域之间采样,而中间格雷码计数值经常被遗漏。对于这种 FIFO 设计,更多的考虑是确保计数器不会超出其边界,这可能导致错过满和空标志检测。即使时钟域之间的采样格雷码计数值经常被遗漏,该设计也是稳健的,并且所有重要的格雷码计数值都被适当地采样。有关详细信息,请参阅 [1]。  

        由于可能允许有效设计跳过某些计数值样本,是否可以使用任何计数器来跨 CDC 边界传递计数值?答案是不。 


5.7.1 二进制计数器

        二进制计数器的一个特点是,所有连续二进制递增操作中有一半要求必须更改两个或更多计数器位。尝试跨 CDC 边界同步二进制计数器与尝试将多个 CDC 信号同步到新的时钟域中是一样的。如果一个简单的 4 位二进制计数器从地址 7(二进制 0111)更改为地址 8(二进制 1000),则所有四个计数器位将同时更改。如果同步时钟边沿出现在此转换的中间,则任何 4 位二进制模式都可能被采样并同步到新的时钟域中,如图 23 所示。

        在 FIFO 设计中,新的同步二进制值可能会触发错误的满或空标志,甚至更糟的是,它可能不会触发真正的满或空标志,从而导致数据因 FIFO 而丢失 。当 FIFO 真的为空时,由于尝试读取数据而导致溢出或导致从 FIFO 读取无效数据。 

        如果使用经典的2进制码,那么从7到8的跳变过程中,4位都需要传递,那么可能4位信号间的skew会导致亚稳态或者数据传输错误,这一问题可以通过格雷码来解决。


5.7.2 格雷码         

        格雷码以 Frank Gray[4] 命名,可用于多时钟设计的最安全计数器是格雷码计数器。格雷码只允许为每个时钟转换更改一位,从而消除了与尝试跨时钟域同步多个更改的 CDC 位相关的问题。  

        标准格雷码具有非常好的转换特性,可以将格雷码转换为二进制并再次转换回来。使用这些转换,设计高效的格雷码计数器很简单。

        关于格雷码的概念及转换,可以参考:Verilog实现的格雷码与二进制码的互相转换


5.7.3 格雷码到二进制转换

        将格雷码值转换为等效的二进制码值,以n位格雷码值为例,二进制位0等于格雷码位0的异或与所有其他格雷码的异或位从 1 到 n。二进制位 1 等于格雷码位 1 与从 2 到 n 等所有其他格雷码位的异或运算。最高有效的二进制位刚好等于最高有效的格雷码位。  

        图 24 显示了示例 4 位灰度到二进制转换的方程式。 

        编写格雷码到二进制转换器的最简单方法是编写一个 for 循环并对具有可变索引范围的灰度代码向量进行异或约简,其中每次通过循环,索引范围的 LSB 都会增加,直到我们只剩下 bin[MSB] = 的简单赋值 ^gray[MSB:MSB](只是格雷码向量的 1 位 MSB),如示例 1 所示。

        不幸的是,Verilog 和 SystemVerilog 不允许使用可变索引范围进行部件选择,因此示例 1 中的代码虽然在概念上是正确的,但不会编译。 

        为了解决这个问题,请记住异或门实际上是一个可编程反相器。如果一个输入被拉高,另一个输入被反相并传递到输出。类似地,如果一个输入被拉低,另一个输入被传递到输出而不反转(从输入到输出没有变化)。  

        利用任何涉及 0 输入的添加异或运算不会改变运算结果的事实,格雷码到二进制转换的方法是异或有效的格雷码位带有填充的 0,如图 25 所示。  

        此简化算法的相应参数化 SystemVerilog 模型如示例 2 所示。此示例在语法上是正确的,可以编译并且可以正常工作。

         输入绑定到 0 的所有额外的异或运算会发生什么?综合工具认识到,可以优化一个输入上具有常数 0 的异或门,以推断设计的非常有效的实现。  


5.7.4 二进制到格雷码的转换

        将二进制值转换为等效的格雷码值,以n位二进制值为例,格雷码位0等于二进制位0和1的异或。格雷码位1等于到二进制位 1 和 2 的异或等。最高有效的格雷码位刚好等于最高有效的二进制位。

        示例 4 位二进制到格雷码转换的方程式如图 26 所示。  

        编写二进制到格雷码转换器的最简单方法是编写一个简单的连续赋值,该赋值在二进制向量和相同二进制向量的右移版本之间执行按位异或运算,如图所示 示例 3。这个示例在语法上是正确的,可以编译并且可以工作。 


5.7.5 格雷码计数器样式 #1

        我们可以使用第 5.7.3 节和 5.7.4.对于任何格雷码计数器,重要的是要记住必须寄存格雷码输出以消除设计中的任何组合毛刺。  

        格雷码计数器样式 #1 的 SystemVerilog 代码包含一个格雷码到二进制转换器、一个二进制到格雷码转换器并在转换之间递增二进制值,如图 27 所示。 

 示例 4 显示了格雷码计数器样式 #1 的相应参数化 SystemVerilog 模型。 


5.7.6 格雷码计数器样式 #2

        我们可以仅使用第 5.7.4 节中显示的二进制到格雷码转换来构建第二种样式的格雷码计数器。这个格雷码计数器实际上既是一个二进制计数寄存器又是一个格雷码计数寄存器。 

        用于格雷码计数器样式 #2 的 SystemVerilog 代码包含一个二进制计数器以消除格雷码到二进制转换的需要,并使用下一个二进制计数值进行二进制到格雷码转换,然后将其寄存到格雷码中。这种风格使用两倍数量的触发器,但生成下一个格雷码值的组合逻辑路径更短,这使得这种实现比格雷码计数器样式 #1 更快。格雷码计数器样式 #2 的框图如图 28 所示,  

        示例 5 显示了格雷码计数器样式 #2 的相应参数化 SystemVerilog 模型。


5.8 其他多bit CDC 技术

        除了前面部分描述的 MCP 公式化技术之外,我还发现许多工程师使用标准 FIFO 在时钟域之间传递数据和控制信号。  

        至少有两种有趣的 FIFO 实现策略可用于解决多位 CDC 信号完整性问题:

                (1) 异步 FIFO 实现。

                (2) 2-deep FIFO 实现。  


5.8.1 使用异步 FIFOS 的多位 CDC 信号传递

        传递多个位,无论是数据位还是控制位,都可以通过异步 FIFO 来完成。异步 FIFO 是共享存储器或寄存器缓冲区,其中数据从写时钟域插入,数据从读时钟域移除。由于发送方和接收方都在各自的时钟域内运行,因此使用双端口缓冲器(例如 FIFO)是在时钟域之间传递多位值的安全方式。  

        只要FIFO未满,标准异步FIFO设备就允许插入多个数据或控制字,只要FIFO不为空,接收器然后在方便时提取多个数据或控制字。  

        FIFO 设计中的大部分比较难的工作是通过格雷码计数器的同步完成的,[1] 中描述了一种经过验证的 FIFO 设计技术。 


5.8.2 使用 1-deep/2-register FIFO 同步器的多位 CDC 信号传递

        跨 CDC 边界传递多个控制位和数据位的另一个有趣变化涉及使用1 深度双寄存器FIFO,如图 29 所示。

        这种 1 深度双寄存器 FIFO 具有许多有趣的特性。由于 FIFO 仅使用两个寄存器或一个 2 深双端口 RAM 构建,因此用于检测满和空的格雷码计数器是简单的触发触发器,实际上只不过是 1 位二进制计数器(请记住,标准格雷码的 MSB 与二进制码的 MSB 相同)。  

        复位时,两个指针都被清除,FIFO 为空,因此 FIFO 未满。我们使用反转的未满条件来指示 FIFO 已准备好接收数据或控制字(wrdy 为高)。在将数据或控制字放入 FIFO 后(使用 wput),wptr 切换并且 FIFO 变满,或者换句话说,wrdy 信号变低,这也禁用了切换 wptr 的能力,因此也禁用了能够将另一个字放入 2 寄存器 FIFO,直到第一个字被接收时钟域逻辑从 FIFO 中删除。  

        这个设计特别有趣的是 wptr 现在指向 2 寄存器 FIFO 中的第二个位置,所以当 FIFO 再次准备好时(当 wrdy 为高时),wptr 已经指向下一个位置写。  

        在 FIFO 的接收端复制了相同的概念。当数据或控制字写入 FIFO 时,FIFO 变为非空。我们使用反转的非空条件来指示 FIFO 具有准备接收的数据或控制字(rrdy 为高)。  

        通过使用两个寄存器来存储多位 CDC 值,我们能够从发送 MCP 公式中删除一个时钟周期,并从确认反馈路径中删除另一个周期。