此页面属于关于 timing的一系列页面。前几页解释了 timing 计算背后的理论,讨论了 clock period constraint,并展示了 timing closure的原理。现在是时候开始了解 timing constraints的技术细节了。
create_clock 作为 Tcl 命令的含义
前面几页都是围绕着这个 timing constraint展开的:
create_clock -period 4 -name clk [get_ports clk]
直到现在我才故意不讨论这一行的语法。所以是时候解释一下这到底意味着什么了。
这个 timing constraint 是用 SDC (Synopsys Design Constraints)格式写的,这是 timing constraints最常用的格式。 Vivado 和 Quartus 以及其他几个 FPGA 工具使用这种格式。
SDC 文件本质上是用 Tcl编写的 script 。因此, SDC 文件的内容是一个简短的计算机程序,而不仅仅是信息的集合。但是, SDC 文件作为 script 的功能仅限于一小部分用于编写 constraints的命令: 并非 Tcl script 中允许的所有内容都可以在 SDC 文件中完成。
create_clock 命令用于定义 timing constraint。但实际上,这条命令是告诉 FPGA 软件创建一个新的 clock object。而“object”这个词的意思就是它通常在软件工程方面的意思。因此,新的 clock object 是作为具有自己的 properties的 object 存储在 Tcl interpreter的内存中的东西。
例如, create_clock 命令中的“-name clk”部分将值“clk”赋予称为“name”的 property 。回想一下之前的一页, timing reports中使用了这个名称: 名称“clk”与 timing paths 一起出现是因为这个 constraint (或者更准确地说,因为这个 clock object)。
后来,我们看到 clocks还有其他名字,比如 clk_out1_clk_wiz_1 和 clk_out2_clk_wiz_1。这些实际上是其他 clock objects的名称,由工具自动创建。
有一个用于列出所有 clocks的 Tcl 命令: get_clocks。因此,对于上一页中带有两个 clocks 的示例,这是 Vivado的 Tcl console上的会话:
> get_clocks clk clkfbout_clk_wiz_1 clk_out1_clk_wiz_1 clk_out2_clk_wiz_1
get_clocks 和类似的命令在下一页有更详细的解释。
也可以查看这些 objects的 properties 。这些 properties无需全部了解: 我展示这个只是为了说明 clock 是 object。就个人而言,我从来不需要直接操作任何 object的 property 。
> report_property [get_clocks clk] Property Type Read-only Value CLASS string true clock INPUT_JITTER double true 0.040 IS_GENERATED bool true 0 IS_PROPAGATED bool true 1 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 NAME string true clk PERIOD double true 4.000 SOURCE_PINS string* true clk SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 2.000 > report_property [get_clocks clk_out1_clk_wiz_1] Property Type Read-only Value CLASS string true clock EDGES int* true 1 2 3 EDGE_SHIFT double* true 0.000 2.000 4.000 INPUT_JITTER double true 0.000 IS_GENERATED bool true 1 IS_INVERTED bool true 0 IS_PROPAGATED bool true 1 IS_RENAMED bool true 0 IS_USER_GENERATED bool true 0 IS_VIRTUAL bool true 0 MASTER_CLOCK clock true clk NAME string true clk_out1_clk_wiz_1 PERIOD double true 8.000 SOURCE pin true pll_i/inst/mmcme3_adv_inst/CLKIN1 SOURCE_PINS string* true pll_i/inst/mmcme3_adv_inst/CLKOUT0 SYSTEM_JITTER double true 0.050 WAVEFORM double* true 0.000 4.000
重要的是要意识到 create_clock 只是创建了一个 object。该命令的 parameters 只决定如何设置这个 object的 properties。例如,“-period 4”部分(在我反复展示的 timing constraint 中)只是表示某个 property,称为“PERIOD”,其值应为 4。
如果您想自己尝试这些 Tcl 命令,请注意 FPGA 工具之间存在差异。
在 Vivado中,这些命令只有在打开 Implemented Design后才能执行。
在 Quartus中,先打开 TimeQuest Timing Analyzer,然后依次点击 Create Timing Netlist、 Read SDC File 和 Update Timing Netlist。然后在 Tcl console上尝试一些命令,例如:
> join [ query_collection -all [ get_clocks ] ] "\n" > get_clock_info -waveform [get_clocks clk]
“clock”这个词的含义
当 FPGA 工具使用“clock”这个词时,通常表示 clock object,而不是 FPGA内部的物理 signal 。在 timing reports中尤其如此。
回想一下上一页,我曾多次使用术语“理论上的 clocks”。这些实际上是 clock objects。它们在 timing report中被称为“clocks”,但在 timing analysis 中的使用表明它们只是信息的容器。
那么这些 clock objects 和真正的 signals有什么联系呢?我们已经在 timing analysis中使用了 clock objects 的名称。所有这些如何协同工作?
当工具执行 design的 static timing analysis 时,将检查所有 paths 。如果 path 以 flip-flop开头,则工具会检查连接到 flip-flop的 clock input的 signal (即 net): 有没有和这个 signal相关的 clock object ?例如,当 signal 为 @clk时,相关的 clock object 就是用 create_clock 命令命名为“clk”的那个。在找到相关的 clock object后,工具可以从这个 object的 properties中获取必要的信息。
path末端的 flip-flop 也会发生同样的事情。所以现在工具有两个 clock objects 对应于 path。利用这些 objects的信息,工具可以执行 timing analysis。
当然,同样的程序适用于任何 sequential element,而不仅仅是 flip-flops。
为什么了解这一点很重要?其中,因为有时候 timing report 里面有个 error message 说有 registers 没有 clock。通常,这并不意味着 flip-flop 与其 clock input没有任何连接。相反,这意味着工具没有找到与此 clock input相关的 clock object 。也就是说,工具并没有查到这个 flip-flop的 clock input的任何信息。所以问题通常不在 logic design,而是少了一个 timing constraint (或者写错了)。
值得再说一遍: 当它在 timing report中显示“clock”时,这并不意味着 logic design中有一个具有该名称的 signal ,而是已经创建了一个具有该名称的 clock object 。我们怎么知道哪个 signal?那是下一个话题。
这是谁的 clock ?
timing report 难以阅读的原因之一是 clocks的名称。 logic design 中的大部分 clock signals 都是由 PLL创建的,我们已经看到出现在 timing report 中的名称可能没有帮助。大多数 FPGA 工具允许通过向 SDC 文件添加命令来重命名 clock objects ,但在大多数项目中并没有这样做。黄金法则#4是避免对您的项目有特殊意义的事情。
当 clock 的来源是 IP block (例如 Gigabit transceiver、 PCIe block 或 on-chip processor core)时,名称问题变得更加困难。在这种情况下, clock 的名称通常很少说明它的来源和相关内容。
那么这个问题是如何解决的呢?让我们从最简单的情况开始,当 clock的名称来自我们自己的 SDC 文件中的 create_clock 命令时。这又是同一个 timing constraint :
create_clock -period 4 -name clk [get_ports clk]
该命令的最后一部分是“[get_ports clk]”。在 Tcl 语言中,方括号的意思是将括号内的内容作为 Tcl 命令执行,然后用这条命令的结果代替这些括号。
get_ports 命令查找名为“clk”的 I/O port 。此命令的结果是代表此 port的 object 。所以在上面的 create_clock 命令中,这个 object 是这个命令的 argument 。这就是 create_clock 在 clock object 和真正的 signal之间建立连接的方式。
注意 port 的名称和 object 的名称都是“clk”。不要求它们相同,但建议这样做: object的名称出现在 timing reports中。因此, port 这个名字通常是最好的选择。
也可以使用 net objects 和 pin objects 作为 signal的标识符。这在代表 IP blocks自动生成的 timing constraints 中很常见。但是,如果您觉得有必要在您自己的 constraints中执行此操作,则很可能是您做错了什么。
因此,如果在 SDC 文件中使用了 create_clock 命令,则很容易分辨出 clock object 与哪个 signal 相关。但是那些由工具自动创建的 clock objects 呢?
在这种情况下,识别 clock 的最佳方法是查看 timing report。例如,在上一页的示例中,哪个 clock object 与 @pll_clk_8 相关?找出答案的一个简单方法是在 timing report中执行 text search 。于是搜索“pll_clk_8”,找到以下部分:
Location Delay type Incr(ns) Path(ns) Netlist Resource(s) -------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_1 rise edge) 16.000 16.000 AG12 0.000 16.000 clk (IN) net (fo=0) 0.000 16.000 pll_i/inst/clkin1_ibuf/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.738 16.738 pll_i/inst/clkin1_ibuf/INBUF_INST/O net (fo=1, routed) 0.105 16.843 pll_i/inst/clkin1_ibuf/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.049 16.892 pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O net (fo=1, routed) 0.975 17.867 pll_i/inst/clk_in1_clk_wiz_1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -4.438 13.429 pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.501 13.930 pll_i/inst/clk_out1_clk_wiz_1 BUFGCE_X1Y1 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.101 14.031 pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=1, routed) 1.369 15.400 pll_clk_8 SLICE_X49Y58 FDRE foo_reg_reg/C
这是 clk_out1_clk_wiz_1的 Source Clock Path ,这就是问题的答案。
另一种方法是使用 Tcl 命令获取信息。不同的 FPGA 工具如何做到这一点完全不同。在 Vivado中,打开 Implemented Design后可以使用如下命令:
> get_clocks -of_objects [ get_nets pll_clk_8 ] clk_out1_clk_wiz_1
此方法需要知道 net的名称。有时就像这个例子一样简单,有时需要找到这个 net的名字。 FPGA 工具通常提供一种使用 GUI执行此操作的方法。也可以为此目的使用 Tcl 命令。
事实上,我希望 Tcl 的几个示例让您相信了解如何正确使用 Tcl 的重要性。这就是下一页的内容。
使用 get_port的意义
在上面的示例中, create_clock 命令依赖 get_port 在 clock object 和物理 input pin之间建立连接。如上所述,此连接对于了解哪些 logic elements 连接到此 clock (或从中生成的 clocks )是必要的。
但是使用 get_port 并不是唯一的可能性。例如,也可以引用 global clock buffer的 output pin 。是这样的:
create_clock -name clk -period 4 [get_pins my_BUFG_inst/O]
不同之处在于工具将 global clock buffer的 output pin 视为 clock的起源。也就是说, clock paths的计算是从这个位置开始的。此原点的第一个 clock edge 出现在 0 ns,因此此 output pin 成为时间参考。
这是一个合法的 timing constraint,但它有两个重要的缺点:
- 必须要求工具将此 clock 视为与所有其他 clocks相关的unrelated clock 。即使对于使用相同 PLL生成的 clocks 也是如此。原因是这些工具将 output pin 视为时间参考。因此,对于 clocks 之间关于它们的 clock path delays 的差异没有补偿(即不考虑 clock skews )。
- 不可能(或很难)定义与在 FPGA外部可见的 clock 相关的I/O timing constraints。这是因为这样的 constraints 依赖一个 clock object 作为时间参考。但再一次,时间参考是 global clock buffer的 output pin。从外部 clock 到 global clock buffer 的 clock skew 是未知的(例如它随温度变化)。
因此,应尽可能使用 get_port 。否则,除了它本身, clock的 timing 应该被认为是未知的。
在此页面中,介绍了许多 Tcl 命令,但没有充分解释它们。下一页填补了这一空白。