这一节主要介绍 objects 和 collection 相关知识
Verilog 视角下的Design objects
如图,Verilog 的Design objects分类主要有
- Design(设计):代表顶层模块,例如这里的
TOP
。 - Port(端口):模块的输入输出接口,用于连接外部信号。例如输入端口
input A, B, C, D, CLK;
输出端口如output [1:0] OUT1;
- Clock(时钟):特殊信号,通常用于同步模块行为。例如这里的
CLK
是时钟信号。 - Net(网络):于连接模块内部信号的路径。例如图中
wire
定义了网络信号INV1, INV0, bus1, bus0
。 - Cell(单元):描述模块内部具体逻辑的实例化单元。例如
ENCODE U1
和INV U2, U3
是被实例化的单元。 - Pin(引脚):单元的连接点,用于接收或输出信号。例如
U3(.A(BUS1), .Z(INV1));
中A
和Z
是引脚。
下面这张图可以更清晰的看出几种design objects。注意这里clk,对于最右边的clk来说它是top的port,对于图中连线上的clk来说它是net,对于regfile模块的clk来说它是pin
在这里介绍一下Ports 和 Pins 的区别,Ports(端口)和 Pins(引脚)是描述模块输入输出的关键概念,但两者的定义和作用是相对的,与设计层次相关。
Ports(端口) 是current design的输入和输出。当当前设计被作为子模块实例化到更高层次的设计中时,这些端口将变成引脚。如图,在模块 MID
中,MI
是一个端口,但在 TOP
中,MI
被视为 U8/MI
的引脚。
Pins(引脚)是current design中任何被实例化子模块的输入和输出。如果该子模块被提升为当前设计层次的顶层模块,其引脚会被视为端口。如图,在模块 BOT
中,BO
是引脚,但在 MID
设计中,它被视为 MID
的端口。
相同名称对象的歧义处理
在设计中,可能会出现多个对象(如端口和网络)使用相同的名称的情况。这种情况可能导致工具在操作时出现歧义,尤其是在分配负载或进行约束设置时。
如图,名为 SUM
的对象既可以表示模块的输出端口,也可以表示连接该端口的网络(net)。使用命令 set_load 5 SUM
时,工具需要知道该负载是应用到端口 SUM
还是网络 SUM
。加载位置的不同会造成不同的影响。若负载设定到端口,会影响整体模块的输入输出负载估计。若负载设定到网络,会直接影响相关连接的网络驱动能力和时序。当DC检测到相同名称的对象时,默认情况下会优先选择端口作为操作目标。
为避免歧义,我们要在命令中使用get_*
命令来明确的对象类型(如 get_ports
或 get_nets
)。get_*
命令会在current_design、DC memory或库中查找并返回匹配的对象。它既可以独立使用,也可以嵌入到其他命令中,以下是一些get_*
命令的用法。
- 直接返回对象列表,例如
get_ports
、get_nets
。 - 嵌入到命令中,例如
set_load 5 [get_nets SUM]
。 - 支持
?
和 `` 通配符,用于匹配多个对象。例如get_ports addr_bus*
可以匹配所有以addr_bus
开头的端口。get_ports Y??M Z*
可以匹配格式为Y任意两个字符M
的端口,以及所有以Z
开头的端口。 get_*
命令会返回与匹配条件一致的对象集合。如果没有匹配成功的对象,则返回空集合(empty collection
)。
与get_*
****命令作用类似的是all_*
命令,all_*
命令也用于获取当前设计中不同类型的对象,例如:
- 命令:
all_inputs
用于获取当前设计中所有输入端口。 - 命令:
all_outputs
用于获取当前设计中所有输出端口和输入/输出端口。 - 命令:
all_clocks
用于获取当前设计及其层级中定义的所有时钟信号。 - 命令:
all_registers
用于获取当前设计层级中定义的所有寄存器单元。
两者的区别如下
特性 | get_* 命令 | all_* 命令 |
---|---|---|
灵活性 | 支持筛选、模糊匹配,适合特定对象操作 | 获取所有对象,无筛选功能 |
使用范围 | 需要精确选择设计中某些对象 | 快速获取某类对象的完整列表 |
返回值 | 返回匹配的对象集合(可以为空) | 返回所有符合类型的对象(不能为空) |
嵌套支持 | 可嵌套在其他命令中使用 | 通常单独使用 |
举例
remove_from_collection [all_inputs] [get_ports CLK]
all_inputs
: 获取设计中所有的输入端口,返回一个集合。get_ports CLK
: 精确获取名称为CLK
的端口(无论它是输入、输出还是双向端口),返回一个单独的对象。remove_from_collection
: 从第一个集合(即所有输入端口all_inputs
)中移除第二个集合中的对象(即CLK
端口)。
返回结果
- 返回的是从
all_inputs
集合中移除了CLK
端口之后剩余的输入端口集合。
Library objects
Library objects有File Name (文件名)、Library Name (库名)、Library Cell (库单元)。
这里Library Cell里面的具体内容已经在上一节学过,不再赘述。
Obejcts and attributes
为了追踪电路的功能与时序,DC 为每种对象附加了多种属性。例如:
Ports(端口)
direction
:端口的方向(输入/输出/双向)。driving_cell_rise
:驱动单元的上升时间。load
:端口的负载值。max_capacitance
:端口的最大允许电容。- 其他属性
Cells(单元)
dont_touch
:指示该单元是否不可修改。is_hierarchical
:单元是否是层次化模块。is_mapped
:单元是否已映射到库中。is_sequential
:单元是否为时序逻辑单元。- 其他属性
访问 Synopsys 数据库
在 Synopsys 的 DC-TCL 中,访问 DC objects是通过collection实现的
get_*
或 all_*
命令
- 创建集合:这些命令根据查询条件生成一个对象集合,同时包括它们的属性。
- 显示内容:命令会将集合的内容回显到屏幕上,便于检查。
- 返回集合句柄:命令返回的是集合的“句柄”(作用类似于指针),而非实际的对象列表。
TCL 示例
-
定义集合变量:将与
P
匹配的所有端口存储为集合,并赋给变量foo
set foo [get_ports P]
-
显示集合内容:使用
echo
命令可以查看集合的内容,输出的内容为集合句柄echo $foo
访问和操作collection
创建集合:
set foo [get_ports p*]
- 将所有以
p
开头的端口加入集合foo
。 - 集合内容示例:
{pclk pframe_n pidsel pad[31] ...}
查询集合大小:
sizeof_collection $foo
- 返回集合
foo
的大小,例如:50。
查看集合内容:
query_objects $foo
- 列出集合中所有对象的详细信息。
添加元素到集合:
set pci_ports [add_to_collection $pci_ports [get_ports CTRL*]]
- 原集合
pci_ports
中包含{DATA[0] DATA[1] DATA[2]}
。 - 新增符合条件
CTRL*
的端口后,集合内容变为{DATA[0] DATA[1] DATA[2] CTRLA CTRLB}
。
从集合中移除元素:
set all_inputs_except_clk \\
[remove_from_collection [all_inputs] [get_ports CLK]]
- 从所有输入端口中移除与
CLK
匹配的端口,生成新的集合。
Filter Collections
在 DC 中,可以通过 filter_collection
和 -filter
选项对集合进行过滤,提取特定的设计对象。
示例
获取名称以 AN
开头的寄存器:
filter_collection [get_cells *] "reg_name =~ AN*"
获取未映射的单元:
filter_collection [get_cells *] "is_mapped != true
获取 dont_touch
属性为 true
的单元:
get_cells * -filter "dont_touch == true"
获取周期小于 10 的时钟:
set fastclks [get_clocks * -filter "period < 10"]
支持的关系操作符
- 等于:
==
- 不等于:
!=
- 大于/小于:
>
,<
- 大于等于/小于等于:
>=
,<=
- 正则匹配:
=~
- 非匹配:
!~
遍历集合(Iterate Over a Collection)
在 DC 脚本中,可以使用 foreach_in_collection
命令对集合内的对象逐一进行操作。
foreach_in_collection <变量名> <集合> {
<操作>
}
<变量名>
:表示当前遍历到的对象。<集合>
:通过get_*
或filter_collection
创建的对象集合。<操作>
:在每次循环中执行的具体命令。- 示例:输出设计中所有具有层次结构的单元实例名称。
foreach_in_collection cell [get_cells -hier * -filter "is_hierarchical == true"] {
echo "Instance [get_object_name $cell] is hierarchical"
}
-
get_cells -hier *
:获取所有层次化单元。 -
filter "is_hierarchical == true"
:筛选出is_hierarchical
属性为true
的单元。 -
get_object_name $cell
:获取当前单元的名称。 -
echo
:输出结果。 -
输出结果:
Instance I_Ablock is hierarchical Instance I_CONTROL is hierarchical Instance I_Bblock is hierarchical ...
Collection VS TCL List
List往往用来存放用户定义的,Collection是DC自己提取出来的东西
Collection 用于访问数据库中的objects,例如设计中的端口、单元、时钟等。访问 objects 时,Collections 相比普通 TCL Lists 更加节省内存。直接与 DC 数据库集成。
get_*
:如get_ports
、get_cells
,用于创建 Collection。foreach_in_collection
:遍历 Collection。filter_collection
:从 Collection 中筛选符合条件的对象。
TCL List 用于存储用户定义的数据,可以是任意类型的数据集合。
- 通用结构:与 TCL 的其他脚本逻辑兼容。
- 不直接与 DC 数据库交互。
foreach
:用于遍历普通的 List。