01signal.com

Metastability 和 clock domain crossing的基础知识

本页是关于 clock domains的系列文章中的第三页。

范围

上一页所述,如果一个 path 跨越 related clocks的 clock domains , logic不需要特殊处理。然而,需要确保 FPGA 工具在此 path上强制执行 timing constraints ,以保证 synchronous element 在目的地(setup 和 hold)的时序要求。所以这种情况的处理就好像根本没有 clock domain crossing ,在 path 是定时的意义上。

但是,如果 clocks 是 unrelated clocks,则需要 resynchronization logic : 必须有专门用于解决此问题的 Verilog (或其他语言)代码。本页介绍了如何执行此操作的基础知识。

如果可以,请使用 FIFO

我认为从如何完全避免这种头痛开始,有利于那些有可能逃脱的人,这将是公平的。这是:

由 FPGA 工具生成的Dual-clock FIFOs绝对是最安全的穿越 clock domains的方式。这是最常见的解决方案,而且这种方法被广泛使用的事实本身就是信任它的理由。

特别是,如果您是 FPGAs的新手,那么有充分的理由更喜欢 FIFO,即使您需要为此目的稍微更改自己的 design 。使用 FIFO 可能会消耗比为您的目的量身定制的 logic 稍多的资源。但请注意,如果 FIFO的深度较浅,通常不需要 block RAM 。因此,差异不一定值得冒险搞砸。

FIFO 通常是跨越 clock domains的自然解决方案,特别是当 data stream 从一个功能单元到另一个功能单元时。但即使不是那么自然,也通常可以重新组织 logic 以使其适合。例如,如果一个 clock domain 中的 logic 需要通知另一个 clock domain 中的 logic 某些事件,这可以通过对消息字进行编码并将其写入浅层 FIFO来完成。这种解决方案不仅不易出现错误,而且还可能导致 design更有条理和可读性。

如何连接和使用 FIFOs 在本系列页面中进行了广泛讨论。

Resynchronization logic 用于单个 bit

如果 FIFO 不能解决您的问题,是时候卷起袖子,实施 resynchronization logic 以获得安全的 clock domain crossing。本页的其余部分仅限于使用具有单个 bit宽度的信号穿过 clock domains 。这是任何 resynchronization logic的基石,对于这个看似简单的案例有很多话要说。

本系列的下一页讨论如何使用更宽的信号通过 clock domains ,基于下面介绍的 metastability guard 技术。

Metastability

在转向解决方案之前,重要的是要了解当违反 flip-flop 的时序要求时会发生什么。换言之,当其 data input 的信号在 clock edge之前开始 tsu 并在其之后结束 thold 的时间段内不稳定时。相关的 clock edge 当然是 flip-flop的 clock input。正是这个 clock edge 导致它对 data input进行采样。

显然,如果在这个时间段内数据发生变化,那么这个 clock edge 之后的 flip-flop 的 output 是不可预知的。但它甚至比这样更糟糕: flip-flop 在 flip-flop的 output上展示合法的 '0' 或 '1' 可能需要比平时更长的时间。即使 flip-flop 旨在使 output 在这两个可能值之一(即 '0' 或 '1')上保持稳定,但违反时序要求可能会使其在短时间内处于不稳定状态。或者,用官方术语来说, flip-flop 就是 metastable

我不会跳过这个陈词滥调的形象,即球站在山顶,这通常用于描绘 flip-flop 可以达到的不稳定情况:

Ball on tip of hill, illustrating metastability

Metastability 是一个更大的问题,而不仅仅是 flip-flop 最终登陆的不确定性。如果 flip-flop的 output 连接到多个目的地,则尤其如此: 由于在 '0' 或 '1'上着陆需要较长时间,因此对于到达目的地的 flip-flops 而言,信号可能变得稳定太晚。因此,目的地的一些 flip-flops 可能会像 '0' 一样接收到摆动信号,而其他 flip-flops 可能会像 '1'一样接收到它。这种不匹配会使 logic 进入非法状态,从这种情况下, logic 可以表现出 black magic 的风格。因此,对于 metastability 有风险的 flip-flop 永远不应连接到多个 logic element。

