01signal.com

使用 Tcl 命令选择 logic elements

此页面属于关于 timing的一系列页面。前几页解释了 timing 计算背后的理论,讨论了 clock period constraint,展示了 timing closure的原理,并开始研究 Tcl 环境。此页面解释了允许 timing constraints 特定的命令。

概述

回想一下, timing constraints 的目的是确保满足 design 中所有 paths 的 timing 要求。不同的 paths 可能有不同的要求,所以每个 timing constraint 都必须指向 paths的相关组,不能包含任何其他内容。

定义 paths 的唯一方法是引用与这些 paths相关的 logic elements 。因此,能够编写准确的表达式来选择正确的 logic elements组非常重要。换句话说,必须正确使用像 get_clocks、 get_ports 和 get_cells这样的命令,以便它们的结果正是所需要的。

本页介绍了描述 logic elements组的基本技术。这里几乎所有内容都是特定于 SDC 语法的。我还将假设 Tcl command-line 接口可用。 Vivado 和 Quartus以及最近的大多数 FPGA 工具都是如此。

一些 FPGA 供应商公开宣称 Synopsys的软件已集成到他们的工具中,因此这些工具自然会使用相同的 Tcl 命令。另一方面, Vivado不被认为是从 Synopsys派生的。然而,两者之间有一些惊人的相似之处,尤其是在 Tcl 接口方面。 Quartus 好像是独立开发的,所以它的 Tcl 接口有点不一样。

乍一看,似乎此页面对 Tcl scripting的详细信息过多。事实恰恰相反: 准确性极其重要,只有准确理解命令的解释方式才能做到这一点。事实上,这个页面只是解释了基本原理。阅读文档是无可替代的。

从 FPGA 工具获取指导

开始编写 timing constraints通常很困难,因为需要处理的细节太多了。 FPGA 工具可以帮助解决这个问题。

在 Tcl console 中可以使用“help”命令来查看有关 Tcl 命令的文档。这通常与官方文档中的信息完全相同(例如 pdf 格式)。

大多数 FPGA 工具都有一个 GUI 接口,用于自动创建 timing constraints 。工作方式通常用于选择所需的 timing constraints 类型。下一步是从列表中选择相关的 logic elements 。结果是将一个或多个 timing constraints 添加到 SDC 文件中。

使用此类 wizard 有助于获得有关 Tcl 语法的提示。有时,自动创建的 constraints 也可以按原样使用。然而,在大多数情况下,抵制使用 wizard输出的诱惑是值得的,而是仔细思考如何最好地实现 timing constraint 的目的。同样重要的是要考虑项目的预期发展方式,并确保 timing constraints 随着时间的推移保持正确。使用 GUI wizard 进行快速会话不太可能实现这一目标。

一些 FPGA 工具还有一个 GUI 接口,用于在 design中查找 logic elements 。使用该接口时,通常会显示与请求的搜索对应的 Tcl 命令。这是获取 Tcl 表达式以查找特定 logic elements的便捷方法。再一次,这些表达式应该被视为进一步工作的起点。

大多数 FPGA 工具的第三个功能是 Tcl command-line console ,它允许键入命令(或使用 Copy-Paste)并在屏幕上查看结果。这允许测试命令及其 search patterns,并查看找到了哪个 objects 。这有助于验证 search pattern 是否正确。

总之, FPGA 工具可以协助创建 Tcl 命令。但正如已经说过的,由工具创建的 Tcl 命令应该只是编写准确的 search patterns 的基础,保证随着时间的推移正确工作。

现在,当我们知道创建 timing constraints的惰性方法时,是时候学习如何正确地执行此操作了。

netlist: 一个简短的提醒

在更具体地了解 Tcl 环境之前,我想简要提醒一下 netlists。

netlists 最常见的文件格式是 EDIF,但许多 FPGA 工具也有自己的特定格式。通常, synthesizer 会创建 netlist,即使这些工具也可能在后期阶段对其进行更改。该文件描述了 logic design 的基本组件以及这些组件之间的连接。它就像接线图,但具有文本表示而不是图形图像。

