01signal.com

Timing constraints 为 multi-cycle paths

此页面属于关于 timing的一系列页面。前几页解释了 timing 计算背后的理论,展示了如何编写几个 timing constraints 并讨论了 timing closure的原理。本页针对multi-cycle paths讨论 timing constraints

介绍

关于 multi-cycle paths 的第一件事是它通常是一个坏主意。尽管此页面解释了如何使用 multi-cycle path constraints,但结论应该是完全避免这种技术。在 ASIC 世界中使用这种技术是有原因的,但在 FPGA design 中通常最好添加一个 clock 。

也就是说,让我们看看这种 timing exception 什么时候才有意义。

考虑这个 Verilog 代码示例:

reg foo, bar;
   reg en, pre_en;

   always @(posedge clk)
     begin
	pre_en <= !pre_en;
	en <= pre_en;

	if (en)
	  begin
	     foo <= !foo;
	     bar <= foo;
	  end
     end

请注意,此示例不完整: 您可能需要将 synthesis attributes 添加到 @pre_en 和 @en。否则,由于 synthesizer的优化可能会发生意想不到的事情。更多关于下面的内容。

@pre_en 在每个 clock cycle上在 '0' 和 '1' 之间来回变化。 @en 做同样的事情,稍有延迟。

“if (en)”部分意味着对于“begin”和“end”之间的所有内容, @en 充当 clock enable 的角色: 当 @en 为低电平时,这部分 Verilog 代码没有任何反应。换句话说,当 @en 为低电平时, @foo 和 @bar 的行为就好像 clock edge 不存在一样。

在这个例子中, @en 每两个 clock cycles就高一次。因此, @foo 和 @bar 的表现就好像 clock的频率是实际频率的一半。因此可以相应地放宽 timing 的要求: tsetup 的计算可以使用两倍大的 clock period 来完成。

至于 thold,没有变化: 此 timing 要求的计算假设相同的 clock edge 达到两个 flip-flops。因此, clock period 没有任何意义,正如在 thold 分析示例中所讨论的那样。因此,较慢的 clock 的错觉对 thold没有影响。

何时使用 clock enable

使用 clock enable只有两个理由:

目前尚不清楚 clock enable 是否会改善 FPGA的功耗。可以说,额外的 clock 会浪费电量。但是 clock enable 也是 fan-out高配的 signal 。平均而言, clock enable 更改其值的频率与它所替代的 clock 一样频繁。所以在 logic state 的变化上(耗电大户),没有区别。

除非有特殊原因必须使用 multi-cycle paths,否则请随意跳过此页面。就算你在 design 中有一个 clock enable 在技术上是合适的,但相关的 timing exception可能还是不要用比较好。特别是,如果没有这个 timing exception也能轻松实现 timing constraints ,那犯错误的风险是不值得的。

timing exception

关于上面的 Verilog 代码,这些是 Vivado的 multi-cycle path constraints :

set en_regs [all_fanout -endpoints_only -only_cells -flat [get_nets en]]
set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

Quartus也一样:

set en_regs [get_fanouts en]
set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

Vivado 和 Quartus 的区别就在第一行: 命令“all_fanout”与 Vivado一起使用,“get_fanouts”与 Quartus一起使用。 constraints 在与 SDC配合使用的其他工具上类似。

第一行查找所有 synchronous elements (cells),它们是从 @en开始的任何 path 的结尾。 cell objects 的这个列表存储在 $en_regs中。接下来的几行更改 paths 的 timing 要求,开始和结束于此列表中的 cells 。

这需要很多解释。为什么我要这样写 $en_regs 的定义?为什么有两个 set_multicycle_path 命令?为什么它在第二个命令中说“-hold”,即使我说过 thold 的 timing 要求不受 multi-cycle paths的影响?

我将从 set_multicycle_path 命令开始,因为这是更容易解释的部分。

set_multicycle_path 命令

如上所示,命令是

set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

为了稍微概括上面的示例,让我们也考虑一下: 如果 clock enable 在每八个 clock cycles中激活一次,则 Verilog code 将是:

reg en;
   reg [2:0] pre_en;

   always @(posedge clk)
     begin
	pre_en <= pre_en + 1;
	en <= (pre_en == 0);
     end

