01signal.com

I/O 时序约束的 SDC 语法

本页属于关于时序的系列文章之一。前面几页解释了时序计算背后的理论,展示了如何编写多条时序约束,并讨论了时序收敛(timing closure)的原则。上一页介绍了 I/O 时序约束的一些基本原则。本页继续讨论这个主题的实践内容。

引言

I/O 时序约束的目的是确保与外部世界接口的可靠性:它们保证来自外部世界的每个信号都能可靠地到达 FPGA 上的相关触发器。同样,它们也保证 FPGA 发出的每个信号都能可靠地到达外部器件上的触发器。

I/O 时序约束是最难写的一种时序约束:有些时序参数取决于板上的外部电子元件。通常必须阅读这些外部元件的数据手册,以便确定正确的时序要求。要得到正确的时序约束,往往还需要用纸笔做一番计算。

跳过这项复杂任务、转而选择简单的替代办法,是很有诱惑力的。这种捷径就是试错:让工具随意发挥,然后看看能不能工作。如果输入端口有问题,就把接收该输入信号的触发器换到另一个时钟沿;同样,如果输出不好用,就把输出触发器换到另一个时钟沿。这种做法通常能又快又简单地把电子系统调通。

但这种方法的问题在于,时序行为会随温度变化;此外,半导体元件的制造工艺也会带来不确定性,FPGA 和外部电子元件都一样。因此,不当时序约束可能导致“黑魔法模式”。所有时序约束都是如此,但 I/O 时序约束尤其容易遇到这种情况。

跳过纸笔计算最糟糕的后果是:有时由于电路板设计的原因,时序要求根本无法保证。如果这类问题在电路板设计过程中被发现,通常还有简单的解决办法(改变与 FPGA 之间的走线,或者重新考虑时钟的分配)。但如果 PCB 制造出来之后才发现这类缺陷,就可能没有修复办法了。换句话说,就无法保证电子系统可靠工作了。

本页内容

本页概述 I/O 端口的基本时序约束。这里展示的是 SDC 语法,Vivado、Quartus 以及其他 FPGA 工具都使用这种语法。

本页首先介绍专用于 I/O 的时序约束:set_input_delay 和 set_output_delay,并解释这些约束的含义。随后会引到两个单独页面,分别展示 Vivado 和 Quartus 生成的时序报告示例。

也可以使用 set_max_delay 和 set_min_delay 来定义时序约束。在某些场景下,这两个命令更为合适。关于 FPGA 内部路径,我们已经遇到过它们。它们作为 I/O 时序约束时的含义也会在下面解释。

本页只讨论这些 I/O 时序约束的技术层面。理论部分请参考上一页,其中还介绍了如何为 I/O 端口定义伪路径(false path)。

set_input_delay 和 set_output_delay 的含义

当与外部器件的接口采用系统同步时钟(system synchronous clock)方式时,这两个命令是合适的。简单来说:

必须注意,只有当下面两个条件都满足时,这些定义才是正确的:

这两个条件对于确保时钟延迟被正确计算是必要的。

还要注意,如果命令中既没有 -min 也没有 -max,该命令会被解释成两条命令:一条带 -min 属性,另一条带 -max 属性。这大概不是你想要的。

这些命令的定义有点让人困惑:set_input_delay 定义的是数据信号在时钟边沿之后何时允许改变;set_output_delay 定义的是在数据信号改变之后何时允许出现时钟边沿。大概这样定义的原因是,数据手册中的数值可以直接用在时序约束里。

set_input_delay 和 set_output_delay 命令还有若干选项,这里不一一介绍。特别是,可以选择下降沿作为时间基准。更多信息请参考工具的文档。

务必同时使用 min 和 max

坚持在每条时序约束中同时使用 -min 和 -max,看起来似乎没有必要。例如,如果外部器件的 tsu 是 8 ns,下面这条命令有什么问题?

set_output_delay -clock theclk 8 [get_ports test_out]

它确实正确定义了建立时间。至于保持时间,它无意中被定义为 –8 ns。这等于允许输出端口在时钟边沿之前 8 ns 就改变电平。但谁在乎呢?这不可能发生,对吧?