netlist 中的组件称为“cells”。绝大多数 cells 都类似于 LUT, combinatorial logic 或 flip-flop的另一个小元素。此外, Verilog 代码(例如 IP cores)中的 black boxes 的 instantiations 在 netlist中表示为 cell 。其他 cells 有 PLLs、 block RAMs、大 logic elements (“hard IPs”): PCIe blocks、 MGT transceivers、 processors 等

每个 cell 都有一些pins : 这些 pins 就像一个物理电子元件的外部连接点。但不要将它与 FPGA的外部 I/O混淆: cells 和 pins 都存在于 FPGA内部。

netlist 中的互连由 nets组成。这些就像物理电线。例如,当在 Verilog中使用“wire”定义 signal 时,这将导致 net。一个 net 连接在两个或多个 pins之间,这样可以保证这些 pins 始终拥有相同的 logic level 。

logic elements 表示为 objects

当 FPGA 工具通过项目的 implementation 运行时,实际发生的是 Tcl scripts 被执行。 Vivado 和 Quartus 以及其他几个 FPGA 工具都是如此。即使使用不同工作方式的软件,做出以下假设仍然是正确的: 一切都是一个大 Tcl script 的错觉是由 API 为 constraints 和其他 script 文件创建的。

在这个 Tcl script 的环境中(无论是否是虚构的),所有 logic elements 都表示为从不同的 classes创建的 objects 。这些 objects 可以被 SDC 文件(或 Xilinx的 XDC 文件)中的 timing constraints 访问。同样, Tcl command-line console 和 Tcl scripts 中的 Tcl 命令可以访问这些 objects。

这些是所有使用 SDC 语法的 FPGA 工具都支持的五个 Tcl 命令。这些命令用于查找不同类型的 objects (即不同的 classes)。我已经在之前的 timing constraints示例中使用过它们。事实上,如果没有这些命令,就不可能编写出有意义的 timing constraints 。

在没有任何 arguments的情况下,这些命令会找到相关类型的所有 objects 。稍后,我们将看到如何优化搜索。

除了这五个命令外,每个 FPGA 工具都有自己的附加命令和附加的 objects。例如, Vivado 具有 all_ffs、 all_registers、 all_inputs、 all_outputs、 all_rams 等其他命令。 Quartus 支持其中的一些,另外还有 get_registers、 get_keepers、 get_nodes、 get_fanins 和 get_fanouts 等。

这些 objects 的重要性在于它们在 timing constraints 中用来指代 FPGA design中的 logic elements 。它们也可以在 Tcl scripts 中用于获取信息,例如 clock的频率。每个 FPGA 工具都有自己的 API ,用于访问 Tcl scripts中的这些 objects 。例如,可以在 Vivado 中使用此 Tcl 命令来获取 clock period:

> get_property PERIOD [get_clocks clk]
4.000

在 Quartus中也一样:

> get_clock_info -period [get_clocks clk]
4.000

尽管存在这些差异,但大多数 FPGA 工具都同意 SDC 格式中 timing constraints 的语法和含义。每个工具支持的 API 的信息通常在 user guides 中找到,这些信息的标题与 Tcl scripting 或 Timing Closure相关。

除非另有说明,否则此页面上的示例基于 Vivado。

Tcl的一些注意事项

Tcl 是一种古老的语言,但是因为它在 logic design领域已经很成熟,所以预计这种语言不会很快消失。幸运的是,即使不太了解,也可以使用 Tcl 做一些有用的事情。

我已经简要提到过的第一件事是 square brackets (“[”和“]”)。在 Tcl中,这意味着执行 square brackets中的命令,并将结果放在它们的位置。对于熟悉 shell scripting 或 Perl的人来说,这与 backticks相同。例如,在此命令中, get_port 替换为名称为“clk”的 port object :

create_clock -period 4.000 -name clk [get_ports clk]

同样,这可以这样写:

set the_clk_port [get_ports clk]
create_clock -period 4.000 -name clk $the_clk_port

如第二个示例所示,使用“set”命令定义变量并为其赋值。要访问变量的值,使用 dollar sign ($)。再次类似于 shell script 和 Perl。

