01signal.com

验证时序约束是否正确

本页是关于时序的系列文章中的最后一页。前面几页解释了时序计算背后的理论,介绍了如何编写多种时序约束,并讨论了时序收敛(timing closure)的原则。

引言

FPGA 的开发方式常常是这样的:添加新功能,把不能工作的部分修好,然后再重复一遍。在这个过程中,那些可能写错、但还没有表现出可见问题的部分,很容易被忽略。

必须理解一个关键问题:即使时序约束使用不当,甚至没有得到满足,FPGA 设计也仍然可能工作得很好。正确使用时序约束(timing constraints)是保证 FPGA 稳定、可靠工作的重要因素之一。然而,忽略这个话题并不一定意味着马上就会出故障。更常见的情况是,不当的时序会引发偶发性故障,这些故障可能非常令人费解

本页归纳并重申本关于时序约束的系列文章中的许多建议,目的是强调在排查那些由时序问题引发的故障时,值得留意的几个方面。最好能有一种自觉,让自己平时也偶尔对这些问题做做检查;但在现实中,我们大多数人都是在遇到一个看起来有点像巫术的问题时,才会去做这件事。

阅读自己的时序报告

所有 FPGA 设计工具都会生成时序报告。我们打开它们最常见的原因,是工具没能满足时序约束:在报告里可以找到出问题的路径(path),并希望能弄清楚还能做些什么。

不过,查看时序报告还有一个同等重要的原因:验证工具对时序约束的理解是否符合我们的本意,并确认它们确实被正确地应用了。偶尔做一下这种检查是个好习惯,尤其是在添加了新的时序约束之后。

由于不同 FPGA 设计工具生成的报告在结构和格式上各有不同,在这里不可能逐条说明每份报告里每个部分的确切含义。因此,本页只阐述所有工具共通的原理。至于你所使用的工具,它的报告格式和能力还是值得花时间去熟悉一下的。

对时序报告做检查还有另一个潜在好处:它可能暴露出逻辑设计里不正确的地方。例如,如果设计中错误地使用了异步逻辑,或者依赖了一些无法施加时序约束的时钟,这些问题往往会反映在时序报告中:由于无法给这类逻辑施加约束,它们可能会以无约束路径的形式出现在报告里。

另外值得一提的是,设计中包含的 IP 核(IP core)常常会贡献出它们自己的时序约束,工具会把它们叠加在你提供的约束之上。这些约束所涉及的路径通常没有检查的必要,但它们会给时序报告增加不小的篇幅。

无约束的内部路径

在本页的讨论范围内,同步元件是指触发器、块 RAM、移位寄存器,以及任何其它在时钟上升沿或下降沿对数据输入进行采样、或者更新数据输出的逻辑元件。

所有从某个同步元件的数据输出、到达另一个同步元件数据输入的路径,都必须纳入时序分析,也就是说,必须有相应的时序约束。唯一的例外是,时序违例的可能性已经在终点处的同步元件上得到了妥善处理,就像无关时钟域之间的处理方式那样。

换句话说,凡是到达同步元件的路径都必须有时序约束,除非在该处已经显式地设置了重新同步机制,用来弥补设计工具不负责保证采样结果可预测这一点。

有时,只需要一条单行时序约束来定义参考时钟,剩下的交给 FPGA 工具即可。另一些情况下,需要做的不止这些。在最糟糕的情况下,某些内部路径会由于疏忽而一直处于无约束状态。造成这种情况的原因可能有多种,其中包括:

FPGA 设计工具支持生成用于列出无约束路径的时序报告,这些路径就是没有施加任何时序约束的路径。原则上,这个列表应当是空的:那些不需要时序约束的路径,应该在时序约束文件中显式地定义为伪路径(false path)。这类约束对本来就没有被纳入时序计算的路径不会造成任何功能上的改变,但它能让时序报告中的无约束路径列表保持为空,从而更容易发现那些因为疏忽而被漏掉的路径。此外,由于该列表能容纳的路径数量有限,那些根本不应该出现在列表中的路径反而可能被隐藏起来,因为里面列出的都是无害路径。

遗憾的是,在一些工具的时序报告中,无约束路径这一节还可能包含一些本来就无需施加时序约束的路径。例如,从时钟缓冲器输出端到另一个时钟资源输入端的路径。因此,时序报告可能会列出很多无约束路径,而这是完全正常的。这确实会让我们更难从报告中推断实际状况,但也不算是真正的问题,因为这类路径通常会与那些终点为触发器数据输入或其它同步元件的路径分开列出。说到底,还是要仔细阅读报告,弄清楚每组被列出的路径意味着什么。

工具默认生成的时序报告有时不够详细,无法包含这些信息。任何像样的 FPGA 设计工具都应该支持按需生成一份列出无约束路径的时序报告,并且可以选择每组列出多少条路径(10 条是一个合理的数量,即使相关列表本来应当完全是空的)。

时钟域之间的路径

对于一条内部路径,如果它的起点和终点分别与不同的时钟同步,而这些时钟是相关时钟(related clocks)(更准确地说,是逻辑把它们当作相关时钟),那么这条路径必须有时序约束。反之,如果时钟不相关,就不应当施加这些约束,因为这种不必要的限制会浪费高质量的布线资源,甚至可能导致时序约束无法满足。请参阅相关时钟与无关时钟的页面。

每个工具套件都有自己的默认规则,用来决定是否把一对时钟视为相关。在使用 SDC 语法书写时序约束文件的工具中(例如 Vivado 和 Quartus),都有 set_clock_groups 命令,可以用来定义相关时钟组和无关时钟组。还有其它方法可以引导工具做出正确的判断,尤其是使用伪路径(false path)时序约束。