嗯,其实这是可能发生的。我已经讨论过使用 PLL 基于来自输入引脚的时钟(也就是在板上可见的时钟)产生内部时钟。这样做可以让 PLL 把 FPGA 的内部时钟与输入时钟对齐。PLL 会稍微移动(平移)时钟,以补偿时钟分配网络的延迟。

实际上,为了满足某条时序约束,FPGA 工具可以随意把内部时钟移动得比板上时钟稍微早一点:如果 FPGA 内部时钟被移到外部时钟之前,外部器件感受到的时钟到输出延迟就会变小。这是因为 FPGA 内部的触发器与内部时钟同步,但外部看到的时序是相对于外部时钟的。

但当 FPGA 内部时钟早于板上时钟时,FPGA 的输出可能会在外部时钟边沿之前就改变。这可能导致接收这些输出的器件违反保持时间要求。

如果 set_output_delay 命令把保持时间定义为 –8 ns,那并不意味着输出会在时钟之前 8 ns 改变;但它允许工具以违反 thold 要求的方式移动内部时钟。使用带 -min 的 set_output_delay 可以正确地阻止这种情况发生。

针对走线延迟的修正

一定要记住,工具不会考虑 PCB 的走线延迟。工具没有这些信息。因此,在用 set_input_delay 和 set_output_delay 做时序计算时,它们假设该延迟为零。修正的方法是,把走线延迟加到数据手册给出的时钟到输出延迟和 tsu 上。

可能还需要考虑时钟偏斜(clock skew):在理想的 PCB 上,时钟到达所有元件时的延迟相同。在现实中,FPGA 与外部器件之间可能会有时钟偏斜。工具在做时序计算时,并不会把这种时钟偏斜考虑进去。

因此,如果时钟相对于外部器件更早到达 FPGA,则需要进行如下修正:

同样,如果时钟相对于外部器件更晚到达 FPGA,则需要进行如下修正:

注意,无论这里如何修正,时序报告显示出的时钟偏斜也可能不是零。但时序报告中出现的时钟偏斜与 FPGA 内部的时钟延迟有关,而不是 PCB 上的时钟偏斜。

时序报告示例

这些示例基于下面的 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 是一个输出引脚。注意,这里没有使用 PLL 来把内部时钟与板上时钟对齐,所以存在明显的时钟延迟。

时序约束如下:

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 clock)方式时,使用 set_input_delay 和 set_output_delay 就不太自然了。set_max_delay 和 set_min_delay 更适合这种情况。在之前的一页中,这两个命令只是作为时钟周期约束的补充或调整(时序例外)出现。所有路径都是内部路径,起点和终点都是时序元件。当这两个命令作为 I/O 时序约束使用时,路径的起点或终点是 I/O 端口。那么在这种情况下,时序分析是如何进行的?

事实上,深入探究这些命令的时序分析常常没有意义:它们的目的一般是通过写出工具几乎无法满足的时序约束,来限制工具的行为。这些约束中的数值,通常是通过反复尝试、逐步收紧约束而得到的。在这种方法下,时序分析本身并不重要。

尽管如此,理解 set_max_delay 和 set_min_delay 背后的计算仍然是个好主意:

回想之前的讲解,一次时序分析有两个部分:第一部分是源路径(Source Path):它计算从时钟边沿(位于外部时钟引脚)开始,直到第二个触发器数据输入端出现更新后的有效值为止所需的时间。这一部分是三个要素之和:

  1. 时钟边沿到达第一个触发器所花的时间(时钟路径)
  2. 这个触发器更新自身值所花的时间
  3. 这个新值到达第二个触发器所花的时间

第二部分是目的时钟路径(Destination Clock Path),它只包含时钟边沿到达第二个触发器所花的时间。我们已经知道这个触发器的输入何时被更新(来自源路径),因此可以把时间差与所需的 tsu 或 thold 进行比较。

