这一篇在干嘛?

仿真调试最常见的两个痛点:一是「改动之后波形哪里变了看不出来」,二是「每天重复敲同一串命令敲到手酸」。这一篇对应官方 Tutorial 的第 18 章(波形比较)和第 19 章(Tcl/DO 自动化):波形比较能自动算出两次仿真之间测试信号与参考信号的时序差异;DO 文件则把一串命令打包成脚本,甚至可以完全脱离 GUI 在命令行跑仿真。学完这一篇,你的仿真工作流会从「手工时代」进入「脚本时代」。

波形比较:四步找出两次仿真的差异(对应原文第 18 章)

先记住总流程:四个步骤

波形比较(Waveform Compare)计算测试信号(test signals)与参考信号(reference signals)之间的时序差异。比较波形的一般流程分四步:

  1. 选择要比较的仿真或数据集(dataset);
  2. 指定要比较的信号或区域(region);
  3. 运行比较;
  4. 查看比较结果。

本节练习的思路是:先运行并保存一次仿真 → 修改一个源文件 → 再跑一次仿真 → 最后比较两次运行的结果。

实验设计

示例设计由一个控制行为级存储器的有限状态机(FSM)组成,测试平台 test_sm 提供激励。QuestaSim 安装自带 Verilog 和 VHDL 两个版本:

  • Verilog:<install_dir>/examples/tutorials/verilog/compare
  • VHDL:<install_dir>/examples/tutorials/vhdl/compare

本节用 Verilog 版做练习(有 VHDL license 可改用 VHDL 版),需要区分时文中会分别说明。想深入了解可参阅 User’s Manual 中的 Waveform Compare 与 Recording Simulation Results With Datasets 章节。

第一步:建立参考数据集 gold.wlf

参考数据集(reference dataset)是测试数据集将来要比对的基准,它可以是一个已保存的数据集、当前仿真数据集,或者当前仿真数据集的任意一部分。.wlf 文件就是 QuestaSim 记录仿真结果的波形日志文件。

操作步骤:

  1. 新建目录,把示例目录中的所有文件复制进去;
  2. 启动 QuestaSim:在 UNIX shell 输入 vsim 或用 Windows 图标(弹出 Welcome 对话框就点 Close),然后 File > Change Directory 切换到新目录;
  3. 依次执行以下命令:

Verilog:

vlib work
vlog *.v
vopt +acc test_sm -o opt_test_gold
vsim -wlf gold.wlf opt_test_gold
add wave *
run 750 ns
quit -sim

VHDL:

vlib work
vcom -93 sm.vhd sm_seq.vhd sm_sram.vhd test_sm.vhd
vopt +acc test_sm -o opt_test_gold
vsim -wlf gold.wlf opt_test_gold
add wave *
run 750 ns
quit -sim

关键在 vsim -wlf gold.wlf ...-wlf 开关让本次仿真的结果保存为名为 gold.wlf 的参考数据集(gold = 黄金参考,硬件圈惯用说法)。

第二步:修改测试平台,生成测试数据集

测试数据集(test dataset)就是将来要和参考数据集比较的那个 .wlf 文件。为了省事,我们直接从刚跑完的仿真出发——但要先编辑测试平台,制造两次运行之间的差异。

Verilog 版:

  1. 选择 File > Open 打开 test_sm.v
  2. 在 Source 窗口右键,取消弹出菜单中的 Read Only 勾选,让文件可编辑;
  3. 滚动到第 122 行,内容如下:
@ (posedge clk) wt_wd('h10,'haa);
  1. 把数据模式 'haa 改成 'hab,即:
@ (posedge clk) wt_wd('h10,'hab);
  1. 选择 File > Save 保存文件;
  2. 重新编译并重跑仿真:
vlog test_sm.v
vopt +acc test_sm -o opt_test_sm -noincr
vsim opt_test_sm
add wave *
run 750 ns

