本页是有关时序(timing)的一系列页面中的最后一页。前几页解释了时序计算背后的理论,展示了如何编写多个时序约束(timing constraints)并讨论了时序收敛(timing closure)的原理。
介绍
很多时候, FPGA 开发策略是添加功能,修复任何不起作用的部分,然后重复。很容易忽略设计中可能有问题但不会造成明显问题的部分。
了解 FPGA 设计可能工作得非常好是至关重要的,即使时序约束应用不正确并且即使它们没有实现: 正确使用时序约束是保证 FPGA稳定正确运行的关键部件之一。然而,如果忽略这个话题,并不一定意味着立即失败。相反,不正确的时序可能会导致偶发的故障,这些故障可能会让人非常困惑。
本页总结并重申了这一系列关于时序约束的页面中的许多建议。目的是强调一些在查找因时序不足而导致的问题时应牢记的主题。人们可能希望有智慧偶尔为此检查一下这些主题,但在现实生活中,我们大多数人这样做是为了解决一个看起来有点像巫术的问题。
阅读您的时序报告(timing report)
所有 FPGA design 工具都会发出时序报告。我们打开它们的最常见原因是当工具无法实现时序约束时: 这就是人们可以找到失败的路径(paths)的地方,并希望找出可以对它们做些什么。
但是,检查时序报告有一个同样重要的原因: 验证时序约束是否被工具按预期解释,因此它们被正确应用。偶尔做这个检查是个好习惯,尤其是在添加新的时序约束之后。
由于每个 FPGA design 工具都以不同的结构和格式生成这些报告(reports),因此无法准确关联每个报告(reports)中每个部分的内容。因此,本页概述了所有工具都通用的原理。至于您自己的时序报告的格式和功能,无论如何花时间学习它们都是一个好主意。
时序报告检查的另一个可能的好处是它可能会发现逻辑的错误使用。例如,如果设计包含错误使用的异步逻辑或依赖于无法应用时序约束的时钟,这在时序报告中会变得很明显: 因为将时序约束应用于此逻辑是不可能的,所以它可能会作为 unconstrained paths出现在报告中。
另一方面,值得一提的是,包含在设计中的 IPs 通常会贡献自己的时序约束,这些工具会添加到您提供的工具之上。通常没有必要检查与这些约束(constraints)相关的路径,但它们可以为时序报告增加相当多的重量。
Unconstrained internal paths
为了便于讨论,同步逻辑单元(synchronous element)是触发器(flip-flop)、 block RAM、 shift 寄存器或任何其他逻辑单元(logic elements),它们在时钟的上升沿(rising edge)或下降沿(falling edge)上对其数据输入(data input)进行采样和/或更改其数据输出(data output)。
所有从同步逻辑单元的数据输出到另一个同步逻辑单元(synchronous element)的数据输入的路径都必须定时,即服从时序约束。唯一的例外是当时序违反(timing violation)的可能性在目的地的同步逻辑单元上得到正确处理时,就像 unrelated 时钟域一样。
换句话说,路径到任何同步逻辑单元都必须定时,除非有明确的 resynchronization 机制来掩盖设计工具不承担以可预测的方式对输入信号进行采样的任何责任的事实。
有时定义参考时钟(reference clock)的单排时序约束就足够了, FPGA 工具负责其余的工作。在其他情况下,它需要的不止于此。在非常糟糕的情况下,一些内部路径错误地保留为 unconstrained 。这有几个可能的原因,其中包括:
- 忘记添加时序约束。
- 路径被错误地定义为false path 。
- 路径在 related 时钟之间穿过时钟域(clock domains),但工具将它们视为 unrelated 时钟(下文将详细介绍)。
- 时序约束不会通过时钟资源(clock resource)元素传播。例如,如果参考时钟连接到 FPGA 锁相环,则通常会为 FPGA的输入引脚提供时序约束。然后,工具会自动为锁相环(PLL)的输出时钟(output clocks)创建时序约束 。它们通常会这样做,但有时会出现意外。
- 时钟由逻辑生成(例如,使用在逻辑阵列(logic fabric)中实现的频率分频器(frequency divider))并且没有被时序约束明确覆盖。
FPGA design 工具支持创建列出 unconstrained 路径的时序报告, unconstrained 路径是未应用时序约束的路径。原则上,此列表应为空: 不需要约束的路径应该在时序约束文件中明确定义为 false 路径。这样的约束不会对路径的任何未计时的功能进行任何更改,但它们允许将时序报告中的 unconstrained paths 列表保持为空。这使得很容易发现无意中遗漏的路径。此外,由于此列表中的路径数量有限,因此绝对不应该在此列表中的路径可能会被隐藏,因为无害的路径被列出了。
但不幸的是,在某些时序报告中, unconstrained paths 的部分可能还包含没有理由被时序约束覆盖的路径。例如,路径从时钟缓冲器(clock buffer)的输出(output)到另一个时钟资源(clock resource)的输入。因此,时序报告可能会将一大堆路径列为 unconstrained,但这完全没问题。这使得从时序报告推断情况变得稍微困难一些,但这并不是真正的问题,因为这样的路径通常与以触发器的数据输入或其他同步逻辑单元结尾的路径分开列出。因此,归根结底就是要仔细阅读报告,并注意列出的每组路径的含义。
默认创建的时序报告有时不够详细,无法包含这些信息。任何合理的 FPGA design 工具都允许创建列出 unconstrained paths的按需时序报告,并可以选择为每个组列出多少个这样的路径(10 是一个合理的数字,即使相关列表应该完全为空)。
路径之间时钟域
如果时钟是 related 时钟(或者更准确地说,如果逻辑将它们视为相关),则必须在与源和目标处的不同时钟同步的内部路径上使用时序约束。否则,它们不应该如此,因为这种不必要的限制可能会浪费高质量的布线资源(routing resources),并可能导致无法实现时序约束。请参阅有关 related 时钟与 unrelated 时钟的页面。
每个工具集都有自己的方法来自动决定是否将一对时钟视为相关。在时序约束文件中使用 SDC 语法的工具(例如 Vivado 和 Quartus)具有 set_clock_groups command,它允许定义 related 时钟和 unrelated 时钟组。还有其他方法可以指导工具处理此事,特别是借助 false path 时序约束。
所以问题是如何检查工具是否将时钟视为 related 时钟。不幸的是,每个工具集都有不同的方式。以 Vivado为例,有一 Clock Interaction Report,用彩图表示每对时钟的情况:
- 从一个时钟到另一条路径(paths)已经计时(即时钟被认为是 related 时钟)。
- 从一个时钟到另一条路径的路径尚未计时(“User Ignored Paths”)。
- 一些路径已经定时。
或者,可以使用仅限于某些路径组的自定义时序报告来调查此问题。可以根据所涉及的时钟选择路径。这可以通过选择路径来完成,路径从与一个时钟同步的逻辑单元开始,并在另一个时钟结束。专门查看已知参与跨时钟域(clock domain crossings)的路径组也可能有益。
尽管这可能很困难,但全面审查工具将约束应用于哪个跨时钟域(clock domain crossings)以及它们是否正确是至关重要的。同时,这是一个审查逻辑设计是否在需要的地方有 resynchronization 逻辑的机会。由于对每个信号使用哪个时钟感到困惑,很容易以不安全的跨时钟域告终。
进行此审查时,务必要考虑每个时钟的性质。例如,如果来自不同 oscillators 的两个时钟具有相同的预期频率,因此在时序约束文件中具有相似的定义,则工具可能会错误地认为它们是相关的,并计算从一个时钟遍历到另一条路径上的时序。将约束应用于这样的路径是没有意义的,因为无法保证两个时钟之间的相位(phase)关系。就其本身而言,不必要的时序约束只会使工具更难实现时序,而这可能是无害的。真正的问题是时序报告可能会误导我们认为时钟实际上是 related 时钟。因此,仅通过查看约束和时序报告,它们之间的路径可能看起来是安全的(即不需要针对时序违反的保护),但事实上并非如此: 两个时钟没有任何共同之处,除了频率大致相同。避免此类错误的唯一方法是了解每个时钟是如何生成的。
Unconstrained external paths
时序约束应始终应用于从 I/O 引脚开始或以 I/O 引脚结束的路径。有一个单独的页面解释了如何执行此操作。唯一的例外是具有特殊接口的时钟输入引脚(clock input pins)和引脚,例如 gigabit transceivers、引脚,它们直接连接到 FPGA硅片上的 hardware 处理器等。如果接口非常慢,即使未定义时序约束也是可以原谅的。例如,使用 LEDs、按钮甚至 I2C 线路。但最好将 false path 约束分配给这样的引脚,因为它会使时序报告中的 unconstrained I/O 引脚列表保持为空。
很多时候在 external 路径上应用时序约束似乎没有意义。例如,如果有多个输出引脚(output pins)都与同一个时钟同步,则通过它们同时切换的事实来确保正确的时序。实现这种同时切换的常用方法是使用IOB 寄存器 。这款触发器提供了可能最好的 clock-to-output ,并且偏移(skew)的偏移介于输出引脚(output pins)之间,令人印象深刻。
然而,当输出引脚上的时序约束很重要时,这是一个很好的例子: 通过保持时序约束紧密,确保这些引脚之间的低偏移。当使用 IOB 寄存器时,紧缩时序约束可以成为强制工具使用此触发器的一种方式,或者确保如果工具无法这样做,它不会被忽视: 如果工具未按要求放置触发器,时序将失败。
出于类似的原因,也应该在输入引脚上应用紧时序约束。
当为了多个引脚之间的低偏移而使用时序约束时,重要的是不仅要限制 I/O 引脚直到报告的 slack 几乎为零,而且要验证需要更严格的时序会导致无法实现约束。这是因为 I/O signal 路径可能包含可选的时延 lines(delay lines),工具可能会以违反直觉的方式使用它。比如 Intel FPGA的 Quartus 如果时序 budget (timing budget)有余量的话,可能会在输入路径(input path)上加一些时延(delay) 。工具选择执行此操作可能既不希望也不期望。
不正确的 false paths 和其他宽松的时序
有时会应用时序约束,但它们过于宽松。这很难检测到,因为相关的路径是定时的,只是在错误的要求下。
此类事故有几个可能的原因,其中包括:
- False path 约束被意外地应用于路径,但实际上不应该,很可能是由于表达式(expression)中存在错误,该错误定义了它所涵盖的逻辑单元。
- 其他特定 time 约束的问题相同,例如 multi-cycle 路径的时序要求不正确。
- 指定所需时间量的值在时序约束中指定不正确。
- FPGA design 工具玩弄了一些卑鄙的伎俩。例如,除非 SDC 文件中出现“derive_pll_clocks”语句,否则 Intel FPGA的 Quartus 可能会将错误的时钟频率分配给路径。
没有简单的方法可以发现此类问题。从上到下仔细阅读时序报告绝对是一个好主意,但是即使报告显示每组 10条路径,也不能保证有问题的路径会恰好出现在那里。或者在任何数量的显示路径中,就此而言。
解决这个问题的另一种方法是检查时序约束的书写方式。由于 SDC 约束是用 Tcl书写的,因此可以将约束文件中选择逻辑单元(带有“from”和“to”)的表达式评估为 Tcl expressions,并读取端点列表。
因此,例如,如果此行出现在 Vivado .xdc 文件中:
set_false_path -to [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ]
可以打开 implemented design 并列出 false 路径适用的目的地:
puts [join [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ] "\n" ]
Tcl 命令(“puts”和“join”)确保每个元素都列在单独的行中,因此很容易读取输出(可能非常长)。
这种对约束的审查很重要,但也很困难,因为它需要真正的脑力劳动。
概括
确保这些工具对各种路径实施正确的时序限制是一项棘手的任务。这是了解时序约束语句的确切含义和知道如何检查时序报告的组合,以增加发现错误的机会。
但最重要的是,它需要自律地检查和重新检查设计,尤其是当它看起来工作正常时。