请注意, @en 的行为类似于 strobe,并且每次仅在一个 clock cycle 期间处于活动状态。它不是 clock divider的 MSb 。

这种可能性的 multi-cycle constraints 是:

set_multicycle_path -setup -from $en_regs -to $en_regs 8
set_multicycle_path -hold -from $en_regs -to $en_regs 7

鉴于这两个示例,很明显第一个 set_multicycle_path 命令中的数字只是 clock enable的分频比。

至于第二个命令,它的分频比相同,但减一。所以它总是 N 和 N-1。

关于第一个命令没有太多解释: 如果 clock enable 在 N中的一个 clock cycle 中处于活动状态,则允许的 delay 乘以 N。这与 tsetup的 timing 要求相关。

但是为什么会有第二个命令呢?为什么有必要对 thold说点什么?答案是第一个命令还更改了最小 delay的要求。换句话说, thold 的计算也受到带有“-setup”选项的命令的影响。为什么?可能没有很好的解释。

第二个命令纠正了这个问题: 它将对最小 delay 的要求改回到原始值。所以在第二个命令之后, thold 的计算与之前一样。

关于为什么在第二个命令中使用 N-1 ,文档中有冗长的解释。但说实话,这些额外的信息没有什么有趣的。 set_multicycle_path 相对于 thold 的行为很奇怪,理解为什么数字应该是 N-1 并不能减少它的奇怪程度。

但 set_multicycle_path 是比较容易的部分。真正的困难来了: 生成正确的 cell objects 列表以用作 $en_regs。

选择 registers

为了选择 $en_regs中应列出的 registers ,有必要了解是什么使 path 符合 multi-cycle path的条件。所以这是规则: 仅当 clock enable 控制两侧时,才允许放宽 path 的 timing 要求。这意味着当 clock enable 处于非活动状态时,可以保证两个 sequential elements 都不会在 clock edge之后更改其值。

clock domains的角度考虑: clock enable 控制的所有 sequential elements 都属于一个虚构的 clock domain。这个假想的 clock domain 里面的 clock 频率较低,所以这个 clock domain 里面的 timing 要求可以调整。

但是,如果 path的其中一侧不属于这个虚构的 clock domain,则这是两个related clocks之间的虚构 clock domain crossing 。这样的 path不需要特别做什么,因为现有的 timing constraints已经照顾好了。但是把 multi-cycle exception 套用在这种 path 上是不对的。

上面显示的 timing constraints 反映了这个想法: @en 控制的所有 sequential elements 在 $en_regs 中列为 cell objects。然后,将两个 set_multicycle_path 命令应用于 paths ,这两个命令的开头和结尾均位于属于该列表的 sequential elements 。

关于 multi-cycle paths 最困难的部分是确保 sequential elements 的这个列表是正确的: 此列表应包含由 clock enable控制的所有 sequential elements 。但没有其他 sequential elements 应该在此列表中。

如果此列表中缺少 sequential element ,则相关 paths 的 timing 执行将比必要的更严格。这不是灾难,但它降低了 timing exception 的效率。

但是如果误将一个不应该在列表中的 sequential element 加入其中,可能会产生严重的后果: 这将导致 paths 的 timing 计算不够紧密。换句话说,这些工具不能保证 sequential elements的正常运行要求。当不满足 timing 要求时,可能会发生奇怪的事情

我选择使用“all_fanout”(或“get_fanouts”)命令来创建这个 sequential elements列表。这并不总是能保证正确工作,这是我接下来要讨论的内容。之后,我将讨论创建此列表的其他选项。当 FPGA 工具不支持“all_fanout”或类似命令时,这些其他选项尤其相关。

all_fanout 和 get_fanouts可能出现的问题

multi-cycle constraint 最可能的错误是 clock enable 本身(即 @en)包含在列表中(即 $en_regs)。如果发生这种情况, multi-cycle exception 将应用于从 @en 本身到它控制的 sequential elements 的所有 paths 。这实际上意味着无法保证这些 paths 的 timing 要求。这可以产生可见的效果,因为 clock enable 通常是 signal 具有高 fan-out。