为了解决 metastability,很高兴知道 flip-flop 需要多长时间才能进入其稳定状态之一。不幸的是,对此没有明确的答案。这就像问球会在山顶停留多长时间: 这取决于很多因素,随机振动最终会使它以这种或另一种方式倒下。与 flip-flop的 metastability相同: 由于电子电路中的随机噪声或其他任何原因,它将脱离这种状态。

所以理论上,一台 flip-flop 可以永远保持 metastability 的状态,但实际上它会在一段时间后进入它的一种稳定状态。它停留在这种状态的时间是 random variable。为了估计这款 random variable的行为,已经进行了大量的实验和模拟。然而,这些尝试都不是真正相关的,因为行为取决于 silicon的制造技术、温度、 crosstalk 的噪音水平等等。

所以再一次,即使在 metastability的状态下有一个 flip-flop 永远不会超过的指定最大时间会很方便,但没有这样的限制。甚至不可能得到一个大概的数字,因为更新的制造工艺使得 flip-flops 倾向于更快地离开 metastability 的状态。

相反,这是习惯的想法: 当您将 clock domains 与 unrelated clocks一起使用时,总是有可能一些 flip-flop 将保持 metastability 状态的时间超过 design 可以容忍的时间,因此会出现问题。作为 designers ,我们唯一能做的就是降低这种风险。我们充其量只能实现一个可以忍受的 MTBF (Mean Time Between Failure)。

幸运的是,有一种成熟的技术可以实现这一点,这将我们带到下一个主题。

metastability guard

长话短说,让我们重温上一页的第一个代码示例。如果 @clk1 和 @clk2 是 unrelated clocks,那么从 @foo 获得稳定 @bar 的常用 resynchronization logic 是

reg foo, bar, bar_metaguard;

always @(posedge clk1)
  foo <= !foo;

always @(posedge clk2)
  begin
    bar_metaguard <= foo;
    bar <= bar_metaguard;
  end

顾名思义, @bar_metaguard 就是 metastability guard。在采样 @foo时,偶尔会违反实现 @bar_metaguard 的 flip-flop 的时序要求。因此 @bar_metaguard 可以有 metastability的短时刻。因为这个 flip-flop 预计会很快从这个状态中恢复,所以它会足够快地稳定,以满足 @bar对 setup的时序要求。因此, @bar 可以在 @clk2的 clock domain内部可靠地使用。

这种解释听起来可能不准确,但确实如此。它实际上与我在上面写的关于 metastability 的内容相矛盾,因为 metastability没有故障安全解决方案。我将在下面进一步讨论。但是现在,我们还是按照惯例,如上图添加 metastability guard ,不用再操心了。说实话,我从来没有听说过有人对此有问题。

想要更加安全的人,请添加更多 registers。所以双 metastability guard 是这样完成的:

reg foo, bar, bar_metaguard_a, bar_metaguard_b;

always @(posedge clk1)
  foo <= !foo;

always @(posedge clk2)
  begin
    bar_metaguard_a <= foo;
    bar_metaguard_b <= bar_metaguard_a;
    bar <= bar_metaguard_b;
  end

@bar_metaguard_a 是第一款 metastability guard。如果我们不走运,它会在 metastability 状态下停留太久,违反了 @bar_metaguard_b的时序要求。因此, @bar_metaguard_b 在下一个 clock cycle上有一个 metastability 的周期。但是这次 metastability 的时间很短,可以希望: 他们说,闪电从来不会两次击中一个地方。但当然 @bar_metaguard_b 也可以在 metastability 状态下保持足够长的时间来违反 @bar的 timing。但这种情况的概率是多少?请记住,目标是获得合理的 MTBF。

