ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Innovus中如何一次性提取module下所有寄存器clock pin

Innovus中如何一次性提取module下所有寄存器clock pin 1. 为什么“捞全一个 module 的 reg clock pin”是个高频刚需做数字后端的人几乎都遇到过这种场景某个 module 的时序报告里 clock 路径看着不对劲或者 CTS 之后想核对某个子模块里所有寄存器的时钟引脚到底挂在哪根 clock net 上又或者做 clock gating 检查、功耗分析、DRC 前的 clock 树梳理时需要把这个 module 下所有寄存器flip-flop / latch的 clock pin 一次性列出来。手动在 GUI 里一个个点几十上百个寄存器点到手抽筋还容易漏。用get_pins盲捞写不对 pattern要么捞出一堆 data pin要么把别的 module 的 clock pin 也带进来。这个需求的核心其实就三件事定位 module 实例、筛选寄存器类型、精确匹配 clock pin。听起来简单但 Innovus 的get_db/get_pins体系里module、instance、cell、pin 这几个层级的关系如果不理清楚命令写出来就是“看着对、跑出来错”。我见过太多人卡在get_pins -of_objects和get_db混用上最后靠-filter硬凑效率极低。这篇文章就是把我自己反复用过、踩过坑、最后固化下来的一套方法完整拆开讲。不管你是刚接触 Innovus 数字后端的新手还是已经能跑 flow 但想提升 debug 效率的老手都能直接抄作业。核心工具就是get_db配合get_pins关键词围绕Innovus、module、reg、clock pin、get_db展开但我会把背后的数据模型、筛选逻辑、常见翻车点全部讲透让你以后遇到类似“捞全某类 pin”的需求都能自己推出来。先说清楚适用边界这套方法针对的是已经完成或部分完成综合、在 Innovus 里有明确 netlist 和 floorplan 的阶段。如果你的设计还在 RTL 阶段或者 module 层次已经被 flatten 掉那思路要调整后面我也会提。另外这里说的“reg clock pin”默认指时序单元sequential cell的 clock 输入引脚包括普通 DFF 的 CK、带 enable 的 ECK、latch 的 G 等具体 pin 名取决于标准单元库。2. Innovus 数据模型module、instance、cell、pin 到底怎么串2.1 四个层级的从属关系很多人命令写错根子在于没搞清 Innovus 数据库里这几个对象的关系。我用一个生活化的类比把设计想象成一栋大楼module是楼层图纸逻辑层次instance是实际盖出来的房间物理实例cell是房间的类型比如“标准客房”对应一个 DFF 单元pin就是房间上的门信号出入口。图纸和实际房间不是一一对应的——同一个 module 可能被实例化多次每次都是一个独立的 instance。具体到数据库对象Module逻辑层次定义get_db module能拿到。它描述“这个模块里有哪些子模块、哪些 instance、哪些 port”。Instancemodule 的一次具体实例化get_db inst能拿到。它挂在某个 parent module 下有唯一的 hierarchical name。Cell标准单元或宏单元的类型定义get_db cell能拿到。比如DFFRQ_X1这种。Pininstance 或 cell 上的引脚get_db pin或get_pins能拿到。instance pin 是实际连接的cell pin 是类型定义上的。关键点你要捞的是 instance pin不是 cell pin。因为 clock net 是连在具体 instance 的 CK 脚上的cell 定义里的 CK 只是一个抽象引脚。这个区别在写命令时直接决定你用get_pins还是get_db cell.pins。2.2 为什么不能直接 get_pins 一把梭新手最容易写的就是get_pins -hier *CK这条命令能跑但问题一大堆。第一*CK是通配不同库的 clock pin 名可能是CK、CKB、CP、G、GN你写不全就漏。第二-hier会把整个设计所有层次的 CK 都捞出来如果你只要某个 module 下的就多了一堆无关的。第三有些 data pin 名字里也带 CK比如某些复杂单元的 scan 相关引脚会误伤。第四也是最坑的——它不区分这个 pin 是不是真的 clock pin只按名字匹配。所以正确思路是先定位 module 下的所有寄存器 instance再从这些 instance 上筛出 clock pin。这样既不漏也不误伤而且逻辑清晰换任何库都能适配。2.3 get_db 与 get_pins 的分工get_db是直接查数据库返回的是数据库对象句柄速度快、属性全适合做层级遍历和类型筛选。get_pins更偏向“按名字/属性找 pin”语法友好但筛选能力弱一些。我的习惯是用 get_db 做 instance 级筛选用 get_pins 或 get_db pin 做最终 pin 级提取。两者配合既准确又高效。举个直观对比方法命令示例优点缺点纯 get_pinsget_pins -hier *CK写法简单易漏易误伤不区分层次get_db 筛 instget_db inst -filter ...精确、可组合条件需要理解属性名组合方案先 get_db inst 再取 pin准确、可复用命令稍长理解了这层后面的实操就顺了。3. 一次捞全的核心命令拆解与参数计算3.1 第一步锁定目标 module 下的所有 instance假设你要查的 module 叫u_core/u_alu先确认它在数据库里的层次名。用get_db module u_core/u_alu如果返回空说明层次名写错了或者这个 module 被 flatten 了。确认存在后捞它下面的所有 instanceset insts [get_db insts -of_objects [get_db module u_core/u_alu]]这里-of_objects是关键它表示“属于这个 module 的 instance”。注意默认只返回直接子 instance不含更深层次。如果你要递归所有层次需要加-hier或者用get_db insts -hier。但大多数场景下我们只关心这个 module 直接包含的寄存器因为子模块的时钟通常由上层统一处理混进来反而乱。提示get_db insts和get_db inst都能用复数形式更符合语义但 Innovus 里两者基本等价看个人习惯。3.2 第二步从 instance 筛出寄存器类型拿到 instance 列表后要判断哪些是寄存器。最可靠的方法不是看名字而是看它对应的 cell 是不是时序单元。Innovus 数据库里cell 有个属性叫is_sequential直接筛set reg_insts [get_db $insts -if {.cell.is_sequential true}]这行命令的意思是对$insts里每个 instance检查它的.cell.is_sequential属性是否为真。这是最稳的筛选方式不依赖命名规范也不怕库换名字。如果你用的库比较老is_sequential可能不生效那就退而求其次用 cell 名匹配常见寄存器前缀set reg_insts [get_db $insts -if {.cell.name ~ *DFF* || .cell.name ~ *LAT*}]但我要强调优先用 is_sequential。名字匹配是下策因为不同 foundry 的命名千奇百怪SDFF、DFFR、LATCH、DLAT都有你永远写不全。3.3 第三步提取 clock pin 并去重有了寄存器 instance 列表接下来取它们的 clock pin。这里有两种写法写法 A用get_pins -of_objectsset clk_pins [get_pins -of_objects $reg_insts -filter is_clock true]写法 B用get_db pin配合属性set clk_pins [get_db pins -of_objects $reg_insts -if {.is_clock true}]两种都能用但is_clock这个属性不是所有版本都支持。如果不支持就得回到按 pin 名匹配。这时候你需要先知道库里 clock pin 叫什么。查一个已知寄存器get_db pins -of_objects [lindex $reg_insts 0]看输出里哪个 pin 的direction是 input 且名字像 clock。常见的有CK、CP、CKB、G、GN、CLK。确认后写set clk_pins [get_db pins -of_objects $reg_insts -if {.name CK || .name CP || .name G}]这里有个细节pin 名匹配要用全名不要用通配。因为寄存器上可能同时有CK和CKN差分时钟通配*CK*会把两个都捞进来而你可能只想要其中一个。如果确实要差分对那就明确写两个条件。3.4 参数与性能考量当设计规模很大时比如几十万 instance上面这套命令如果直接对全设计跑会慢。优化点有两个第一尽量缩小 insts 范围。先定位到具体 module而不是从 top 开始筛。get_db module定位是 O(1) 级别的很快。第二避免在 -if 里做复杂字符串运算。.cell.is_sequential是布尔属性判断极快而.cell.name ~ *DFF*是正则匹配慢一个数量级。能用布尔就别用正则。第三如果只是临时查一次可以在 Innovus 命令行直接敲如果要反复用建议封装成 proc把 module 名作为参数传入后面我会给完整脚本。4. 完整实操流程从定位到输出一张干净的表4.1 实操现场记录我拿一个真实项目里的子模块举例module 名是u_top/u_dsp/u_mac。目标是列出这个 MAC 模块下所有寄存器的 clock pin并输出 instance 名、cell 名、pin 名、连接的 clock net。第一步确认 module 存在get_db module u_top/u_dsp/u_mac返回一个句柄说明存在。第二步捞直接子 instanceset insts [get_db insts -of_objects [get_db module u_top/u_dsp/u_mac]] puts Total insts: [llength $insts]输出Total insts: 342说明这个 module 下有 342 个 instance。第三步筛寄存器set reg_insts [get_db $insts -if {.cell.is_sequential true}] puts Reg insts: [llength $reg_insts]输出Reg insts: 128128 个寄存器合理。第四步取 clock pinset clk_pins [get_db pins -of_objects $reg_insts -if {.is_clock true}] puts Clock pins: [llength $clk_pins]输出Clock pins: 128每个寄存器一个 clock pin对上了。第五步输出详细信息foreach pin $clk_pins { set inst [get_db $pin .inst] set cell [get_db $inst .cell] set net [get_db $pin .net] puts [get_db $inst .name] | [get_db $cell .name] | [get_db $pin .name] | [get_db $net .name] }这样就能得到一张干净的表每行是一个寄存器的 clock pin 及其连接关系。4.2 输出结果示例实际输出类似InstanceCellPinNetu_mac/reg_a_reg[0]DFFRQ_X2CKclk_coreu_mac/reg_a_reg[1]DFFRQ_X2CKclk_coreu_mac/reg_b_regSDFFRQ_X1CKclk_coreu_mac/reg_c_regDLAT_X1Gclk_gated从这张表能一眼看出大部分寄存器挂clk_core有一个 latch 挂clk_gated。如果clk_gated是预期外的那就要查 clock gating 逻辑了。4.3 封装成可复用脚本每次都敲一遍太累我把它封装成一个 proc放在.innovus初始化脚本里proc get_reg_clk_pins { module_name } { set mod [get_db module $module_name] if { $mod } { puts ERROR: module $module_name not found return } set insts [get_db insts -of_objects $mod] set reg_insts [get_db $insts -if {.cell.is_sequential true}] if { [llength $reg_insts] 0 } { puts WARNING: no sequential insts in $module_name return } set clk_pins [get_db pins -of_objects $reg_insts -if {.is_clock true}] puts Module: $module_name puts Reg insts: [llength $reg_insts], Clock pins: [llength $clk_pins] foreach pin $clk_pins { set inst [get_db $pin .inst] set cell [get_db $inst .cell] set net [get_db $pin .net] puts [get_db $inst .name] | [get_db $cell .name] | [get_db $pin .name] | [get_db $net .name] } return $clk_pins }用法get_reg_clk_pins u_top/u_dsp/u_mac这个 proc 的好处是module 名作为参数想查哪个查哪个有错误提示返回 pin 列表方便后续继续处理比如统计每个 clock net 上的寄存器数量。4.4 进一步统计 clock net 分布拿到 pin 列表后按 net 分组统计array set net_count {} foreach pin $clk_pins { set net [get_db $pin .net] set net_name [get_db $net .name] if { [info exists net_count($net_name)] } { incr net_count($net_name) } else { set net_count($net_name) 1 } } foreach net_name [array names net_count] { puts $net_name : $net_count($net_name) regs }输出clk_core : 127 regs clk_gated : 1 regs这个统计在 clock tree 分析和功耗评估时特别有用。如果某个 clock net 上只挂了一个寄存器那很可能是 clock gating 的边界值得重点检查。5. 常见翻车点与排查速查表5.1 捞出来是空的怎么回事这是最高频的问题。按可能性排序第一module 名写错。Innovus 的层次名用/分隔不是.也不是:。而且大小写敏感。先用get_db module *关键词*模糊搜一下确认。第二module 被 flatten 了。如果综合或 floorplan 阶段做了 flatten逻辑层次就没了get_db module找不到。这时候只能从 instance 名反推用get_db insts -filter name ~ *u_mac*这种方式。第三is_sequential 属性不生效。某些老版本或特定库不支持这个属性返回全 false。换成 cell 名匹配。第四is_clock 属性不生效。同理换成 pin 名匹配。第五module 下确实没有寄存器。纯组合逻辑模块那结果为空是正常的。5.2 捞多了混进非目标 pin常见原因pin 名通配写太宽。比如*CK*会匹配到CK、CKN、CK_GATE等。解决方法是明确列出所有可能的 clock pin 名用精确匹配而不是~通配。另一个原因-hier用过头。如果你加了-hier子模块的寄存器也会被捞进来。确认你是否真的需要递归。5.3 性能太慢跑一次要几分钟大设计上get_db遍历几十万对象确实慢。优化手段缩小 module 范围别从 top 开始。用is_sequential布尔判断别用正则。如果只是查一次接受慢如果要反复查把结果缓存到文件下次直接读。避免在循环里反复调get_db能一次拿到的属性就一次拿。5.4 速查表现象可能原因排查命令解决结果为空module 名错get_db module *kw*修正层次名结果为空被 flattenget_db insts -filter name ~ *kw*改用 instance 名匹配结果为空is_sequential 失效get_db [lindex $insts 0] .cell.is_sequential改用 cell 名匹配结果为空is_clock 失效get_db pins -of_objects [lindex $reg_insts 0]改用 pin 名匹配结果偏多通配太宽检查~表达式改精确匹配结果偏多用了 -hier检查命令去掉 -hier跑得慢范围太大检查 module 层级缩小到具体子模块跑得慢正则匹配检查 -if 条件改布尔属性5.5 几个我踩过的坑坑一以为 get_pins 和 get_db pins 等价。实际上get_pins返回的是 pin 名列表字符串而get_db pins返回的是对象句柄。如果你后续要用.net这种属性必须用get_db pins。用get_pins拿到字符串后还得再get_db pin $name转一次多此一举。坑二在 -if 里用 .inst.name 而不是 .name。对于 pin 对象.name就是 pin 名.inst.name是它所属 instance 的名。别搞混。同理对于 instance 对象.name是 instance 名.cell.name是 cell 名。坑三忘了处理 bus 类型的寄存器。像reg_a_reg[0]、reg_a_reg[1]这种get_db insts会分别返回每个 bit不会合并。这是对的因为每个 bit 是独立 instance。但如果你按名字去重可能会误合并。别去重保持每个 bit 独立。坑四clock pin 上的 net 可能是空。如果寄存器还没连时钟.net返回空。这时候要检查 netlist 是否完整或者这个寄存器是不是被 tie 掉了。6. 延伸场景这套思路还能怎么用6.1 捞全某个 module 的所有 data pin同样的套路把is_clock换成direction in且排除 clock就能拿到所有数据输入 pin。或者直接按 pin 名匹配D、SI、SE等。6.2 统计每个 clock net 的 fanout前面已经演示了按 net 分组统计。进一步可以结合get_db net .fanout拿到官方 fanout 数和手动统计的对比验证一致性。6.3 检查 clock gating 覆盖率如果设计里用了 clock gating可以对比clk_core和clk_gated上的寄存器数量算出 gating 覆盖率。覆盖率低说明 gating 逻辑没插好功耗优化空间大。6.4 跨 module 汇总如果想把多个 module 的结果合并把 proc 改成接受 module 列表循环调用最后汇总输出。这在做全芯片 clock 树审查时很有用。6.5 导出到文件供后续分析把输出重定向到文件set fp [open reg_clk_pins.rpt w] puts $fp Instance | Cell | Pin | Net foreach pin $clk_pins { set inst [get_db $pin .inst] set cell [get_db $inst .cell] set net [get_db $pin .net] puts $fp [get_db $inst .name] | [get_db $cell .name] | [get_db $pin .name] | [get_db $net .name] } close $fp这个报告可以直接丢给前端或功耗团队比截图强多了。6.6 和时序报告交叉验证拿到 clock pin 列表后可以对照report_timing里的 clock 路径确认每个寄存器的时钟来源是否和预期一致。如果某个寄存器的 clock pin 连的 net 和时序报告里的 clock 名对不上那说明 netlist 或约束有问题。7. 一些个人经验和收尾建议这套方法我在多个 28nm、16nm、7nm 项目上都用过从几万 instance 的小模块到几百万 instance 的全芯片逻辑都一样只是性能调优的力度不同。最核心的体会是别跟通配符较劲用数据库属性做筛选。通配符看着快实际是给自己挖坑尤其是库换版本、pin 名微调的时候通配符写的脚本全废而基于is_sequential、is_clock的脚本纹丝不动。另一个经验是把常用查询封装成 proc放在初始化脚本里。我自己的.innovus里有一整套这样的工具函数查 clock pin、查 data pin、查 tie cell、查 isolation cell都是同一个套路。用熟了之后debug 效率至少翻倍。最后提醒一点Innovus 版本差异确实存在is_sequential和is_clock这两个属性在较新版本里支持得很好老版本可能要用get_db cell .is_seq或类似变体。遇到属性不生效别慌先用get_db [lindex $objs 0]把对象的所有属性 dump 出来看一眼属性名一目了然。这个 dump 习惯是我从入行到现在一直保留的推荐你也养成。
返回列表