在上面的示例中, @pre_en避免了这种情况。您可能会问自己,为什么 @en 不是这样定义的:

always @(posedge clk)
  en <= !en; // Wrong!

如果 @en 是这样定义的,那么从 @en 到它自己就会有一个 path 。因此, @en 将包含在 $en_regs中。

所以 @pre_en 解决了这个问题。但重要的是要确保 synthesizer 不会为了优化而淘汰这个 register 。例如, Quartus的 synthesizer 检测到 @pre_en 的唯一用途是给 @en 赋值(在本页顶部的 Verilog 代码中)。因此 synthesizer 删除了 @pre_en,并继续说“en <= !en”。因此, @en 包含在 $en_regs中。一种可能的解决方案是按如下方式声明 @pre_en :

reg pre_en /* synthesis preserve */;

这个简单的示例演示了 synthesizer 的意外优化如何导致灾难性的结果。尽管解决方案很简单,但很容易忽略阻止这种优化的必要性。

clock enable 的另一个可能的问题与此 signal 通常具有较高的 fan-out这一事实有关。因此,这些工具可能会自动复制 register ,以便每个副本都有一个低于特定限制的 fan-out 。但这将如何影响 $en_regs?列入此列表的标准是基于特定的 net。因此,不包括由 @en 副本控制的 sequential elements 。

然而,这种情况的结果并不是灾难性的: 如前所述,这仅意味着对某些 paths 的 timing 强制执行将比必要的更严格。 design的可靠性不受影响。

registers 的复制已经在高 fan-outs的背景下进行了讨论。正如那里所争论的那样,手动复制 @en 比等待 synthesizer 复制要好。为了避免意外,请始终添加不允许复制此 register的 synthesis attribute 。如果较高的 fan-out 稍后导致 timing closure 出现问题,请通过手动复制解决这些问题。这样会更容易理解问题的根源。如果 synthesizer 由于项目增长而突然复制 @en ,那么就不会那么容易意识到为什么没有实现 timing constraints 。

无论哪种方式,必须更新定义 $en_regs 的命令,以便包含 @en 的副本。

说到这里,请注意 $en_regs 的定义依赖于 net的名称。如前所述,这意味着如果 synthesizer 只是将 net 的名称更改为与“en”不同的名称,则 $en_regs 将变为空列表。因此, multi-cycle path constraints 变得完全无用。这种可能性也不是灾难: design 仍然可靠,但要实现 timing constraints则更加困难。

另一个可能的问题是,由于 $en_regs 的定义方式, @en 不能用作 clock enable以外的任何其他用途。例如,考虑这个 Verilog 代码:

reg [7:0] counter;

always @(posedge clk)
  if (en)
    counter <= counter + 1;

在此示例中, @en 显然用作 clock enable。因此可以认为与 @counter 相关的所有 paths 都是 multi-cycle paths。但是这个呢?

reg [7:0] counter;

always @(posedge clk)
  if (en)
    counter <= counter + 1;
  else
    counter <= counter - 1;

在这里, @en 的使用与任何 register一样。 @counter 的值在每个 clock cycle上都会发生变化。所以 @counter 绝对不应该成为 multi-cycle path的候选者。然而, @counter 的所有 flip-flops 都包含在 $en_regs中: 从 @en 到所有这些 flip-flops都有 paths 。

这相对容易解决,方法是创建 @en的副本:

reg [7:0] counter;
reg non_ce_en;

always @(posedge clk)
  non_ce_en <= pre_en;

always @(posedge clk)
  if (non_ce_en)
    counter <= counter + 1;
  else
    counter <= counter + 2;

请注意,需要 synthesis attribute 以防止 synthesizer 将 @en 和 @non_ce_en 合并为一个 register。

综上所述,以 @en 开始的所有 paths 为基础的 $en_regs 定义简洁明了。但这个定义也是一个雷区。因此,让我们看看一些替代方案。

创建 $en_regs的替代方法

为 multi-cycle path timing exception 创建 sequential elements 列表的最安全方法是依赖 design hierarchy: 由 clock enable 控制的所有 logic 都应该在单独的 module 中(也可能在 sub-modules中)。这允许通过基于 cell object的全名查找 cells 来创建 $en_regs 。例如,对于 Vivado:

