此页面属于关于时序(timing)的一系列页面。前几页解释了时序计算背后的理论,展示了如何编写几个时序约束(timing constraints)并讨论了时序收敛(timing closure)的原理。上一页解释了有关 I/O 时序约束的一些基本原则。本页继续介绍本主题的实际方面。
介绍
I/O 时序约束的目的是确保与外界的可靠接口: 它们保证来自外部世界的每个信号可靠地到达 FPGA上的相关触发器(flip-flop)。同样,他们还确保从 FPGA 到外部世界的每个信号都可靠地到达外部组件上的触发器。
I/O 时序约束是时序约束(timing constraints)中最难的一种: 部分时序参数(timing parameters)取决于电路板上的外部电子元件。通常需要读取这些外部元件的数据手册(datasheets)才能确定正确的时序要求(timing requirements)。通常需要用纸笔计算才能获得正确的时序约束。
跳过这个复杂的任务并选择一个简单的替代方案很诱人: 反复试验。这个捷径包括让工具做他们想做的事,然后看看它是否有效。如果输入端口有问题,请在接收输入信号的触发器上使用相反的时钟边沿(clock edge)。同样,如果输出(output)不能正常工作,请在输出触发器(output flip-flop)上使用相反的时钟边沿。这种方式通常可以让电子设备快速简单地工作。
这种方法的问题是时序的行为会随着温度的变化而变化。由于半导体元件的制造过程,也存在不确定性。这适用于 FPGA 以及外部电子设备。所以不正确的时序约束会导致“ Black Magic 模式”。所有时序约束都是如此,但 I/O 时序约束更常见。
跳过纸笔计算的最糟糕的事情是,有时由于电路板设计,无法保证时序要求。如果在电路板设计的过程中发现这种情况,通常有一个简单的解决方案(更改 FPGA 的接线或重新考虑时钟的分布)。但如果在电路板生产后发现此类缺陷,则可能无法修复它。换句话说,无法保证电子设备可靠地工作。
本页内容
本页概述了 I/O 端口的基本时序约束。此处显示的语法是 SDC, Vivado 和 Quartus以及其他 FPGA 工具也使用该语法。
此页面以专用于 I/O的时序约束开头: set_input_delay 和 set_output_delay。这些约束(constraints)的含义解释一下。接下来是对两个单独页面的引用,这些页面显示了 Vivado 和 Quartus的时序报告(timing reports)示例。
也可以将时序约束与 set_max_delay 和 set_min_delay一起定义。这些命令在某些场景中更合适。我们已经在 FPGA中的路径(paths)中见过它们。它们作为 I/O 时序约束的含义也在下面解释。
本页仅讨论有关这些 I/O 时序约束的技术方面。对于理论部分,请参考上一页,其中还展示了如何为 I/O 端口定义 false paths 。
set_input_delay 和 set_output_delay的含义
这两个命令适合与外部元件接口为系统同步(system synchronous)的情况。总之,
- set_input_delay -clock … -max … : 输入端口+电路板的 trace 时延外接元件最大 clock-to-output 。
- set_input_delay -clock … -min … : 连接到输入端口的外部组件的最小 clock-to-output 。如果数据手册没有提供此信息,请选择零(以防万一此组件的未来版本将使用非常快的 process制造)。
- set_output_delay -clock … -max … : tsu 的输出信号+电路板的 trace 时延外接组件。
- set_output_delay -clock … -min … : 接收输出信号的外部组件的 –thold 。注意减号。例如,如果 hold time 为 1 ns,则将此约束设置为 -1 。
重要的是要注意,只有满足以下两个条件时,这些定义才是正确的:
- 接口是系统同步 。
- 定义时钟的 create_clock 命令引用带有 get_ports 命令的时钟信号(而不是依赖 FPGA 内部的另一个信号,例如 get_pins)。
这两个条件是确保时钟时延(clock delays)计算正确所必需的。
还要注意,如果既不使用 -min 也不使用 -max ,则命令将被解释为有两个命令: 一个命令和一个 -min 属性,第二个命令和 -max 属性。这可能不是您想要的。
这些命令的定义有点令人困惑: set_input_delay 定义何时允许数据信号(data signal)在时钟边沿之后更改其值。但是 set_output_delay 定义了在数据信号更改其值后何时允许时钟边沿。据推测,这些定义背后的基本原理是数据手册中的数字可以直接用于时序约束。
set_input_delay 和 set_output_delay 命令有几个选项,这里不作介绍。特别是,可以选择下降沿(falling clock edge)作为时间参考。有关更多信息,请参阅工具文档。
始终同时使用 min 和 max
坚持为每个时序约束同时使用 -min 和 -max 似乎毫无意义。例如,如果外部组件的 tsetup 是 8 ns,这有什么问题?
set_output_delay -clock theclk 8 [get_ports test_out]
这正确定义了 setup time 。至于 hold time,无意中定义为 –8 ns。这允许输出端口(output port)在时钟之前更改其值 8 ns 。但谁在乎?那不可能发生,不是吗?
好吧,实际上它可以。我已经讨论了使用锁相环(PLL)生成内部时钟,基于来自输入引脚的时钟(即在电路板上可见的时钟)。这允许锁相环将 FPGA的内部时钟与输入时钟对齐。锁相环通过稍微移动 (shifting)时钟来补偿时钟 distribution network(clock distribution network)的时延(delay)来实现这一点。
实际上, FPGA 工具可以随意将时钟移动到比电路板的时钟稍早一点,以实现时序约束: 如果将 FPGA 内部的时钟移动到外部时钟之前,则外部组件感知到的 clock-to-output 会变小。这是因为 FPGA 内部的触发器与内部的时钟是同步的,但是可见的时序是相对于外部的时钟而言的。
但是当 FPGA的内部时钟早于电路板上的时钟时, FPGA的输出可以在外部时钟的时钟边沿之前进行更改。这会导致接收这些输出的组件违反 hold time 。
如果 set_output_delay 命令将 hold time 定义为 –8 ns,这并不意味着输出会在时钟之前更改其值 8 ns 。但这允许工具以违反 thold 要求的方式移动内部时钟。正确使用 set_output_delay 和 -min 可防止这种情况发生。
由于 trace 时延的调整
请务必记住,这些工具不会考虑电路板的 trace 时延。这些工具没有此信息。因此,在对 set_input_delay 和 set_output_delay进行时序计算时,他们假设此时延为零。更正是将 trace 时延添加到 clock-to-output 和 tsu的数据手册值中。
可能还需要考虑时钟偏移(clock skew): 在完美的电路板上,时钟到达具有相同时延的所有组件。在现实生活中, FPGA 和外部组件之间可能存在时钟偏移。在工具的时序计算中不考虑此类时钟偏移。
因此,如果时钟比 FPGA 更早到达(相对于外部组件),则需要进行以下更正:
- 对于 set_input_delay -clock … -max … : 将时钟偏移添加到命令的时延值(这类似于外部组件的更大的 clock-to-output )。
- 对于 set_output_delay -clock … -min … : 将时钟偏移从命令的时延值中减小,即使得其更负(这类似于外部组件的更大的 thold )。
同样,如果时钟晚于 FPGA到货,则需要进行以下更正:
- 对于 set_input_delay -clock … -min … : 从命令的时延值中减去时钟偏移(这类似于外部组件的较小 clock-to-output )。如果外部组件的数据手册没有给出最小 clock-to-output 值,则使用时钟偏移的减号作为此命令的值。
- set_output_delay -clock … -max … : 将时钟偏移添加到命令的时延值(这类似于更大的电路板 trace delay(PCB trace delay))。
请注意,无论此处概述的调整如何,时序报告都可能显示不为零的时钟偏移。然而,出现在时序报告中的时钟偏移与 FPGA内部的时钟时延相关,而不是电路板上的时钟时延。
时序报告的例子
这些示例基于以下 Verilog 代码:
module top(
input test_clk,
input test_in,
output reg test_out
);
reg test_samp;
always @(posedge test_clk)
begin
test_samp <= test_in;
test_out <= test_samp;
end
endmodule
@test_clk 是输入时钟, @test_in 是输入引脚, @test_out 是输出引脚(output pin)。请注意,没有锁相环用于将内部时钟与电路板的时钟对齐,因此有一个重要的时钟时延。
时序约束如下:
create_clock -name theclk -period 20 [get_ports test_clk] set_output_delay -clock theclk -max 8 [get_ports test_out] set_output_delay -clock theclk -min -3 [get_ports test_out] set_input_delay -clock theclk -max 4 [get_ports test_in] set_input_delay -clock theclk -min 2 [get_ports test_in]
由于时序报告比较长,它们在单独的页面上显示:
使用 set_max_delay 和 set_min_delay
当与外部组件的接口是source synchronous时,使用 set_input_delay 和 set_output_delay 就不那么自然了。 set_max_delay 和 set_min_delay 更适合这种情况。在上一页中,这两个命令仅作为时钟周期约束(clock period constraints)的补充或调整(timing exceptions)被提及。所有路径都是内部的: 它们以时序逻辑元件(sequential element)开始和结束。当这些命令用作 I/O 时序约束时,路径的开始或结束都是 I/O 端口。在这种情况下,时序分析是如何完成的?
事实是,深入研究这些命令的时序分析通常是毫无意义的: 它们的目的通常是通过编写工具几乎无法满足的时序约束来限制工具的行为。因此,这些时序约束中的数字是通过反复尝试使这些约束更严格而发现的。使用这种方法,时序分析本身并不重要。
也就是说,了解 set_max_delay 和 set_min_delay背后的计算仍然是个好主意:
回想一下,时序分析有两个部分: 第一部分是源路径(source path): 它计算从时钟边沿(在外部时钟引脚处)到第二个触发器(flip-flop)的数据输入(data input)处的更新有效值的时间。这部分是三个元素的总和:
- 时钟边沿到达第一个触发器(时钟路径(clock path))所需的时间
- 这个触发器更新它的值所花费的时间
- 这个新值达到第二个触发器所用的时间
第二部分是目的地路径(destination path),它仅包含时钟边沿到达第二个触发器所需的时间。我们已经知道此触发器的输入何时更新(从源路径开始),因此可以根据需要将时间差与所需的 tsu 或 thold进行比较。
但两个时序逻辑元件(sequential elements)确实如此。当一侧是 I/O 端口时会发生什么情况?为了时序分析,端口被视为虚拟的触发器。时钟路径时延(clock path delay)到此触发器为零。
让我们考虑通常的情况,其中时钟定义为依赖于 get_ports 的 create_clock 命令(如我几乎所有示例中所示)。时钟路径时延为零意味着这个假想触发器的时钟输入直接连接到时钟引脚。因此,时钟引脚和这个假想触发器之间没有时延。
此触发器的所有时序参数均为零: tsu、 thold 和 clock-to-output。这并不反映任何真实的电子元件,但它赋予 set_max_delay 和 set_min_delay 与输出端口一起使用时的意义: 端口的 clock-to-output。例如:
set_max_delay -to [get_ports test_out] 7 set_min_delay -to [get_ports test_out] 0
这两款时序约束要求 @test_out的 clock-to-output 介于 0 ns 和 7 ns之间。
让我们解释一下原因: 回想一下,通常情况下,对于特定的触发器之间的路径, set_max_delay 命令与 period 约束相似。那么Destination Clock Path会发生什么情况呢?计算从第二个时钟边沿(clock edge)开始,即 7 ns。但是时钟路径时延到第二个触发器为零,并且这个触发器的 tsu 也为零。因此, Destination Clock Path 的计算结果就是 7 ns。这是 Source Path允许的最大值,其计算方式与往常一样: Source Clock Path 加上 Data Path。总之,要求是第一个时钟边沿之后的数据输出(data output)是有效的 7 ns 。这正是输出端口的 clock-to-output 的定义。如果相关时钟的 create_clock 命令基于 get_ports,则此 clock-to-output 相对于电路板上的时钟。
请参阅时序报告和 Vivado的示例。
请注意, set_output_delay 与外部组件的 tsu 或 thold 相关。 set_max_delay 定义了 FPGA的输出端口的 clock-to-output 。所以这两个选项之间的主要区别在于焦点在哪里。
关于一个输入端口, set_max_delay 和 set_min_delay 是什么意思,没有直观的解释: Source Path 由输入引脚和接收输入信号的触发器的数据输入之间的时延组成。 Destination Clock Path 的启动时间是在时序约束命令(timing constraint command)中指定的时间。时钟路径时延会添加到这个时间中。这些都是无意义的计算(参见时序报告 )。使用 set_input_delay更为自然,它与外部组件的 clock-to-output 相关。
请注意,代表 set_max_delay 和 set_min_delay 生成的时序分析不依赖于时钟周期(clock period)。因此,如果时钟的频率发生更改,则在工具强制执行这些约束时将使用相同的数字。相比之下, set_input_delay 和 set_output_delay 的计算依赖于时钟的频率。
I/O 时序约束对时钟的频率的依赖可能是优点也可能是缺点,具体取决于具体情况。如果时序约束是基于外部组件的时序参数编写的(并且接口是system synchronous ),那么最好依赖 set_input_delay 和 set_output_delay: 即使时钟的频率发生变化,这些约束仍将保持正确。但是,当时序约束的目的是强制工具做出某些选择(例如使用IOB 寄存器 )时, set_max_delay 和 set_min_delay 更可能合适。
使用 -datapath_only
时序约束的一个可能动机是确保 FPGA 工具尽其所能实现最小可能的时延到 I/O 端口或从 I/O 端口到IOB 寄存器。这通常意味着使用IOB 寄存器 。这也可能意味着避免在输入端口和触发器之间插入额外的时延(工具可能会这样做,以便以更好的裕度满足 thold 要求)。
当时序约束用于此目的时,没有特定的时延作为目标。这样做的目的是防止工具做除实现最佳结果之外的任何事情。如果 FPGA 工具支持 -datapath_only,最好使用带有此选项的 set_max_delay 。这完全消除了计算中的时钟时延路径(clock delay path),因此只考虑 I/O 端口和触发器之间的时延。这样,时序约束的要求就与其目的准确对应: 在触发器和 I/O 引脚之间控制时延。
这是 Vivado的一个简单示例:
set_max_delay -datapath_only -from [get_ports test_in] 2 set_max_delay -datapath_only -from [all_registers] \ -to [get_ports test_out] 3
但是标有“-from [all_registers]”的部件的用途是什么?为什么需要“-from”?简而言之, Vivado 拒绝接受没有“-from”部件的命令。对于输入端口,命令没有类似的要求。
时序报告和 datapath_only 位于带有示例的页面底部。
概括
set_input_delay 和 set_output_delay 通常被认为是 I/O 时序约束的首选命令。事实上,当接口是system synchronous时,这通常是正确的选择。在其他情况下,可能值得考虑使用 set_max_delay 和 set_min_delay ,因为它们可能更好地反映了 I/O 端口的时序所需的限制。