4、Synopsys Tcl扩展(STCL):了解Synopsys对Tcl的扩展,包括get_*、set_*、report_*等命令族。

好,咱们进入第四讲。这一讲咱们聊聊Synopsys对Tcl的扩展,也就是STCL。

说实话,我刚入行那会儿,觉得Tcl就是个脚本语言,没啥特别的。直到我真正开始用Synopsys工具做项目,才发现——嗯,事情没那么简单。工具里那些get_*set_*report_*命令,才是真正的生产力。

你想想看,一个芯片设计项目,动辄几百万门、上千万条线。你手动一条条去查?不现实。这时候,STCL就是你的瑞士军刀。

4.1 什么是STCL?

STCL,全称Synopsys Tcl Command Language。说白了,就是Synopsys在标准Tcl基础上,加了一大堆专门用于芯片设计自动化操作的命令。

我个人的理解是:标准Tcl给了你语法和流程控制,而STCL给了你操作芯片设计对象的能力。比如,你想知道设计中所有时钟的名字,标准Tcl做不到,但STCL的get_clocks可以。

核心思想:STCL = 标准Tcl + 芯片设计领域专用命令

4.2 get_* 命令族:查询设计对象

get_*命令族,是我用得最多的。几乎每个脚本的开头,都会有一堆get_*

它们的作用很简单:从当前设计中,找出你感兴趣的对象。

4.2.1 常用get_*命令一览

命令 作用 返回对象
get_cells 获取标准单元或模块 cell对象集合
get_nets 获取连线 net对象集合
get_pins 获取端口/引脚 pin对象集合
get_clocks 获取时钟定义 clock对象集合
get_ports 获取顶层端口 port对象集合
get_libs 获取库文件 library对象集合
get_designs 获取当前设计 design对象

4.2.2 使用技巧:通配符与过滤

我在项目中遇到过一个问题:设计里有几千个寄存器,我只想找名字里带"ctrl"的那几个。怎么办?

用通配符!

# 获取所有名字包含"ctrl"的cell
set ctrl_cells [get_cells *ctrl*]

# 获取所有以"U"开头的net
set u_nets [get_nets U*]

# 获取所有时钟域为"clk_core"的寄存器
get_cells -hierarchical -filter "ref_name =~ *DFF* && clock_pin == clk_core"

这里有个坑,我提醒一下:-filter选项的语法,跟Tcl的string match很像,但又不完全一样。我曾经在这里栽过跟头——写了个==,结果死活匹配不上。后来才发现,字符串匹配要用=~,精确匹配才用==

注意:-filter中的通配符是*?,不是正则表达式。别把Perl的习惯带进来。

4.3 set_* 命令族:设置属性与约束

set_*命令族,是咱们给设计“下指令”的工具。你想让某个路径走快一点?想给某个端口设个负载?都得靠它们。

4.3.1 核心set_*命令

命令 典型用途
set_driving_cell 设置输入端口的驱动单元
set_load 设置输出端口的负载电容
set_input_delay 设置输入延迟约束
set_output_delay 设置输出延迟约束
set_clock_uncertainty 设置时钟不确定性
set_false_path 设置伪路径(不分析时序)
set_multicycle_path 设置多周期路径

4.3.2 实战:一个完整的约束片段

我习惯把约束脚本分成几个块。下面这个例子,是我在一个28nm项目里用过的模板:

# 1. 时钟定义
create_clock -name clk_sys -period 10 [get_ports clk]
set_clock_uncertainty -setup 0.2 [get_clocks clk_sys]

# 2. 输入延迟
set_input_delay -clock clk_sys -max 3.0 [get_ports data_in*]
set_input_delay -clock clk_sys -min 1.0 [get_ports data_in*]

# 3. 输出延迟
set_output_delay -clock clk_sys -max 4.0 [get_ports data_out*]
set_output_delay -clock clk_sys -min 0.5 [get_ports data_out*]

# 4. 驱动与负载
set_driving_cell -lib_cell INV_X1 [get_ports data_in*]
set_load -pin_load 0.05 [get_ports data_out*]

# 5. 例外路径
set_false_path -from [get_clocks clk_sys] -to [get_clocks clk_test]

嗯,这里要注意:set_input_delayset_output_delay-max-min,分别对应建立时间和保持时间。我见过有人把这两个搞反了,结果时序分析全错。

小技巧:写约束时,先用report_*看看当前设计的状态,再动手设。比如先report_clocks看看时钟有没有创建成功。

4.4 report_* 命令族:查看结果与调试

report_*命令族,是咱们的眼睛。没有它们,你根本不知道脚本跑得对不对。

4.4.1 常用report_*命令

命令 输出内容
report_timing 时序路径的详细报告
report_power 功耗分析报告
report_area 面积报告
report_constraints 当前所有约束的汇总
report_clocks 时钟信息
report_qor 综合质量结果(QoR)

4.4.2 调试技巧:组合使用

我曾经遇到一个时序违例,查了半天找不到原因。后来我用了个组合拳:

# 先看最差的路径
report_timing -max_paths 1 -nworst 1

# 再看这条路径上的所有cell
set worst_path_cells [get_cells -of [all_fanin -to [get_pins U_TOP/U_FF1/D] -flat]]
report_cell $worst_path_cells

# 最后检查约束有没有问题
report_constraints -all

结果发现,是set_false_path写错了,把一条关键路径给忽略了。嗯,从那以后,我每次设例外路径,都会用report_timing -exceptions再确认一遍。

我的习惯:每个脚本跑完后,至少用3个report_*命令做交叉验证。比如report_timingreport_areareport_power一起看,数据对得上,才放心。

4.5 命令族的协同工作

这三个命令族,不是孤立的。它们是一个完整的闭环:

  1. get_*:找到你要操作的对象
  2. set_*:对这些对象施加约束或属性
  3. report_*:验证结果是否正确

举个例子,你想给所有时钟域为100MHz的路径设一个多周期约束:

# 第一步:找到所有100MHz的时钟
set fast_clocks [get_clocks -filter "period == 10"]

# 第二步:对这些时钟域下的路径设多周期
foreach_in_collection clk $fast_clocks {
    set_multicycle_path -setup 2 -from $clk
}

# 第三步:验证
report_timing -from [get_clocks -filter "period == 10"] -max_paths 10

你看,三个命令族配合起来,就能完成一个完整的自动化任务。

避坑指南:我曾经在foreach_in_collection里直接修改了集合,结果导致循环异常。记住:遍历集合时,不要同时增删集合里的元素。

4.6 小结

这一讲,咱们把STCL的三大命令族过了一遍:

  • get_*:查询设计对象,是脚本的“眼睛”
  • set_*:设置约束和属性,是脚本的“手”
  • report_*:查看结果,是脚本的“耳朵”

我个人觉得,掌握这三个命令族,你就掌握了Synopsys工具自动化的80%。剩下的20%,就是各种细节和组合技巧了。

下一讲,咱们会深入get_*命令的过滤和集合操作,到时候我会分享一些我在实际项目中用到的“骚操作”。

课后练习:打开你的Design Compiler或IC Compiler,试试用get_*找出设计中所有扇出大于10的net,然后用report_*看看它们的时序情况。