关于 curly braces (“{”和“}”),情况不同: 像其他几种语言一样,它们的含义在很大程度上取决于它们的上下文。在 Tcl中, curly braces 的意想不到的含义之一是封闭的 string 应保持不变。换句话说,应该没有替代品, whitespaces 应该被视为任何 character。例如,同一个 timing constraint 可以这样写:

create_clock -period {4.000} -name {clk} [get_ports {clk}]

在这个例子中, curly braces 是完全不需要的,这个命令的意思和前面完全一样。不幸的是,不必要的 curly braces 很常见,而且通常它们没有任何意义,就像这个例子中一样。

使用 Tcl console的提示

经常发生的情况是,找到的 objects 数量很多,这使得难以读取搜索命令的输出。这可以通过简单的 Tcl 命令解决。具体如何执行此操作取决于使用哪种工具。对于 Vivado,此命令打印出 design中的所有 cells 。每个 cell 都打印在单独的一行中,所以即使有很多 cells,输出仍然是可读的:

> join [get_cells -hierarchical] \n
GND
VCC
bar__0_i_1
bar_reg_OBUF_inst
bar_reg__0
[ ... ]

“join”命令在 get_cell 生成的 array 中的每个元素之间放置一个 newline 。

使用 Quartus,可以通过以下方式实现相同的结果:

join [query_collection -all [get_cells -hierarchical] ] \n

或者更明智地使用 query_collection :

query_collection -all -report_format [get_cells -hierarchical]

查找特定元素

介绍了很久,终于到了说说真正有趣的地方的时候了。为了下面的例子,参考下面的 Verilog 代码,后面也会用到

module top(
    input clk,
    input foo,
    output reg bar_reg,
    output reg baz
);
    reg foo_reg;
    reg bar;
    reg baz_metaguard;
    wire pll_clk_8, pll_clk_6;

   clk_wiz_1 pll_i
   (.clk_in1(clk),
    .clk_out1(pll_clk_8),
    .clk_out2(pll_clk_6));

always @(posedge pll_clk_8)
  foo_reg <= foo;

always @(posedge pll_clk_6)
  begin
    bar <= !foo_reg;
    bar_reg <= bar;
  end

always @(posedge clk)
  begin
    baz_metaguard <= bar;
    baz <= baz_metaguard;
  end

如本页顶部所述, timing constraints 的精度取决于选择正确的 logic elements组。因此,有可能而且有必要缩小上述五个 get_* 命令的搜索结果。有几种方法可以做到这一点,但最常见的方法是基于 object的名称。最简单的 pattern 是找到一个具有我们正在寻找的确切名称的单个 object 。比如在 Vivado的 Tcl console中:

> get_ports clk
clk

同样,可以找到名称与特定 pattern匹配的所有 objects :

> get_pins pll_i/*
pll_i/clk_in1 pll_i/clk_out1 pll_i/clk_out2

请注意,这两个示例中的输出都是 objects。在 Vivado的 Tcl console中,为了方便,把这些 objects 的名字都打印出来了。

更重要的是,请注意每个 FPGA 工具相对于这些 search patterns的行为略有不同。此处的示例中使用了 Vivado 。同样的原则也适用于其他工具。

星号(“*”)是 wildcard character,可替代任意数量的 characters。问号(“?”)代替一个 character。这就像带有文件名的 wildcards 一样工作。

请注意,就像文件名一样, wildcards 与 hierarchy separator 不匹配(例如“/”与 Vivado 和“|”与 Quartus)。

hierarchical path 和文件目录之间也有相似之处: 搜索是针对 top-level hierarchy进行的,它类似于 file system的 root directory 。所以,例如:

> get_pins */clk_*
pll_i/clk_in1 pll_i/clk_out1 pll_i/clk_out2
> get_pins pll_i/clk_out?
pll_i/clk_out1 pll_i/clk_out2

需要指定每个 logic element 在 hierarchy 中的确切位置通常是一个重大缺点: 我们要找的 logic elements 往往在不同的位置。这是用“-hierarchical”解决的: 当此 flag 存在时,将在 hierarchy中的所有位置搜索 pattern 。

一般来说,不推荐使用 wildcards 找 logic elements 。唯一的例外是当 search pattern 非常简单或别无选择时。有一个单独的页面解释了如何使用 wildcards 和“-hierarchical”,并且还显示了此方法的局限性。