总结一下: 如果您正在寻找有关如何通过单个 bit处理 clock domain 的食谱解决方案,就是这样。一个 metastability guard 或两个 metastability guards 可以很好地完成这项工作。如果您正在寻找一个永不失败的解决方案,不幸的是这是不可能的。但是,如果您想尽可能减少发生事故的可能性,请继续阅读。

Timing analysis 的 metastability

因此,现在让我们回到上面使用单个 metastability guard的示例,并问为什么我如此确定 @bar_metaguard 从 metastability 中恢复得足够快。正如已经提到的,答案是没有理由确定。

不过,让我们来做一个 timing analysis。 @bar_metaguard 和 @bar 都与同一个 clock同步。假设没有专门为这些 registers 分配特殊的 timing constraints ,那么它们之间的 path 将受到 @clk2的 timing constraint (clock period) 的限制。也就是说,工具保证在没有 metastability的情况下, @bar 的 input signal 是用合法的 timing采样的。

但 @bar_metaguard 的全部意义在于它偶尔允许 metastability 。所以时序计算是不够的: 实际上, flip-flop 在 metastability 状态下花费的时间是对其 clock-to-output 时间的补充。换句话说, metastability 意味着 flip-flop 的 output 比它通常的 delay稳定。因此,如果 @bar_metaguard 和 @bar 之间的 path 的 slack 几乎为零(即其 propagation delay 足以达到 timing,但没有余量),则 metastability 可能会使 path的总 delay 超过允许的限制。此类事件将导致 @bar的 input违反 timing 。当然,这适用于温度、电压和制造过程中最坏的情况,但是: 这些工具在 metastability guard的 output 上应用常规 timing constraint 的事实意味着它不能确保必要的时序要求。

幸运的是,这很容易解决。或者改进,我应该说。方法是在需要可靠的 metastability guards 到 registers 的所有 paths 上加一个更紧的 timing constraint 。换句话说,这个想法是在这些 paths上允许更少的 propagation delay 。通过减少 propagation delay允许的时间,这个时间是为了从 metastability恢复。

例如,如果所有 metastability guard registers 都具有 *_metaguard 后缀,则可以编写单个 timing constraint 以要求来自它们的所有 paths 获得一些额外的稳定时间。对于 Vivado,这样的 constraint 会说:

set_max_delay -from [ get_cells -hier -filter {name=~*_metaguard*} ] 0.75

(set_max_delay 在时序异常页面有说明)

这个 timing constraint 意味着任何以 metastability guard 开始的 path 都有 0.75 ns 到达其目的地(这些 paths 由 registers名称的后缀选择)。这是我在我尝试过的特定 FPGA 上不使 timing constraint 失败而设法获得的最小延迟(因此在其他 FPGAs上可能有所不同)。达到这个数字的方法是选择一个较低的数字,直到工具无法达到这个 constraint。出现此故障时,根据需要增加此数字以获得非常小的 slack (例如 0.2 ns)。这迫使工具在这些 paths上发挥最大的作用。

此 set_max_delay 命令等效于请求 0.75ns (1333 MHz) 的 clock period 。因此,例如,如果实际的 clock period 是 250 MHz (4 ns),则实现此 timing constraint 至少会产生 4 – 0.75 = 3.25 ns的盈余。因此,如果 metastability guards 处于 metastability 状态直至 3.25 ns,则不会发生时序违规,这要归功于此 set_max_delay constraint。

这当然比完全没有保证要好,但是 3.25 ns 就够了吗?很多吗?是否足以确保 clock domains的故障安全过渡?

如前所述,这个问题的答案取决于几个因素。从已发表的实验来看,我个人的印象是,在今天实际使用的任何 FPGA 上,极不可能看到 flip-flop 保持 metastability 状态的时间与 1 ns一样长。但是在这件事上很难说任何肯定的话。

但是通过添加这个 timing constraint,从而推动工具发挥最大作用,您可以使您的 FPGA design 比其他人的情况更符合或更好。因此,本着黄金法则#4 (don't push your luck) 的精神,让自己处于一个位置,如果它对你不利,那么很多其他人也会有理由抱怨。