set all_sync [all_fanout -endpoints_only -only_cells -flat \
  [get_nets -of_objects [get_clocks clk]]]
set en_regs [filter $all_sync {name =~ module_ins/multicycle_ins/* }]

第一个命令查找所有连接到 @clk 的 logic elements , clock buffer 本身除外(此 clock 由名称为“clk”的 clock object 表示)。结果存储在 $all_sync中。这是制作包含所有可能相关的 synchronous elements 的列表的一种可能方法。第二个命令创建 $all_sync 中所有 logic elements 的列表,这些 logic elements 位于所述单独的 module内。

请注意,此方法依赖于 clock object 的名称和 instantiations的名称。这些预计不会改变。使用这种方法, clock enable 是否被复制或它的名称是否被 synthesizer更改都无关紧要。

使用单独的 module 的另一个优点是使用 Verilog 代码更容易: 由 clock enable 控制的 sequential elements 和不由 clock enable 控制的 sequential elements 之间混淆的可能性较小。

然而,将依赖于 clock enable的 logic 分离出来并放在单独的 module中并不总是很自然。此外,如果 Verilog 代码已经编写并且已知可以正常工作,那么对其进行更改可能不是一个好主意。

我还要提到另一种选择,它可能适用于某些情况: 所有 registers的命名约定。例如,可以为所有由 clock enable 控制的 registers 指定一个以“MC_”开头的名称。此选择使创建 $en_regs 的命令变得简单: 根据他们的名字搜索 cells objects 就可以了。通过相应地选择 instantiation的名称,也可以包括其他 logic elements (例如 block RAMs)。有些人会说这种方法使 Verilog 代码变得丑陋,有些人会说它更容易使用。没有一种方法是完美的。

为什么不使用 -of_objects

根据一个简单的标准来定义 $en_regs 似乎很有吸引力: 找到名称为“en”的 net ,并添加所有连接到此 net的 registers 。例如,对于 Vivado ,这可以写成:

set en_regs [get_cells -of_objects [get_nets en]]

这是错误的有几个原因。第一个原因是这包括 @en 本身。因此, multi-cycle constraints 适用于从 clock enable 本身到它控制的 sequential elements 的所有 paths 。如上所述,这是一个严重的错误。

第二个原因是,有些 sequential elements 可能会被忽略。根据上面的命令,包含的标准是 cell 应该连接到特定的 net (@en)。当此 net 直接连接到 flip-flop的 CE input时,此方法有效。但通常 synthesizer 选择使用基于 @en 的 combinatorial function 。

例如, synthesizer 可以选择实现 @foo ,就好像 Verilog 代码是这样的:

foo <= foo ^ en;

这在功能上等同于原始表达式:

if (en)
  foo <= !foo;

这种优化是合法的,应该是预期的: synthesizer 无论如何都必须使用 LUT 才能实现 NOT 的功能。那么为什么不用这个 LUT 直接获取 flip-flop 的 next value 呢? flip-flop的 CE input为什么还要加一根线?

这种优化的副作用是 flip-flop 本身并没有直接连接到 @en。因此它不会包含在 $en_regs中。已经讨论过这种情况的影响。

通常可以告诉 synthesizer 仅使用 @en 作为 synchronous elements的 clock enable input。例如,某些 synthesizers 支持称为“direct_enable”或类似名称的 synthesis attribute 。请注意,使用此功能时, synthesizer优化 logic 的自由度会降低。因此,为了解决工具的技术问题, design的性能可能会受到负面影响。

最重要的是,如果复制或重命名 @en ,则会出现与上述相同的问题。

因此,出于所有这些原因,以直接连接到 net 作为标准是一个糟糕的选择。

与 reset交互

假设我们将 synchronous reset 添加到上面的 Verilog 示例中:

always @(posedge clk)
     begin
	pre_en <= !pre_en;
	en <= pre_en;
     end

   always @(posedge clk)
     if (reset)
       begin
	  foo <= 0;
	  bar <= 0;
       end
     else if (en)
       begin
	  foo <= !foo;
	  bar <= foo;
       end

回想一下 multi-cycle timing exception 背后的想法是所有 synchronous elements 的行为就好像它们都是想象中的 clock domain的一部分一样。这个 clock domain 内部的假想 clock 的频率是 @clk的一半。因此,当 @en 为低电平时,所有 registers 都必须忽略 @clk 。在 Verilog 代码的最后一个示例中,情况并非如此: @reset 与 @en无关。

例如,考虑一下如果像这样定义 @reset 会发生什么:

assign reset = foo;

这是合法的 synchronous reset,尽管它可能没有实际用途。但是这个定义显示了 multipath exception的问题: 当 @en 为高电平时, @foo 之后在 clock cycle 上变为高电平。但这也让 @reset 变得很高。所以在下一个 clock cycle上, @foo 再次变低。 @foo 在每个 clock cycle上更改值。因此,从 @foo 到 multicycle path 本身导致 timing 要求在这个 path上不够严格。

这很容易通过让 @en 控制 synchronous reset 来解决:

always @(posedge clk)
     if (en && reset)
       begin
	  foo <= 0;
	  bar <= 0;
       end
     else if (en)
       begin
	  foo <= !foo;
	  bar <= foo;
       end

这是正确的,但是 @reset 必须与 @en一起激活。一个简单的解决方案是将 @reset 保持在几个 clock cycles的高电平。

相同的原则适用于 asynchronous reset。页面上写的关于 asynchronous resets的所有内容在这里也相关,但 multi-cycle path constraints更复杂。最简单的解决方案可能是使用 synchronizer,如另一页所建议的那样。

@en 和 asynchronous resets

@pre_en 的好处之一是它确保 @en 的所有副本始终具有相同的 logic level 。这听起来很明显,但如果asynchronous reset 使用不当则不能保证。例如,假设原始 Verilog 代码为:

reg en;

always @(posedge clk or posedge reset)
  if (reset)
    en <= 0;
  else
    en <= !en; // This is not recommended!

如果 @en 被复制,结果可以等同于:

reg en, en_1, en_2;

always @(posedge clk or posedge reset)
  if (reset)
    begin
      en <= 0;
      en_1 <= 0;
      en_2 <= 0;
    end
  else
    begin
      en <= !en;
      en_1 <= !en_1;
      en_2 <= !en_2;
    end

注意 @en_1 的 next value 取决于它自己的当前值,而不是 @en的值。这是复制 register的真实结果。

如果以不安全的方式停用 asynchronous reset 会怎样?有可能 @en 的一些复制品对 reset之后的第一个 clock edge 有反应,而其他复制品则忽略了这个 clock edge。结果将是副本的 logic state 永远不会变得相同(直到下一个 reset)。

@pre_en 解决了这个问题,因为所有副本都从同一源复制它们的 next value 。这确保了长期的正常运行,即使有一个艰难的开始。

概括

使用 clock enable很容易。将 set_multicycle_path 用作 timing exception的命令很容易。但是要使这项工作可靠地进行并不容易。有很多事情都可能出错,有时原因是 synthesizer 改变了它的行为以响应不断增长的 logic design。

这些意外问题的结果可能是 multi-cycle path 无法实现其目的: 如果放宽的 timing 要求未应用于某些 paths,则此方法的好处值得怀疑。更糟糕的是,如果将 multi-cycle path exception 应用于本不应该受到影响的 paths ,一个错误可能会导致 logic design不可靠。

因此,为相关的 paths 读取 timing reports 是至关重要的,以确保没有发生意外。不幸的是,这并不能防止将来出现意外: 随着 design 的发展, synthesizer的行为很难预测。

因此,如果可能的话,最好从同一个 PLL 生成一个额外的 clock ,而不是使用 clock enable。 clock domain crossings 加上这个新的 clock 是靠谱的,因为两个 clocks 都是related clocks 。此新 clock 的 timing constraints 由工具自动生成。这样不会有任何意外的风险。

因此,如果您正在阅读本文是因为您想为 design添加一个 clock enable 和一个 multi-cycle exception ,我希望此页面能让您有所思考。


FPGA中关于 timing constraints for paths 的部分到此结束。但是 I/O呢?这就是下一页开始讨论的内容。

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