使用 -filter

使用基于 wildcards的 search patterns 有几个缺点。最显着的缺点是 hierarchy 必须精确定义或根本不定义。一个问题是通常希望将搜索限制为属于 sub-hierarchy的 objects 。然而,这对于 wildcards是不可能的,“-hierarchical”选项也不能解决这个问题。

由于这个原因和其他原因,定义 search patterns 的首选方法是使用 -filter 选项。此选件随 boolean expression一起提供。使用此选项时,仅保留此 expression 为 true 的搜索结果。

例如,

> get_pins */clk_*
pll_i/clk_in1 pll_i/clk_out1 pll_i/clk_out2
> get_pins */clk_* -filter {name =~ *2*}
pll_i/clk_out2

在此示例中,在通过 wildcard找到的每个 objects 上检查了名为“name”的 property 。仅当此 property 与“*2*”匹配时, object 才会保留在搜索结果中。换句话说,只有当 object 的名称中包含“2”时。

从实用的角度来看,这个例子并不有趣。使用“-hierarchical”时更有趣: 没有任何 search pattern,找到所有 objects 。换句话说,此命令在所有 hierarchies中找到所有 pins :

get_pins -hierarchical

从这一点来看,可以缩小 -filter的搜索结果范围:

> get_pins -hierarchical -filter {name =~ pll_i/*/*out1 }
pll_i/inst/clk_out1
> get_pins -hierarchical -filter {name =~ pll_i/*out1 }
pll_i/clk_out1 pll_i/inst/clk_out1
> get_pins -hierarchical -filter {name =~ *out1 }
pll_i/clk_out1 pll_i/inst/clk_out1

请注意, curly braces (“{”和“}”)的含义只是它们内部的部分不应被 Tcl interpreter修改。

另请注意, search pattern 属于 -filter 选项,并且表现不同: 它将“name”视为 object的 property 。因此,所有 characters 都被平等对待: “/”没有特殊意义。 “/”是 hierarchy separator并不重要。所有字符,包括“/”,都可以与 wildcard (“*”)匹配。同样,所有字符,包括“/”,都可以在 search pattern中使用。如果没有 -filter,这当然不是真的。

任何 character 都可以与“*”匹配的事实使 -filter 成为更强大的工具,但这种优势也可能导致错误: 很容易忘记,无辜的“*”可能会意外地与“pll_i/clk_”和“pll_i/inst/clk_”匹配,如上例所示。

正确使用此功能是为了在 hierarchy的某处找到具有已知名称的 logic element :

> get_pins -hierarchical -filter {name =~ */clkout2_buf/O }
pll_i/inst/clkout2_buf/O

如果我们确定 design 中只有一个名为“clkout2_buf”的 cell ,那么这是正确的。通过使用此命令,始终可以找到此 cell 的 output pin ,即使包含此 logic element 的 module 在 project的 hierarchy中移动。在现实场景中,最好选择一个比“clkout2_buf”更独特的名称。

同样的方法适用于 get_cells 和 get_nets,例如:

> get_cells -hierarchical -filter {name =~ */clk*_buf}
pll_i/inst/clkf_buf pll_i/inst/clkout1_buf pll_i/inst/clkout2_buf

但 -filter 适用于所有 properties,而不仅仅是名称。因此,此命令会查找名称中包含“bar”的所有 registers :

get_cells -hier -filter {primitive_type =~ register.*.* && name =~ *bar*}

请注意“&&”运算符,它表示 logical AND (就像在 Verilog 和 C中一样)。

在此命令中,“primitive_type”和“name”只是 properties的名称。 “=~”运算符进行比较,并允许 wildcards。

回想一下,可以使用“report_property”命令(在 Vivado中)列出 object 的属性。

所以 -filter 可以灵活使用。遗憾的是,它并非适用于所有 FPGA 工具。

请注意, Vivado 有一个名为 filter的命令,它执行与 -filter相同的操作。所以下面两个命令是等价的:

set result [get_cells -hierarchical -filter {name =~ *_reg}]
set result [filter [get_cells -hierarchical] {name =~ *_reg}]

当 objects 列表存储在变量中时,第二种格式很有用。所以上面的两个命令也等同于:

set all_cells [get_cells -hierarchical]
set result [filter $all_cells {name =~ *_reg}]

使用 -regex

熟悉 regular expressions 的人可能会想使用这个选项。这样做通常不是一个好主意,主要是因为这会让其他人更难理解 timing constraints 。支持 regular expressions 的 FPGA 工具通常也支持 -filter 选项,因此使用 -filter几乎总是更好。

回想一下,通常的 search pattern 的主要问题是 hierarchy separator 与 wildcard不匹配。

所以 -regexp 可以解决这个问题,如以下几个示例所示:

> get_pins -hierarchical -regexp {.+/clk_out[123]}
pll_i/clk_out1 pll_i/clk_out2 pll_i/inst/clk_out1 pll_i/inst/clk_out2
> get_pins {pll_i/[^/]+/clk[^/]+} -hierarchical -regexp
pll_i/inst/clk_in1 pll_i/inst/clk_out1 pll_i/inst/clk_out2

第一个命令演示了“.”。与任何 character匹配。这包括“/”( hierarchy separator)。

第二个命令演示了如何使用“[^/]+”来匹配除 hierarchy separator之外的任何内容。因此,这允许控制搜索结果的 hierarchy 的确切深度。此命令还表明 search pattern 不是 -regexp的 argument ,而是 -regexp 更改了 search pattern的语法。

请注意, regular expression 必须与 object的全名匹配。换句话说,这些工具隐含地在 regular expression的开头添加了一个“^”,并在末尾添加了一个“$”。

但是,如果有另一种方法可以达到相同的结果,请不要使用 -regex 。大多数其他 FPGA designers 将无法理解 search pattern。

使用 -of_object

-of_object 允许根据它们与其他 objects的关系查找 objects ,而不是根据它们的名称查找 objects 。在大多数情况下,“-of_objects”大致表示“连接到”。

例如,为了找到连接到 net的所有 pins :

> get_pins -of_objects [get_nets bar]
bar_reg_reg/D baz_metaguard_reg/D bar_reg__0/Q

或者 cell的所有 pins :

> get_pins -of_objects [get_cells bar_reg_reg]
bar_reg_reg/Q bar_reg_reg/C bar_reg_reg/CE bar_reg_reg/D bar_reg_reg/R

或作为 clock起源的 pin :

> get_pins -of_objects [get_clocks clk_out1_clk_wiz_1]
pll_i/inst/mmcme3_adv_inst/CLKOUT0

同样的方法也适用于查找 nets。例如,哪些 nets 连接到特定的 pin?

> get_nets -of_objects [get_pins bar_reg_reg/C]
pll_clk_6

或者,哪些 nets 连接到相关的 cell?

> get_nets -of_objects [get_cells bar_reg_reg]
bar_reg_OBUF pll_clk_6 <const1> bar <const0>

同样,可以通过这种方式搜索 cells 。例如,哪些 cells 连接到 @pll_clk_6?

> get_cells -of_objects [get_nets pll_clk_6]
bar_reg__0 bar_reg_reg pll_i

请注意,“pll_i”是 Clock Wizard的 IP的 instantiation name 。这可能不是想要的搜索结果。那么也许将搜索结果缩小到 flip-flops?

> get_cells -of_objects [get_nets pll_clk_6] -filter {primitive_type =~ register.*.*}
bar_reg__0 bar_reg_reg

到目前为止,上面的 -of_objects 示例演示了 pins、 nets 和 cells 如何相互引用。但也可以使用此选项找到 clock objects :

> get_clocks -of_objects [get_nets pll_clk_6]
clk_out2_clk_wiz_1
> get_clocks -of_objects [get_cells bar_reg_reg]
clk_out2_clk_wiz_1
> get_clocks -of_objects [get_pins bar_reg_reg/C]
clk_out2_clk_wiz_1

这三个命令显示了如何根据与其连接的 logic element 找到 clock 。请注意,当此 logic element 为 cell时,可能会有多个结果。例如:

> get_clocks -of_objects [get_cells pll_i]
clk clk_out1_clk_wiz_1 clk_out2_clk_wiz_1

回想一下“pll_i”是一个 IP,所以它的 pins 就是这个 module的 ports :

