作者:Darren H. Chen
方向:Backend Flow / 后端实现流程 / EDA 工具工程 / Tcl 脚本工程
demo:LAY-BE-12_collection_property_filter
标签:Backend Flow、EDA、Tcl、Collection、Property、Filter、Design Object Model、后端实现
在前面的文章里,我们已经讨论了一个基础问题:Backend 工具真正理解设计之后,内部看到的不是 Verilog 文本,而是一组可被查询、可被筛选、可被操作的设计对象。
这些对象包括:
text
cell
net
pin
port
module
clock
site
row
shape
violation
property
但是,仅仅知道有这些对象还不够。真正进入工程阶段后,最关键的问题会变成:
text
如何从几十万、几百万甚至上千万个对象中,稳定地找到你要处理的那一小部分?
这就引出了 Backend Tcl 脚本里最重要的三类能力:
text
Collection
Property
Filter
它们看起来只是对象查询接口的一部分,但从底层架构看,它们决定的是:
工具数据库如何把"海量设计对象"转化成"可控工程操作集合"。
本文不讨论某个具体命令怎么背,而是从系统模型角度讲清楚:为什么 Collection / Property / Filter 是 Backend 脚本工程的核心能力。
一、对象查询为什么不能只靠名字匹配?
很多人最开始写 Tcl 脚本时,会习惯这样想:
text
我要找某个 cell,就按名字找;
我要找某个 net,就按名字找;
我要找某个 pin,就按名字找。
例如:
tcl
get_cells U123
get_nets clk
get_pins U123/A
这当然是必要能力,但远远不够。因为真实设计中,很多工程问题不是"找一个名字",而是"找一类对象"。
例如:
text
找所有未放置的 cell;
找所有 fixed macro;
找所有 clock net;
找所有 high fanout net;
找所有属于某个 hierarchy 的 instance;
找所有没有物理 shape 的 pin;
找所有 timing critical path 上的 cell;
找所有 power/ground port;
找所有某个 property 满足条件的对象。
这类问题如果只靠名字匹配,会很快失控。因为名字只是对象的一种外部标识,而 Backend 工程真正关心的是对象的状态、属性、关系和上下文。
所以,Backend Tcl 查询的核心不是字符串处理,而是:
在设计数据库中构造对象集合,并在集合上进行属性判断和关系筛选。
二、Collection 是什么?
可以把 Collection 理解成:
一组被工具数据库识别的对象引用。
它不是普通字符串列表。普通字符串列表只是文本:
text
U1 U2 U3
而 Collection 背后通常对应数据库对象:
text
cell object handle
net object handle
pin object handle
port object handle
这意味着 Collection 至少有三个重要特点。
1. 它有对象类型
一个 Collection 可能是:
text
cell collection
net collection
pin collection
port collection
shape collection
violation collection
不同类型的 Collection 能被不同命令消费。
2. 它有数据库身份
对象不只是名字。一个 cell 的名字可能显示为:
text
U123
但在工具内部,它还可能有:
text
object id
hierarchical path
master cell
location
orientation
fixed status
physical status
properties
Collection 保存的是对象引用,而不只是名字文本。
3. 它可以继续被操作
Collection 的价值不在于"查出来",而在于它可以继续作为后续命令的输入。
text
查询对象
↓
筛选对象
↓
读取属性
↓
生成报告
↓
修改状态
↓
再次查询
这就是 Backend Tcl 脚本和普通文本脚本的根本差异。
三、Property 是什么?
如果 Collection 回答的是:
text
有哪些对象?
那么 Property 回答的是:
text
这些对象是什么状态?
一个 cell 可能有属性:
text
name
ref_name
is_fixed
is_placed
location
orientation
area
is_macro
is_sequential
is_clock_cell
dont_touch
一个 net 可能有属性:
text
name
usage
fanout
is_clock
is_power
is_ground
total_capacitance
driver
loads
一个 pin 可能有属性:
text
name
direction
net
cell
is_clock_pin
capacitance
location
layer
Property 是对象模型的可观测层。没有 Property,Collection 只是一堆对象。有了 Property,脚本才知道这些对象有什么差异,哪些该被处理,哪些不该被处理。
四、Filter 是什么?
Filter 可以理解成:
基于对象属性和关系条件,对 Collection 进行选择。
例如:
text
从所有 cell 中筛出 macro;
从所有 net 中筛出 clock net;
从所有 pin 中筛出 input pin;
从所有 cell 中筛出未放置对象;
从所有 object 中筛出某个 property 为 true 的对象。
Filter 的本质是谓词判断:
text
对象是否满足某个条件?
用系统模型表示:
text
FilteredCollection = Filter(Collection, Predicate(Property, Relation, Context))
也就是说:
text
输入:一组对象
条件:属性 / 关系 / 上下文判断
输出:一组更小、更准确的对象
这就是 Collection / Property / Filter 的完整闭环。
五、对象查询流水线
Backend 脚本中的对象查询,通常不是单条命令,而是一条流水线。
Design Database
get_* 查询对象
Collection
get_property / list_property
Filter Predicate
Filtered Collection
Report / Check / Modify
这条流水线的核心思想是:
text
先得到对象集合;
再观察对象属性;
再根据条件筛选;
最后执行报告、检查或状态修改。
所以,一个成熟的 Backend 脚本,不应该是大量硬编码名字堆起来的文本,而应该是:
text
对象查询 + 属性判断 + 集合变换 + 阶段动作
六、为什么对象查询能力决定脚本工程上限?
因为 Backend 工程问题的规模通常不是单点问题。它处理的是大规模对象集合。
例如:
text
不是改一个 cell,而是处理某类 cell;
不是查一个 net,而是查一批 high fanout net;
不是看一个 pin,而是看某类 pin 的物理可达性;
不是读一条 timing path,而是分析一组 critical path 的共性。
如果查询能力弱,脚本就只能写成:
text
手工列名字
手工改路径
手工判断类型
手工排除例外
手工生成局部报告
这种脚本短期能用,但很难维护。
相反,如果 Collection / Property / Filter 能力强,脚本可以变成:
text
根据对象状态构造处理集合;
根据属性决定处理策略;
根据报告反馈更新筛选条件;
根据阶段上下文控制命令边界。
这里说的不是让工具替代工程师判断,而是让脚本从"文本拼接"升级为"数据库对象操作"。这就是脚本工程上限的来源。
七、名字、对象和属性三者不能混淆
Backend Tcl 中最常见的错误之一,就是把名字、对象和属性混在一起。
| 概念 | 本质 | 示例 | 风险 |
|---|---|---|---|
| name | 文本标识 | U123 |
可能重名、转义、层次路径变化 |
| object | 数据库对象 | cell handle | 依赖当前 session 和数据库状态 |
| property | 对象状态 | is_fixed |
需要对象类型和上下文支持 |
例如:
tcl
set cells [get_cells *]
这里 cells 不应该简单理解成字符串列表,而应该理解成 cell 对象集合。
再比如:
tcl
get_property $cells ref_name
这里读取的是对象属性,而不是从名字中解析字符串。
一旦把对象当字符串处理,很多脚本就会出现隐性问题:
text
hierarchy 分隔符处理错误;
bus name 转义错误;
对象已经不存在但名字仍在;
名字匹配过宽或过窄;
同名对象在不同 scope 下混淆。
所以,Backend 脚本的第一原则是:
能用对象集合解决的问题,不要退回到纯字符串处理。
八、Filter 的底层思想:把经验变成条件
工程师的经验通常是这样表达的:
text
这些 macro 不要动;
clock net 不应该当普通 signal net 处理;
fixed cell 不能随便移动;
critical path 上的 cell 要重点看;
power/ground net 不参与普通信号逻辑判断;
hierarchy A 下面的对象要单独报告。
这些经验如果停留在脑子里,只能靠人操作。如果写进脚本,就要变成 filter 条件:
text
is_macro == true
usage == clock
is_fixed == true
slack < threshold
usage == power || usage == ground
full_name matches hierarchy pattern
Filter 的真正价值是:
把工程经验转换成可执行的对象选择条件。
一个团队对 Flow 的理解越深入,往往不是脚本越长,而是 filter 条件越清楚、越可解释。
九、为什么 list_property 很重要?
很多人只知道 get_property,却忽略 list_property。
但从工程角度看,list_property 很关键。因为它回答的是:
text
某类对象到底有哪些可观察状态?
如果不知道有哪些 property,就无法写出稳定 filter。
例如你想筛选 fixed object,但不知道工具里属性叫:
text
is_fixed
fixed
place_status
status
这时就不能靠猜。
正确方式是:
text
先查询对象;
再列出属性;
再确认属性语义;
最后写 filter。
十、空集合意识与类型意识
对象查询要区分三种状态:
text
命令失败;
命令成功但集合为空;
命令成功且集合非空。
这三个状态不能混在一起。
例如,设计中没有 macro 时,查询 macro collection 为空,这不应该被当作错误。但如果 get_cells 命令本身失败,那就是另一类问题。
同时,不同对象类型的属性不同:
text
cell 有 ref_name;
net 有 fanout;
pin 有 direction;
port 有 input/output;
shape 有 layer;
violation 有 rule_name。
所以一个可靠脚本必须在关键入口检查对象类型,避免对 net 读取 cell 属性,对 pin 读取 net 属性。
十一、对象关系比单个对象更重要
Backend 设计数据库不是一堆孤立对象,而是一张关系图。
典型关系包括:
text
cell owns pin
pin connects net
net connects driver/load pins
cell instantiates lib cell
pin belongs to port or instance
object has physical location
object belongs to hierarchy
clock traverses timing graph
所以,Collection / Property / Filter 的高级用法,不只是筛对象,而是沿关系图走。
例如:
text
从 critical path 找 cell;
从 cell 找 input/output pins;
从 pin 找 net;
从 net 找 fanout;
从 fanout 找 downstream cell;
从 cell 找 placement location。
这时脚本处理的就不再是平面集合,而是对象关系网络。
十二、一个推荐的对象查询分层
为了让脚本可维护,可以把对象查询分为四层。
text
基础查询层
属性封装层
过滤条件层
业务动作层
1. 基础查询层
负责调用工具原生命令:
tcl
get_cells
get_nets
get_pins
get_ports
2. 属性封装层
负责统一读取属性:
tcl
get_object_name
get_object_type
get_object_property
3. 过滤条件层
负责定义选择规则:
tcl
is_macro_cell
is_clock_net
is_fixed_object
is_io_port
4. 业务动作层
负责真正做报告或检查:
tcl
report_macro_summary
check_unplaced_cells
report_clock_net_summary
这种分层可以避免脚本到处散落复杂 filter。
十三、一个简化的对象查询模板
下面是一个抽象模板,不绑定具体工具语法。
tcl
set all_cells [get_cells *]
set all_nets [get_nets *]
set all_ports [get_ports *]
list_property $all_cells
list_property $all_nets
list_property $all_ports
foreach_in_collection cell $all_cells {
set name [get_property $cell name]
set ref [get_property $cell ref_name]
puts "CELL $name REF $ref"
}
set macro_cells [filter_collection $all_cells "is_macro == true"]
if {[sizeof_collection $macro_cells] == 0} {
puts "No macro cells found. Skip macro report."
} else {
report_property $macro_cells
}
这个模板的重点不是具体命令名,而是流程:
text
查询
观察
筛选
检查空集合
再执行动作
十四、Demo 应该验证什么?
本篇对应的 demo 不需要做完整后端流程。它应该验证对象查询机制本身:
text
是否能读取 cell/net/pin/port;
是否能列出 property;
是否能对对象读取关键属性;
是否能处理空集合;
是否能输出对象关系报告;
是否能把查询结果写入 report。
建议输出:
text
object_inventory.rpt
cell_property_summary.rpt
net_property_summary.rpt
pin_property_summary.rpt
filter_result_summary.rpt
empty_collection_check.rpt
Demo 的重点不是做 placement,而是验证对象查询层是否可靠。
十五、总结
Collection 让脚本可以操作对象集合。
Property 让脚本可以观察对象状态。
Filter 让脚本可以把工程经验转换成对象选择条件。
三者组合起来,才形成真正可靠的对象查询能力。
它们解决的是:
text
从海量设计对象中,稳定、可解释、可复查地找到目标对象。
一个 Backend Flow 能不能长期维护,往往不取决于命令写得多不多,而取决于对象查询层是否清楚、稳定、可扩展。
结尾一句话
Backend 脚本真正成熟的标志,不是能执行很多命令,而是能准确回答:
text
我正在操作哪些对象?为什么是这些对象?它们的状态是什么?结果能不能被复查?
Collection / Property / Filter,就是回答这些问题的基础。