VHDL 版(编辑 test_sm.vhd):滚动到第 151 行,把 wt_wd (16#10#, 16#aa#, clk, into); 中的 'aa' 改成 'ab'(即 wt_wd (16#10#, 16#ab#, clk, into);),保存后重新编译:

vcom test_sm.vhd
vopt +acc test_sm -o opt_test_sm
vsim opt_test_sm
add wave *
run 750 ns

第三步:用 Comparison Wizard 执行比较

QuestaSim 自带比较向导(Comparison Wizard)一步步引导你完成配置,你也可以用菜单或命令行手动配置。这里用向导:

a. 选择 Tools > Waveform Compare > Comparison Wizard; b. 点 Browse 按钮,选择 gold.wlf 作为参考数据集——它就是第一次仿真保存的结果:

Comparison Wizard
The first step in creating a comparison is to open the reference and test datasets (.wlf files).
Either dataset can be a saved .wlf file or a dataset that is already opened.
Use the Browse buttons to browse for a saved dataset, or click the down arrow to select a file from the dataset selection history.
Reference Dataset
gold.wlf
Browse...
Test Dataset
Use Current Simulation
Update comparison after each run
Specify Dataset
Browse...
Next >
< Previous
Cancel

c. 测试数据集保持 Use Current Simulation(用当前仿真),点 Next; d. 第二个对话框选择 Compare All Signals(比较所有信号),点 Next:

e. 接下来三个对话框依次点 Next、Compute Differences Now、Finish。

QuestaSim 执行比较,并在 Wave 窗口中显示被比较的信号。对应四步流程:第 1、2 步在向导里完成,第 3 步是 Compute Differences Now,第 4 步马上开始——查看结果。

第四步:查看比较结果

比较数据会显示在 Structure (compare)、Transcript、Objects、Wave 和 List 窗口中。被比较的对象(compare 对象)用黄色三角形标记。

  • Compare 窗口:显示被比较的区域;
  • Transcript 窗口:显示参考与测试数据集之间找到的差异总数;
  • Objects 窗口:当你在 Structure (compare) 窗口选中某个比较对象时,它显示具体的比较差异:

Wave 窗口中的比较信息

Wave 窗口用图形方式清晰呈现比较结果:

  • 路径名列中的时序差异用红色 × 标注:

  • 波形视图中的红色区域就是时序差异所在;
  • 滚动条上的红色线条同样标示差异位置;
  • 被注解(annotated)的差异用蓝色高亮。

Wave 窗口还有六个比较图标,可以在差异之间快速跳转。从左到右依次是:查找第一个差异、查找上一个注解差异、查找上一个差异、查找下一个差异、查找下一个注解差异、查找最后一个差异。这些图标会在所有信号的差异之间循环跳转;只想看某一条选中信号上的差异,用 <tab><shift>-<tab>

List 窗口中的比较信息

比较结果也可以在 List 窗口里按表格查看:

a. 在主窗口菜单栏选 View > List; b. 把 Main 窗口 compare 标签里的 test_sm 比较对象拖到 List 窗口; c. 向下滚动窗口。

差异行用黄色高亮标注,被注解过的差异用红色高亮。可以看到从 455000 ps 起,outof 信号的 gold 值是 000000aa,而 sim 值是 000000ab——正是我们改掉的那一个比特:

compare:/test_sm/\rst<>rst\
ps→delta→compare:/test_sm/\outof<>outof\(gold)(sim)compare:/test_sm/\out_wire<>out_wire\(gold)(sim)compare:/test_sm/\clk<>clk\(gold)(sim)
430000+000000000000000000011
431000+100000000000000000011
435000+000000000000000000011
440000+000000000000000000000
450000+000000000000000000011
451000+100000000000000000011
455000+0000000aa000000ab0011
460000+0000000aa000000ab0000
469000+1000000aa000000ab0000
470000+0000000aa000000ab0011
471000+1000000aa000000ab0011
475000+0000000aa000000ab0011
480000+0000000aa000000ab0000
490000+0000000aa000000ab0011
491000+1000000aa000000ab0011
……(后续行略)

保存与重新加载比较数据

比较数据可以保存下来以后再看——既能存成纯文本报告,也能存成可重新加载回 QuestaSim 的文件。要重新加载,必须保存两个文件:先把计算出的差异存到一个文件,再把比较的配置规则(rules)存到另一个单独的文件;重新加载数据时,必须先打开参考数据集。

  1. 把比较数据保存为文本文件: a. 主窗口选择 Tools > Waveform Compare > Differences > Write Report; b. 点 Save——在当前目录保存 compare.txt; c. 在 VSIM> 提示符输入 notepad compare.txt 查看报告:
Notepad
File Edit Window
compare.txt
Total signals compared = 11
Total primary differences = 6
Total secondary differences = 6
Number of primary signals with differences = 4
Diff number 1, From time 135 ns delta 0 to time 155 ns delta 0.
gold:/test_sm/into = 000000aa
sim:/test_sm/into = 000000ab
Diff number 2, From time 135 ns delta 0 to time 155 ns delta 0.
gold:/test_sm/into[0] = 0
sim:/test_sm/into[0] = 1
Diff number 3, From time 171 ns delta 1 to time 191 ns delta 1.
gold:/test_sm/dat = 000000aa
sim:/test_sm/dat = 000000ab
Diff number 4, From time 171 ns delta 1 to time 191 ns delta 1.
gold:/test_sm/dat[0] = 0
sim:/test_sm/dat[0] = 1
Diff number 5, From time 409 ns delta 1 to time 411 ns delta 1.
gold:/test_sm/dat = 000000aa
sim:/test_sm/dat = 000000ab
Diff number 6, From time 409 ns delta 1 to time 411 ns delta 1.
gold:/test_sm/dat[0] = 0
sim:/test_sm/dat[0] = 1
compare.txt

报告一目了然:总共比较了 11 个信号,6 处差异,全部源于 into 信号上 aa 与 ab 的一比特之差。看完关闭 Notepad。

  1. 把比较数据保存为可重新加载的文件: a. 选择 Tools > Waveform Compare > Difference > Save,点 Save——在当前目录保存 compare.dif(差异数据); b. 选择 Tools > Waveform Compare > Rules > Save,点 Save——保存 compare.rul(比较规则); c. 选择 Tools > Waveform Compare > End Comparison 结束本次比较。

重新加载前,先打开参考数据集

比较结果(.dif)和比较规则(.rul)都依赖参考数据集。重新加载时如果没有先打开 gold.wlf,比较就无法恢复。

  1. 重新加载比较数据: a. 在 Structure (sim) 窗口激活的状态下,选择 File > Open; b. 把文件类型切换为 Log Files (*.wlf):

c. 双击 gold.wlf 打开该数据集; d. 选择 Tools > Waveform Compare > Reload。由于保存时用的是默认文件名,对话框里已自动填好正确的 Waveform Rules 和 Waveform Difference 文件:

e. 点 OK。比较被重新加载,把比较对象拖到 Wave 或 List 窗口即可再次查看差异。

本章收尾

本节结束。继续下一章前需要结束当前仿真并关闭 gold.wlf 数据集:

  1. 在 VSIM> 提示符输入 quit -sim
  2. 在 Questa SIM> 提示符输入 dataset close gold

DO 文件与 Tcl:把重复劳动交给脚本(对应原文第 19 章)

DO 文件为什么值得学

前面的章节里,除了执行几个现成的 DO 文件,我们一直以交互模式使用 QuestaSim:通过 GUI 菜单或主窗口命令行一条条敲命令。但当任务需要反复执行时,DO 文件能大幅提升效率——这就是它 automates 仿真的价值所在:

  • DO 文件是脚本,一次执行一大批命令
  • 简单时就是一串带参数的 QuestaSim 命令;复杂时就是完整的 Tcl 程序,能用变量、条件执行等编程结构;
  • 既可以在 GUI 里执行,也可以在系统命令提示符下运行——完全不打开 GUI

前提

本章假设你已把 <install_dir>/<platform> 目录加入 PATH;否则需要为 vlib、vmap、vlog、vcom、vsim 等工具写完整路径。

FPGA 语境

用安路 TD 做开发时,工程编译靠 IDE 一键完成,但功能仿真回归往往要反复跑。把「编译 + 加载 + 加激励 + 出报告」写成 DO 文件,一次投入,长期复用,这正是工业化验证流程的基础。

手写一个简单的 DO 文件

创建 DO 文件简单到什么程度?在文本文件里敲一组命令就行。本练习创建一个 DO 文件:加载设计、往 Wave 窗口加信号、给信号加激励、推进仿真。此外还可以把保存的 transcript 文件转换成 DO 文件(参阅 GUI Reference Manual 中的 Saving a Transcript File as a DO file)。

  1. 切换到 Basic Simulation 一课创建的目录。
  2. 创建 DO 文件:选择 File > New > Source > Do 新建,在 Source 窗口输入:
vsim testcounter_opt
add wave count
add wave clk
add wave reset
force -freeze clk 0 0, 1 {50 ns} -r 100
force reset 1
run 100
force reset 0
run 300
force reset 1
run 400
force reset 0
run 200

逐条看懂这些命令:

  • vsim testcounter_opt:加载优化后的设计;
  • add wave ...:把 count、clk、reset 三个信号加入 Wave 窗口;
  • force -freeze clk 0 0, 1 {50 ns} -r 100:制造时钟——在 0 时刻把 clk 强制为 0,50 ns 后变为 1,-r 100 表示每 100 ns 重复一次,即周期 100 ns 的方波;-freeze 表示强制期间信号值被冻结;
  • force reset 1 / run 100 / force reset 0 / run 300 / ...:一段完整的复位时序——先复位保持 100 ns,释放 300 ns,再复位 400 ns,再释放 200 ns,覆盖复位与正常工作两种情形。
  1. 保存文件:File > Save As,文件名填 sim.do,存到当前目录。
  2. 执行 DO 文件:在 VSIM> 提示符输入:
do sim.do

QuestaSim 加载设计、执行保存的命令并在 Wave 窗口画出波形:

  1. 练习完成后选择 File > Quit 退出 QuestaSim。

命令行模式:不开 GUI 也能仿真

「命令行模式」(command-line mode)指不启动 GUI、直接在 DOS/UNIX 提示符下运行仿真。QuestaSim 的许多命令(vsim、vlib、vlog 等)本身就是独立的可执行文件,可以在系统命令提示符直接调用;还可以在调用仿真器时指定一个包含其他 QuestaSim 命令的 DO 文件。

  1. 新建目录,复制以下文件进去:

    • /<install_dir>/examples/tutorials/verilog/automation/counter.v
    • /<install_dir>/examples/tutorials/verilog/automation/stim.do

    (有 VHDL license 的可改用 /<install_dir>/examples/tutorials/vhdl/automation 目录下的 counter.vhd 和 stim.do。)

  2. 创建设计库并编译源文件。在第 1 步的新目录里输入: a. vlib work; b. Verilog 输入 vlog counter.v(VHDL 则输入 vcom counter.vhd)。

  3. 创建 DO 文件: a. 打开文本编辑器; b. 输入以下内容:

# list all signals in decimal format
add list -decimal *
#change radix to symbolic
radix -symbolic
# read in stimulus
do stim.do
# output results
write list counter.lst
# quit the simulation
quit -f

c. 以 sim.do 为名保存到当前目录。

注意这个 DO 文件的精妙之处:add list -decimal * 把所有信号按十进制记录到 List 窗口;do stim.do 嵌套调用另一个现成的激励 DO 文件;write list counter.lst 把仿真结果输出到文本文件;quit -f 强制退出——一整条「批处理流水线」。

  1. 优化 counter 设计单元:
vopt +acc counter -o counter_opt
  1. 运行命令行模式仿真:
vsim -c -do sim.do counter_opt -wlf counter_opt.wlf

两个关键参数:-c 指示 QuestaSim 不启动 GUI;-wlf 把仿真结果保存进 WLF 文件——这样之后仍可在 GUI 里查看结果用于调试。

  1. 查看 List 输出:打开 counter.lst 查看仿真结果,Verilog 版输出类似下表(VHDL 版可能略有不同):
counter.lst
0102030 40
1ns/counter/count
2delta/counter/clk
3/counter/reset
40 +0x z 1
53 +00 z 1
650 +00 1 1
7100 +00 0 1
8100 +10 0 0
9150 +00 1 0
10152 +01 1 0
11200 +01 0 0
12250 +01 1 0
13252 +02 1 0
14300 +02 0 0
15350 +02 1 0
  1. 回 GUI 查看结果。因为结果已保存进 counter_opt.wlf,用 -view 参数调用 VSIM 即可在 GUI 中查看(确认 PATH 环境变量指向当前版本的 QuestaSim): a. 在提示符输入:
vsim -view counter_opt.wlf

GUI 打开后,主窗口工作区显示名为 counter_opt 的数据集标签(看不到 Objects 窗口就选 View > Objects):

counter_opt - Default
Instance Design unit
counter counter
y Files counter_opt Objects
Name Value Kind
count 8'h64 Packed Array
clk 1'h0 Net
reset 1'h0 Net

b. 右键 counter 实例选 Add Wave,波形即显示在 Wave 窗口。 8. 看完选择 File > Quit 关闭 QuestaSim。

回归仿真的标准姿势

-c(无界面)跑完仿真 + -wlf 保存结果 + vsim -view xxx.wlf 事后查看,是自动化回归测试的标配组合:跑的时候不占 GUI 资源,出问题了再打开波形细看。

用 Tcl 编程:when + bookmark 自动标注关键波形

前面用的 DO 文件只包含 QuestaSim 命令,但 DO 文件本质上是 Tcl 脚本——所以过程(procedure)、条件运算、数学与三角函数、正则表达式等各种 Tcl 结构都可以用。

本练习写一个简单的 Tcl 脚本:检测信号上的特定值,并在该值出现时自动添加书签(bookmark)来缩放 Wave 窗口。书签可以保存 Wave 窗口的某个缩放范围和滚动位置;脚本还会在主窗口创建名为书签的按钮。

  1. 创建脚本: a. 在文本编辑器中打开新文件,输入:
proc add_wave_zoom {stime num} {
echo "Bookmarking wave $num"
bookmark add wave "bk$num" "[expr $stime - 100] [expr $stime + 50]" 0
add button "$num" [list bookmark goto wave bk$num]}

这几行做的事:

  • 定义一个名为 add_wave_zoom 的过程,带两个参数 stime(时间)和 num(编号);

  • bookmark add wave 创建书签,缩放范围是「当前仿真时间减 100 个时间单位」到「当前仿真时间加 50 个时间单位」([expr ...] 是 Tcl 的算术表达式求值);

  • add button 在主窗口加一个按钮,点击即跳转到该书签。

    b. 在脚本底部追加:

add wave -r /*
when {clk'event and clk="1"} {
    echo "Count is [exa count]"
    if {[examine count]== "8'h27"} {
    add_wave_zoom $now 1
    } elseif {[examine count]== "8'h47"} {
    add_wave_zoom $now 2
    }
}

这几行做的事:

  • add wave -r /*:递归地把所有信号加入 Wave 窗口;

  • when {clk'event and clk="1"}:QuestaSim 的 when 语句——每当 clk 发生事件且变为 1(即时钟上升沿)时,执行花括号里的命令;

  • 每个上升沿用 examine(缩写 exa)读出 count 的当前值并 echo 打印;如果等于 8’h27 就在当前位置建 1 号书签,等于 8’h47 就建 2 号书签——脚本根据仿真内容自动标注了关键时间点,人不用盯着波形找。

    c. 以 add_bkmrk.do 为名,把脚本保存到 Basic Simulation 一课创建的目录。

  1. 加载 test_counter 设计单元,并确保进制设为二进制: a. 启动 QuestaSim; b. File > Change Directory 切换到保存 DO 文件的目录; c. 在 Questa SIM> 提示符输入 radix -binary; d. 输入:
vsim testcounter_opt
  1. 执行 DO 文件并运行: a. 在 VSIM> 提示符输入 do add_bkmrk.do; b. 输入 run 1500 ns

仿真运行完毕,DO 文件创建了两个书签,并在主窗口工具栏上生成了标注「1」「2」的按钮:

c. 点击这两个按钮,观察 Wave 窗口自动缩放并滚动到 count 等于指定值的时间点; d. 若 Wave 窗口停靠在主窗口内,先点击波形区域使其成为活动窗口,再选 Bookmarks > bk1(未停靠则在 Wave 窗口菜单选)。Wave 窗口会缩放滚动到 count 为 8’h27 的时刻,bk2 同理。

本章收尾

选择 File > Quit 关闭 QuestaSim,本章结束。想继续深挖 Tcl,可参阅 User’s Manual 中的 Tcl and DO Files 章节,以及 Brent B. Welch 的《Practical Programming in Tcl and Tk》。

自测