> get_pins -of_objects [get_cells pll_i]
pll_i/clk_in1 pll_i/clk_out1 pll_i/clk_out2

所以为了找到具体的 clock, get_pins 比较安全:

> get_clocks -of_objects [get_pins pll_i/clk_out2]
clk_out2_clk_wiz_1

但当然,最好在外部 I/O port 上找到 clock object :

> get_clocks -of_objects [get_ports clk]
clk

或者,使用更短的命令,这是等效的:

> get_clocks [get_ports clk]
clk

说到 get_ports,它也适用于 -of_objects。请参阅有关特定于 get_ports的可能性的文档。与其他命令类似的可能性是毫无意义的,例如:

> get_ports -of_objects [get_nets clk]
clk

总之, -of_objects 是选择特定 logic elements的绝佳方法。尤其是因为很难根据 object的名称进行搜索,这是下面的下一个主题。

不幸的是,许多 FPGA 工具不支持 -of_objects,这让我们别无选择,只能依赖不太可靠的方法。

按名称搜索的问题

如上所述, timing constraints 的准确性取决于找到 logic elements的准确性。至少其中一些搜索结果取决于 object的名称。让我们看看这会如何引起麻烦。

例如,“get_ports clk”用于在 clock object 和特定 I/O pin上存在的 signal 之间建立连接。此 I/O pin 由名为“clk”的 port object 表示:

create_clock -period 4.000 -name clk [get_ports clk]

但是为什么这款 port object 会得到“clk”这个名字呢?答案与 synthesizer 如何创建 netlist有关: Verilog 代码中 top-level port 的名称也被选为 netlist的 top-level port 的名称。这是一个显而易见的选择,因此任何合适的 synthesizer 都会做同样的事情。

但是 cells的名称呢?我们以上面 Verilog 代码中名称为“foo_reg”的 register 为例。代表这个 register的 flip-flop的 cell object 叫什么名字? Vivado的 synthesizer 为这个 object选择了“foo_reg_reg”这个名字。所以很明显,这款 synthesizer 在 Verilog 的代号中倾向于在名字上加一个“_reg”后缀。这听起来像是一个可以依赖的规则。但另一个 synthesizer 可能会做一些不同的事情。

但是名为“bar”的 register 呢?相关的 cell object 的名称应该是“bar_reg”,但 synthesizer 却做出了不同的选择: “bar_reg__0”。这是因为在 Verilog 代码中有一个名为“bar_reg”的 register 。因此,为避免名称冲突, synthesizer 选择了一个略有不同的名称: 它添加了“_reg__0”而不仅仅是“_reg”。这个简单的例子演示了依赖 objects名称的问题。

更糟糕的是,假设 timing constraint 是在名称为“bar_reg”的 register 被添加到 Verilog 代码之前编写的。在这种情况下,与 @bar 相关的 cell object 照常获得名称“bar_reg”。所以 timing constraint 会为 object使用这个名称。在后期,名称为“bar_reg”的 register 被添加到 design中。结果,请求的 cell object 的名称从“bar_reg”更改为“bar_reg__0”。靠着“bar_reg”这个名字的 timing constraint突然不对了。运气好的话,这些工具会发布关于此的 warning 。

使用 objects 的名称可能出错还有其他可能的原因。例如,如果 fan-out 超出限制,这些工具可能会自动复制 register 。发生这种情况时,额外的 register 可能不会包含在 timing constraint中,因为新的 register的名称与 search pattern不匹配。

更严重的问题是 logic elements 意外包含在 timing constraints 中。例如,属于 IP blocks的 logic elements 可能会发生这种情况。因为我们无法控制这些 logic elements的名称,所以这些名称可能会意外地与 timing constraint中的 pattern 匹配。

timing constraints 中意外包含 logic elements 也可能是懒惰的结果: timing constraints 通常是在为 logic design添加新功能的同时编写的。如果 search pattern 是通过反复试验编写的,则可能不会考虑未来 logic elements 的名称。因此,当添加新的 logic 时,其某些 logic elements 的名称可能会无意中与现有的 timing constraints匹配。

如何避免 timing constraints出错

