第五章Tcl 脚本编程
Tcl 基础
Tcl 三大公理
- Tcl 中一切都是字符串
- 每个字符串都是一个列表
- 每个列表由空白分隔的零个或多个元素组成
基本语法
# 变量
set x 42
set name "hello"
# 变量替换 $
puts $x ;# 输出 42
puts "$name world" ;# 输出 hello world
# 命令替换 []
set y [expr $x + 1]
# 字符替换(转义) \
puts "Line1\nLine2"
# 大括号 {}:阻止替换
set z {$x + $y} ;# z 是字符串 "$x + $y",不替换
# 双引号 "": 允许替换
set w "$x + $y" ;# w 是字符串 "42 + 43"
Procedures(函数)
proc add {{a 0} {b 0}} {
# 过程定义,a 和 b 有默认值 0
return [expr $a + $b]
}
add 3 4 ;# 返回 7
add 5 ;# 返回 5
Return Values 和 Return Status
- 每个 Tcl 命令都有返回值
- 命令可能正常返回或产生错误(error)
catch命令捕获错误
# catch 的语法:catch {some_command ...} variable
# 第一个参数是要执行的命令(含其参数);
# 第二个参数是一个变量,会被设为 some_command 的返回值;
# catch 本身的返回值是 some_command 的执行状态。
if {[catch {some_command} result]} {
puts "Error: $result"
}
Standard Tcl vs Jasper Tcl 的差异
JasperGold Apps 会更改、重命名或不支持标准 Tcl 中的某些命令。官方列出的差异只有两条:
| Standard Tcl | Jasper Tcl 中的解释 |
|---|---|
clock | tcl_clock(被重命名,因为 clock 在 JasperGold 中是设置时钟的命令) |
pid | JasperGold Apps 支持 pid 命令。它返回的是 analysis session 的进程 ID,而不是 JasperGold 控制台的 pid |
JasperGold 脚本常用任务
-silent 开关
许多 JasperGold Apps 命令提供可选的 -silent 开关。使用该开关时,命令不打印任何结果,而是把结果作为普通的 Tcl 返回值返回。在脚本中,只要命令支持这个开关,你几乎总是应该使用它。如果某个命令没有提供该开关,说明它的结果本来就是以 Tcl 结果值的形式返回的。
-silent 是单条 Tcl 命令的开关,不是 jg 的命令行选项——写成 jg -fpv script.tcl -silent 是错误的。Structure Queries(结构查询)
脚本中最常见的任务之一,是获取当前已 elaborate 的设计的信息。get_design_info 命令提供了对大量信息的访问。默认情况下它既返回值又打印信息;在脚本中你通常只想要返回值,加上 -silent 即可禁止打印。
# 获取设计中所有模块
set modList [get_design_info -list module -silent]
# Mod_A1 Mod_B1 Mod_B2 Mod_B3 top
# 获取某个模块类型的所有实例
set instList [get_design_info -module Mod_B2 \
-list instance -silent]
# inst0_B2 inst1_B2 inst2_B2 inst3_B2 inst4_B2 inst5_B2
# 获取信号的位范围
set sigInfo [get_signal_info -indexes wdata0 -silent]
# 1D_Array 31 0
# 获取信号的位宽
set sigInfo [get_signal_info -width wdata0 -silent]
# 32
Cone of Influence (COI) Queries
# 获取某个属性 COI 中的模块
set coiModules [get_design_info -property myTask::addr_conn0 \
-list module -silent]
# maint top
# 获取不在 COI 中的模块
set notCOImods ""
foreach module [get_design_info -silent -list module] {
if { [lsearch -exact $coiModules $module] < 0 } {
lappend notCOImods $module
}
}
set notCOImods
# arbiter bridge egress ingress port_select
常见脚本模式
筛选属性用 get_property_list,它通过 -include / -exclude 按属性的 attribute 取值来筛选,而不是靠专门的开关。注意排除优先于包含:同一个 attribute 值若同时出现在 -include 和 -exclude 中,该属性会被排除。
# 对所有 cex 属性做处理
foreach prop [get_property_list -include {status cex}] {
puts "Failed: $prop"
}
# 取出所有未决的 assert/cover:包含 type 为 assert 或 cover
# 且未被 disable 的,排除已经 proven/cex/unreachable/cover 的
get_property_list -include {type {assert cover} disabled 0} \
-exclude {status {proven cex unreachable cover}}
# 数值型 attribute 需要带比较运算符
get_property_list -include {trace_length <20}
# 用正则匹配属性名
get_property_list -include {name top.inst0*(_vld|_valid)} -regexp
# 每属性时间限制是全局设置,只接受一个时间参数,
# 不能按属性单独指定
set_prove_per_property_time_limit 10s
get_property_list 的完整语法为 get_property_list [-include {...}] [-exclude {...}] [-regexp] [-task <task_name>] [-exclude_env] [-no_task_prefix]——没有 -status、-tag 或 -silent 开关。属性状态是通过 -include {status ...} 传入的 attribute 之一。脚本化方法论
为什么要写 Tcl 脚本而不是只用 GUI?脚本让验证流程可复现、可回归、可自动化。
为什么需要脚本
- 可复现:RTL 修改后重跑脚本得到一致结果
- 可回归:每次 RTL 迭代自动跑回归,确保无新 bug
- 自动化 signoff:脚本自动检查关键断言 proven 并生成报告
- 团队共享:脚本可版本控制,团队使用同一套验证环境
标准脚本模板结构
# ===== 标准 FPV 脚本模板 =====
clear -all
# 1. 设置路径
set RTL_PATH ../source/design
set PROP_PATH ../source/properties
# 2. 分析设计和属性
# 语言开关:-sv | -sv05 | -sv09 | -sv12(VHDL 用 -vhdl 系列)
analyze -sv ${RTL_PATH}/top.v
analyze -sv ${PROP_PATH}/bindings.sva
# 3. 展开设计
elaborate -top top
# 4. 时钟和复位
clock clk
reset ~rstN
# 5. 获取设计信息
get_design_info
# 6. 第一轮:快速证明
set_max_trace_length 20
prove -all
# 7. 第二轮:深度证明
set_max_trace_length 50
set_prove_per_property_time_limit 30s
# 专注找证明:N 和 Tri 性能最好
set_engine_mode {N Tri I}
prove -all
# 8. 报告
report
report -file proof_results.rpt
# 9. CI 检查:取出所有还没有 proven/unreachable 的 assert 和 cover
set failed [get_property_list -include {type {assert cover} disabled 0} \
-exclude {status {proven unreachable}}]
if {[llength $failed] > 0} {
puts "ERROR: [llength $failed] properties not proven!"
exit 1
}常用脚本技巧
批量操作断言
# 批量操作断言:用 -regexp 按属性名匹配
assert -disable {*}[get_property_list -include {name top.debug.*} -regexp]
prove -property {top.submodule.*}
# 保存数据库
set proj_dir [get_proj_dir]
save -jdb $proj_dir/session.jdb -capture_setup脚本最佳实践:
- 总是从
clear -all开始,确保脚本独立运行 - 使用变量管理路径,不要硬编码绝对路径
- 脚本末尾用 exit code 通知 CI 系统是否通过
- 保存数据库(
save -jdb),后续可用 GUI 打开调试 - 注释每个主要步骤的意图,方便团队理解
来源文档
AN_scripting.pdf