因此,接下来的问题就是:如何检查工具到底把哪些时钟视为相关时钟?遗憾的是,每个工具套件的做法不尽相同。以 Vivado 为例,它有一份 Clock Interaction Report(时钟交互报告),用彩色图表显示每对时钟之间的情况:

也可以用自定义时序报告来调查这个问题,把报告范围限定在某些路径组上。这些路径可以按照所涉及的时钟来选取:可以选择那些从与某个时钟同步的逻辑元件出发、并在另一个时钟处结束的路径。专门去查看那些已知存在时钟域交叉的路径组,往往也会很有帮助。

尽管做起来可能有难度,仍然必须全面检查:工具到底对哪些时钟域交叉施加了约束?这些约束是否正确?与此同时,这也是一个机会,可以检查逻辑设计在需要的地方是否确实放置了重新同步逻辑。由于很容易搞不清每个信号究竟与哪个时钟同步,最终留下一个不安全的时钟域交叉,是常有的事。

做这类检查时,一定要把每个时钟的性质放在心上。例如,如果两个来自不同振荡器的时钟具有相同的标称频率,因而在时序约束文件中的定义也很相似,工具就可能会错误地把它们当作相关时钟,并计算穿越这两个时钟的路径上的时序。对这类路径施加约束没有任何意义,因为这两个时钟之间的相位关系完全没有保证。光是存在不必要的时序约束,也只会让工具更难满足时序,这倒还算无害。真正的问题在于,时序报告可能会误导我们,让我们以为这两个时钟确实是相关时钟。于是,如果只看约束和时序报告,这两个时钟之间的路径看上去似乎是安全的(也就是不需要针对时序违例做保护),而事实并非如此:这两个时钟除了频率大致相同之外,没有任何共同之处。要避免这类错误,唯一的方法就是弄清楚每个时钟到底是怎么产生的。

无约束的外部路径

所有从 I/O 引脚出发、或在 I/O 引脚结束的路径,都应当有时序约束。介绍具体做法的内容在另一个页面上。唯一的例外是时钟输入引脚,以及一些特殊接口的引脚,例如高速收发器引脚、直接连接到 FPGA 硅片中硬件处理器的引脚等等。如果接口的速度非常低,不定义时序约束也情有可原。例如,LED、按键,甚至 I2C 线路都可以不定义。但更好的做法,还是为这类引脚设置伪路径(false path)约束,这样时序报告中无约束 I/O 引脚的列表就能保持为空。

很多时候,为外部路径施加时序约束看起来毫无意义。例如,几个输出引脚都与同一个时钟同步,而且由于它们同时翻转,正确的时序已经由这个事实本身保证了。实现这种同时翻转的常见方法,是使用 IOB 寄存器。这个触发器能提供尽可能好的时钟到输出延迟,还能让输出引脚之间的偏斜小得惊人。

然而,这恰恰是一个很好的例子,说明为什么给输出引脚加时序约束仍然很重要:通过把时序约束收紧,可以保证引脚之间的偏斜确实很小。当设计中使用 IOB 寄存器时,收紧的时序约束还可以用来迫使工具使用这个触发器;或者至少能保证,如果工具没有这样做,也不会被忽视——如果工具没有把触发器放到期望的位置,时序就会失败。

出于类似的原因,也应该给输入引脚施加较紧的时序约束。

当使用时序约束来保证几个引脚之间的低偏斜时,重要的不仅是把约束收紧到报告的时序余量(slack)接近零,还要进一步验证:如果把要求收得更紧,工具确实会无法满足约束。这是因为 I/O 信号路径中可能含有可选的延迟线,工具可能会以反直觉的方式去使用它们。例如,Intel FPGA 的 Quartus 如果发现时序预算有富余,可能会在输入路径中加入一些延迟。工具是否会这样做,未必是我们想要或预料到的。

错误的伪路径与过于宽松的时序

有时候,时序约束确实被施加了,但它们过于宽松。这种情况要难发现得多,因为相关路径确实被纳入了时序计算,只是所采用的要求不对而已。

造成这类失误的原因可能有多种,其中包括:

要发现这类问题,并没有简单的公式。从头到尾仔细读时序报告当然是个好主意,但即使报告对每组路径显示了 10 条,也无法保证有问题的路径恰好会出现在其中。无论报告显示多少条路径,都是如此。

另一种处理办法,是重新审查写出来的时序约束。由于 SDC 约束是用 Tcl 编写的,因此可以把约束文件中用于选择逻辑元件的表达式(也就是带有 -from 和 -to 的那些部分)当作 Tcl 表达式来求值,并通读得到的端点列表。

因此,例如在 Vivado 的 .xdc 文件里出现这么一行:

set_false_path -to [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ]

可以打开实现后的设计,把这条伪路径约束所作用的目标全部列出来:

puts [join [ get_pins -hier -filter {name =~ */pclk_i1_bufgctrl.pclk_i1/S*} ] "\n" ]

这里的 Tcl 命令(puts 和 join)确保每个元素单独占一行,因此即使输出内容很长,也仍便于阅读。

对时序约束做这种审查很重要,但也并不轻松,因为它确实需要认真动脑筋。

小结

确保工具在各种路径上强制了正确的时序限制,是一项棘手的工作。它既要求你准确理解每条时序约束语句的含义,也要求你知道如何查看时序报告,从而提高发现错误的机会。

但比这些技术更重要的,是一种自律:要反复检查、再检查自己的设计,尤其是当设计表面上看起来工作正常的时候。

本页由机器从英文翻译而来。如有疑问,请参阅原文
Copyright © 2021-2026. All rights reserved. (dcc38493)