关于 timing 的讨论的另一个见解是,如果 clock的频率相对于使用的 FPGA 而言较高,那么双 metastability guard 可能是个好主意。如果不使用上面建议的额外 timing constraint ,则尤其如此: 如上所述, metastability 消耗了 path的 slack。随着 clock period 变小(频率变高),通常 slack会越来越少,因此 metastability的额外时间也会越来越少。

最后,让我们问一下,为什么几乎每个人都忽略了整个时间问题,却没有人抱怨问题。我将提供一些可能的解释,但这些只是推测。

所以第一个解释是,这些工具很有可能将 metastability guard 在物理上非常靠近 FPGA的 logic fabric 与另一个 flip-flop。因此,这两个 flip-flops 之间的 propagation delay (例如 @bar_metaguard 到 @bar)就 FPGA 而言非常短。但是,如上所述,如果没有明确的 constraining,则无法确保这种紧密的布局。

在 FPGA的供应商提供的官方 FIFOs 中,这种类型的 flip-flops 对经常出现在同一个 slice上。无论如何,可以相信官方 FIFOs 已经解决了这个问题。

另一件事是,随着 FPGAs 变得更快,更高的 clock 频率成为可能,事实证明 flip-flops 也更快地摆脱了 metastability 。因此,从 metastability 和 propagation delay恢复所需的时间,仍然比 clock的 clock period 短得多。

因此,大多数人只是添加 metastability guard 而不再担心它也就不足为奇了。然而,这并不是懒惰添加 constraint的借口。

clocks的频率限制

到目前为止,我还没有对任何 clock domain中 clocks 的频率说太多。例如,如果源的 clock domain (@clk1) 中的 clock 比目标的 clock (@clk2) 快怎么办?

因为 clocks之间不需要同步,所以源的频率本身是更高还是更低并不重要。但是,如果源的信号 (@foo) 变化太快,它可能会在目的地的 flip-flop 有机会对这种变化进行采样之前来回变化。因此,如果所有转换必须在目标处可见,则源的 clock period 必须比目标的 clock稍长(即具有较低的频率)。

还要多久?不是很多。或者,如果您坚持要准确的答案: 这主要取决于目的地 flip-flop 的时序要求。这是一个快速计算(除非你真的感兴趣,否则跳过它):

如果这个 flip-flop的 input 正好在那个 flip-flop的 clock edge之前改变了 tsu ,它就处于被正确采样的边缘,所以它必须在下一个 clock cycle上采样。否则,可能错过了这个变化。为了在下一个 clock cycle 上可靠地采样此变化, input 信号必须保持稳定,直到下一个 clock edge之后的 thold 。所以源的 clock period 必须比目标的 clock period 长 tsu + thold + 2tj。 tj 是 clock period (jitter) 中的不确定性。 jitter 被计算两次,一次用于源的 clock ,第二次用于 destination的 clock。

所以 tsu + thold + 2tj 就是我对源 clock的“稍长” clock period 的意思。

讲述 metastability guards的工具

一些 FPGA 工具的文档建议在 flip-flops 上加一个 attribute 作为 metastability guards使用。这可以防止工具对这些 flip-flops进行操作。它还鼓励工具将 metastability guard 放在与下一个 flip-flop相同的 slice 上,因此 routing 尽可能短。

例如, Vivado 有一个名为 ASYNC_REG的 attribute 。所以和以前一样,如果所有 metastability guard registers 都有 *_metaguard 后缀,则 XDC 文件中的这一行会根据需要设置 attribute :

set_property ASYNC_REG true [get_cells -hier -filter {name=~*_metaguard*}]

也可以在 Verilog 代码中设置此 attribute 。

但是,这个 attribute 不太可能产生任何影响,因为这些工具通常无论如何都能正确处理 metastability guards 。

本系列的第二页到此结束。下一页显示了跨 clock domains传递数据的技术。

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (6f913017)