第一个也是最重要的规则是,仅仅测试哪个 logic elements 与 timing constraint的 search pattern 匹配是不够的。即使您列出了这些 logic elements 的完整列表并仔细查看了此列表,也不能保证以后会添加有关 logic 的任何内容。如果 objects 的名称被 synthesizer 更改或在 implementation的另一个阶段,这样的审查也不能保证 timing constraints 将继续按预期工作。

因此,将 search patterns 视为数学表达式非常重要: 这些 search patterns 现在按预期工作是不够的。如果它们没有按预期工作,仅仅进行小的修复是不够的。相反,必须有一个逻辑解释来解释为什么 search pattern 是正确的,以及为什么它很可能随着时间的推移保持正确。

同样重要的是, search patterns 依赖于不太可能改变的东西。例如,工具不会更改 Verilog 代码中 instantiations 的名称。这适用于 modules、 IPs 和 primitives的 instantiations 。因此,当 hierarchical path 仅包含 Verilog 代码中编写的 instantiations 名称时,可以安全地依赖它。使用在 IP block内部创建的名称并不安全。为了解释这一点,让我们看一下这个命令:

get_clocks -of_object [get_pins pll_i/inst/clkout1_buf/O]

这是参考分配 clock的 global clock buffer 的 output pin 得到 clock object 的方法。问题是我们是否可以确定这会长期有效。

这种方法的问题是“inst”和“clkout1_buf”是 Clocking Wizard IP内部制作的 instantiations 的名称。尽管这些名称不太可能更改,但不能保证它们不会更改。

一种可能的解决方案是找到实现 IP的 Verilog 代码,并将此 Verilog 直接包含在项目中。这确保将来不会发生任何变化。

另一种方法是查看 Verilog中的 IP 的 instantiation 。回想一下,它是:

clk_wiz_1 pll_i
   (.clk_in1(clk),
    .clk_out1(pll_clk_8),
    .clk_out2(pll_clk_6));

因为这是一个 black box的 instantiation ,所以每个 ports 在 netlist中都有一个 pin 。名称不能更改,因为这是识别 IP 的 ports 的方式。因此保证名称为“pll_i/clk_out1”的 pin 与 @pll_clk_8建立连接。所以这是获取 clock object的安全方法:

get_clocks -of_object [get_pins pll_i/clk_out1]

请注意,如果 clk_wiz_1 只是项目中的另一个 Verilog module ,这可能不起作用: 在这种情况下,不会代表此 module的 instantiation 创建 pins (因为 synthesizer 通常通过合并 nets来实现 ports 的连接)。一种可能的解决方案是使用出现在 hierarchy较低级别的名称。

因此,有几种名称可以依赖:

大失败更好

最糟糕的情况是 timing constraint 几乎正确: 当只有少数 paths 不包括在内时。或者当只有少数 paths 被错误地包含在 timing constraint 中时。这种错误是最难发现的。

这也是短小精悍、数学风格的 timing constraints 更胜一筹的主要原因。如果每一小组 logic elements都有一个 timing constraint ,那么很容易在这长长的规则列表中犯错。

为了演示这个想法,让我们回到上面显示的用于查找 clock object的示例:

get_clocks -of_object [get_pins pll_i/inst/clkout1_buf/O]

上面说了,这个表达式的问题是,如果 pll_i 的 instantiation 移动到 hierarchy的另一个位置,就找不到 clock object 了。那会有多糟糕?

假设这个 clock object 像这样存储在 Tcl 变量中:

set my_clock [get_clocks -of_object [get_pins pll_i/inst/clkout1_buf/O]]

然后 $my_clock 用在很多 timing constraints里面,所以涉及到很多 paths 。在这种情况下,如果 $my_clock 由于 bug 突然不包含任何 clock object 其实问题不大: 这个错误很有可能很快就会被发现,因为很多事情都会出错。

但是如果 $my_clock 只用在一个 timing constraint上,而这个 constraint 是为了解决一个很少有什么效果的小问题,这是很糟糕的情况。很可能这个错误会被忽视。

综上所述: 一个写得很好的 timing constraint 要么完美地工作,要么根本不工作。


本页展示了如何准确选择 logic elements。在下一页中,这些知识用于定义选择性 timing constraints。

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (6f913017)