2、脚本化仿真基础:Shell脚本入门,Makefile基础,Tcl脚本入门
各位工程师朋友,欢迎来到第二章。上一章我们聊了为什么要做脚本化仿真,说白了就是让机器替我们干那些重复的苦力活。这一章,我们来点实在的——把三个最常用的脚本工具过一遍。Shell、Makefile、Tcl,这三样东西,你玩转了,仿真效率至少翻一倍。
我个人习惯把脚本比作「胶水」。Shell是粘合操作系统和工具的胶水,Makefile是粘合编译流程的胶水,Tcl则是粘合EDA工具的胶水。三管齐下,你的仿真环境就能跑得飞起。
2.1 Shell脚本入门:让命令行听你的话
Shell脚本,说白了就是把一堆Linux命令写进一个文件里,然后一次性执行。我在项目中遇到过最典型的场景:每天上班第一件事,跑一遍回归测试。手动敲十几条命令?太傻了。写个脚本,一键搞定。
先看一个最简单的例子:
#!/bin/bash
# 我的第一个仿真脚本
echo "开始编译..."
vlog -work work top.sv
echo "开始仿真..."
vsim -c -do run.do top
echo "仿真完成!"
嗯,这里要注意:第一行的 #!/bin/bash 叫 shebang,它告诉系统用哪个解释器来跑这个脚本。没有它,脚本就是个普通文本文件。
变量和参数:
写脚本免不了要用变量。比如你要指定不同的测试用例:
#!/bin/bash
TEST_NAME=$1 # 第一个命令行参数
SEED=$2 # 第二个命令行参数
echo "运行测试: $TEST_NAME, 种子: $SEED"
vsim -c -sv_seed $SEED -do "run -all" $TEST_NAME
调用的时候这样写:./run_test.sh my_test 12345。你看,一个脚本就能跑不同的测试,灵活多了。
条件判断和循环:
仿真中经常要判断文件是否存在,或者循环跑多个测试。Shell的 if 和 for 就是干这个的:
#!/bin/bash
# 检查日志文件是否存在
if [ -f "sim.log" ]; then
echo "发现旧的日志文件,删除它..."
rm sim.log
fi
# 循环跑三个测试
for TEST in test_a test_b test_c; do
echo "正在运行: $TEST"
vsim -c -do "run -all" $TEST
done
为什么会这样?因为仿真经常要反复跑,手动清理文件太容易出错了。用脚本自动清理,省心又安全。
2.2 Makefile基础:让编译自动化
Makefile,说白了就是一套「依赖关系管理」工具。你告诉它:A文件依赖B文件,如果B改了,就重新生成A。Make会自动帮你搞定。
我在做大型SoC项目时,代码文件上百个,手动编译?不存在的。Makefile一出,一条 make 命令全搞定。
一个简单的Makefile:
# 变量定义
TOP = top
SRC = top.sv sub_module.sv driver.sv
WORK = work
# 默认目标
all: compile simulate
# 编译规则
compile:
vlog -work $(WORK) $(SRC)
# 仿真规则
simulate: compile
vsim -c -do "run -all" $(TOP)
# 清理
clean:
rm -rf $(WORK) transcript *.log
你看,all 是默认目标,它依赖 compile 和 simulate。你执行 make,它会先编译,再仿真。如果只编译,就执行 make compile。
Makefile的核心语法:
| 语法 | 说明 | 示例 |
|---|---|---|
| 目标: 依赖 | 定义目标和它的依赖 | simulate: compile |
| $(变量) | 引用变量 | $(TOP) |
| % | 通配符,匹配任意 | %.v: %.sv |
| @ | 隐藏命令回显 | @echo "完成" |
进阶用法:自动推导
Make其实很聪明,它能自动推导一些规则。比如你有一个 top.sv 文件,Make知道它应该用 vlog 来编译。但为了清晰,我建议还是显式写出规则,方便别人阅读。
2.3 Tcl脚本入门:EDA工具的通用语言
Tcl,全称Tool Command Language,是EDA工具的「普通话」。ModelSim、Vivado、Questa、Design Compiler……几乎所有主流工具都支持Tcl。你想想看,学会Tcl,就等于拿到了所有工具的遥控器。
第一个Tcl脚本:
# 创建一个工作库
vlib work
# 编译源文件
vlog top.sv
vlog sub_module.sv
# 启动仿真
vsim top
# 添加波形
add wave -r /*
# 运行仿真
run 1000 ns
把这个保存成 run.do,然后在命令行执行 vsim -c -do run.do。你看,所有操作都自动化了。
Tcl的变量和流程控制:
# 设置变量
set TOP "top_module"
set RUN_TIME "1000 ns"
# 条件判断
if {[file exists "sim.log"]} {
puts "日志文件已存在,删除..."
file delete sim.log
}
# 循环
for {set i 0} {$i < 5} {incr i} {
puts "运行第 [expr {$i + 1}] 次仿真"
vsim -c -do "run $RUN_TIME" $TOP
}
嗯,这里要注意:Tcl的语法和C语言很像,但又有自己的特点。比如变量引用用 $,表达式计算用 expr,方括号 [] 表示命令替换。
常用Tcl命令速查:
| 命令 | 作用 | 示例 |
|---|---|---|
| vlib | 创建库 | vlib work |
| vlog | 编译Verilog/SV | vlog top.sv |
| vcom | 编译VHDL | vcom top.vhd |
| vsim | 启动仿真 | vsim top |
| add wave | 添加波形 | add wave -r /* |
| run | 运行仿真 | run 1000 ns |
run 1000,结果仿真只跑了1000个时间单位(默认是ns),但我以为是1000us。后来养成习惯,每次写run都带上时间单位,比如 run 1000 ns 或 run 1 us。
三个工具如何配合?
在实际项目中,我通常这样用:
- Shell脚本:作为总调度,调用Makefile和Tcl脚本
- Makefile:管理编译流程,处理文件依赖
- Tcl脚本:控制仿真器,设置波形,运行测试
举个例子,一个完整的仿真流程可能是这样的:
#!/bin/bash
# run_all.sh - 一键仿真脚本
echo "=== 开始仿真流程 ==="
# 1. 用Makefile编译
make clean
make compile
if [ $? -ne 0 ]; then
echo "编译失败!"
exit 1
fi
# 2. 用Tcl脚本跑仿真
vsim -c -do run.do top_module
# 3. 检查结果
if grep -q "PASS" sim.log; then
echo "测试通过!"
else
echo "测试失败,请检查日志。"
fi
你看,Shell负责「胶水」工作,Makefile负责「编译」工作,Tcl负责「仿真」工作。各司其职,清清楚楚。
好了,这一章的内容就到这里。Shell、Makefile、Tcl,这三样东西你掌握了,脚本化仿真的基础就算打牢了。下一章,我们会深入实战,用这些工具搭建一个完整的自动化仿真环境。到时候你会发现,原来仿真可以这么轻松。