但那是两端都是时序元件时的情况。当一端是 I/O 端口时会怎样?为了进行时序分析,该端口被当作一个假想的触发器。通向这个假想触发器的时钟路径延迟为零。

考虑常见情形:时钟由 create_clock 命令定义,而该命令依赖 get_ports(几乎我所有的例子都是如此)。时钟路径延迟为零,意味着这个假想触发器的时钟输入直接连接在时钟引脚上,因此时钟引脚与假想触发器之间没有延迟。

这个假想触发器的所有时序参数都是零:tsu、thold 和时钟到输出延迟。这并不反映任何真实的电子元件,但它赋予了 set_max_delay 和 set_min_delay 在与输出端口一起使用时的含义:端口的时钟到输出延迟。例如:

set_max_delay -to [get_ports test_out] 7
set_min_delay -to [get_ports test_out] 0

这两条时序约束要求 @test_out 的时钟到输出延迟在 0 ns 到 7 ns 之间。

解释一下为什么:回顾我们说过,通常 set_max_delay 命令类似于针对特定触发器之间路径的周期约束。那么目的时钟路径会怎样?计算从第二个时钟边沿的时刻开始,也就是从 7 ns 开始。但通向第二个触发器的时钟路径延迟为零,tsu 也为零。所以目的时钟路径的计算结果就是 7 ns。这就是源路径允许的最大值;源路径照常计算,也就是源时钟路径加上数据路径。总而言之,要求就是数据输出在第一个时钟边沿后 7 ns 内有效。这正是输出端口时钟到输出延迟的定义。如果相关时钟的 create_clock 命令基于 get_ports,那么这个时钟到输出延迟就是相对于 PCB 上时钟的。

参见Vivado 的时序报告示例

注意,set_output_delay 与外部器件的 tsu 或 thold 相关;set_max_delay 定义 FPGA 输出端口的时钟到输出延迟。所以这两种选择的主要区别在于关注点不同。

对于输入端口,set_max_delay 和 set_min_delay 的含义并没有直观的解释:源路径由输入引脚到接收输入信号的触发器数据输入之间的延迟组成。目的时钟路径则从时序约束命令所指定的时刻开始,并叠加上时钟路径延迟。这些计算没有多少实际意义(参见时序报告)。更自然的做法是使用 set_input_delay,因为它与外部器件的时钟到输出延迟相关。

注意,为 set_max_delay 和 set_min_delay 所做的时序分析与时钟周期无关。因此,当时钟频率改变时,工具强制这些约束时使用的数值不会变。相比之下,set_input_delay 和 set_output_delay 的计算与时钟频率有关。

I/O 时序约束是否依赖时钟频率,既可能是优点也可能是缺点,这取决于具体情况。如果时序约束是根据外部器件的时序参数编写的(并且接口是系统同步),那么依靠 set_input_delay 和 set_output_delay 可能更好:即使时钟频率改变,这些约束仍能保持正确。然而,如果时序约束的意图是迫使工具做出某些选择(例如使用IOB 寄存器),那么 set_max_delay 和 set_min_delay 更可能合适。

使用 -datapath_only

一条时序约束可能有一个动机:保证 FPGA 工具尽一切可能,实现到 I/O 端口或从 I/O 端口出发的最小可能延迟。这通常意味着使用IOB 寄存器。这也可能意味着避免在输入端口和触发器之间插入额外延迟(工具为了用更大裕量满足 thold 要求,可能会这样做)。

当时序约束用于这个目的时,并没有某个具体的延迟作为目标。其想法是阻止工具做任何其它事情,只让它实现尽可能好的结果。如果 FPGA 工具支持 -datapath_only,那么最好将它与 set_max_delay 一起使用。这样,时钟路径延迟会完全从计算中去掉,只考虑 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 时序约束的首选命令。确实,当接口采用系统同步时钟时,这通常是正确的选择。在其他场景中,可以考虑改用 set_max_delay 和 set_min_delay,因为它们可能更好地反映对 I/O 端口时序的限制要求。


本页结束了这个关于时序的系列文章。但还有最后一页,它以便于检查现有设计的方式总结了其中许多主题。

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