GTKWave 波形分析流程:信号搜索、marker 测距与双 VCD 基线对照
VCD 能在 GTKWave 里打开,只完成了波形调试的一半。真正的分析工作发生在打开之后:在上百个信号里搜到目标、沿跳变沿定位事件、用两个 marker 量一段时序、把总线数值翻译成人话、再拿两份仿真结果互相对照。这些动作在图形界面里是鼠标点击,在回归脚本里则全部有对应的 Tcl 接口。本篇用一个深度 4 的同步 FIFO,配合「快排空 / 慢排空」两份 VCD,把打开波形之后的分析流程完整走一遍。VCD 怎么从 testbench 导出来,见 VCD 波形导出与 GTKWave 查看:dump 任务、分层导出与 .gtkw 布局;布局保存与批导出的自动化入口,见 GTKWave 脚本化查看流程:.gtkw 保存文件与 tcl 批导出。
本文要回答的问题
- 信号树里信号太多,怎么用正则一次性搜出
full、empty、fire_*这组状态信号? - 怎么让两个 marker 卡住
full的上升沿和下降沿,直接读出持续时间? - 总线数值怎么翻译成文字标签,满足条件的信号怎么统一高亮?
- 两份不同激励的 VCD 怎么做基线对照,量化「快排空」和「慢排空」的行为差异?
- 无显示环境下哪些界面菜单命令不能用,替代写法是什么?
这张流程卡是全篇主线:分析不是「盯着波形看」,而是一条可复现的动作链。界面操作解决一次性排查,Tcl 等价命令解决回归复用——同一条链两种执行方式,吃的都是同一份 VCD 数据。
一、这一步在波形调试流程里的位置
波形分析位于「仿真已产出 VCD」与「定位到设计问题」之间。上游 testbench 用 $dumpvars 记录信号,本篇的 testbench 通过 +MODE=A/B 一次编译产出两份波形:A 场景连读 4 拍、停 2 拍、再读 4 拍;B 场景读 1 拍停 1 拍。下游产出的不是新文件,而是三类结论:事件时刻、时间差、跨场景差异。
读图按颜色走:灰色是只读的两份波形,蓝色搜索入窗解决「看哪些信号」,红色 marker 测距解决「事件持续多久」,橙色过滤缩放解决「看得清」,绿色基线对照输出最终结论。VCD 的声明段与值变化段结构是所有动作的共同数据基础,可先读 VCD 与 SAIF 数据格式。
二、分析动作逐段解读:界面操作与 Tcl 等价命令
下表把五个分析动作拆成「界面点哪里 / Tcl 怎么写 / 不加会怎样」三列,Tcl 命令均来自本篇 analyze.tcl 的真跑验证。
| 分析动作 | 界面操作 | Tcl 等价命令与要点 |
|---|---|---|
| SST 正则搜索 | Search → Signal Search Regexp | getNumFacs 取信号总数,循环 getFacName 配 regexp {full|empty|fire_} 过滤;不搜就得手动展开两层层次树 |
| 沿查找 | Search → Find Next Edge | 无头脚本统一用 signalChangeList 拿完整跳变表再枚举边沿;findNextEdge 依赖界面选中态,脚本里结果不可复现 |
| marker 测时间差 | 左键放主 marker、右键放 baseline marker | setMarker/setBaselineMarker 落两个时刻,getMarker - getBaselineMarker 得 ps 级差值,自行除以 1000 换 ns |
| 缩放导航 | Time → Zoom → Zoom Full / Zoom Range | gtkwave::/Time/Zoom/Zoom_Full 全屏,setZoomRangeTimes 起点 终点 卡事件窗 |
| 值翻译与高亮 | Values → Translate Filter、Highlights 菜单 | installFileFilter 装翻译表、setCurrentTranslateFile 启用;setTraceHighlightFromNameMatch {full|empty} 1 正则高亮 |
| 双 VCD 对照 | File → Open New Tab | gtkwave::/File/Open_New_Tab wave_b.vcd,getNumTabs 确认标签页数 |
两个语法细节。第一,信号名里的 [2:0] 在 Tcl 中是命令替换语法,必须用花括号写成 {tb.dut.cnt[2:0]},写在双引号里会报 invalid command name "2:0"。第二,signalChangeList 返回的表尾带一条时间戳约 9.22e18 的哨兵记录(64 位最大值附近),脚本里统一用 if {$t > 1000000000000000} continue 过滤,别把它当真实跳变。
三、输入输出文件:两份 VCD、翻译表、跳变表与 save 文件
输入是 testbench 产出的两份 VCD:wave_a.vcd 2890 字节、wave_b.vcd 3002 字节,时间范围都是 0..500000ps,声明段里 tb 与 tb.dut 两层共 26 个 facility。值翻译过滤器是一个一行一个映射的纯文本 cnt_filter.txt,把 FIFO 占用量翻译成语义标签:0 EMPTY、1 W1、2 W2、3 W3、4 FULL,装上之后 cnt 波形上的数值直接显示文字。
分析过程吐出三类文本产物。其一是跳变表,每行「时间戳 值」,如场景 B 的 full_b.txt 记录 65000 1、75000 0、85000 1……三个完整的满标志窗口;其二是终端 marker 数据;其三是 analysis.gtkw save 文件,[dumpfile] 记录配套波形指纹,@28 段逐行记录入窗的 8 条信号,[timestart] 45000 与 marker 行保存了缩放窗口和主 marker 位置,下次 gtkwave -a analysis.gtkw wave_a.vcd 即可恢复整个分析现场。
这张终端卡按行读:SEARCH_HITS: 8 说明正则在两层层次里命中 8 条状态信号(testbench 层与 dut 层各一组同名信号);FULL_EDGES: 5000/0 65000/1 75000/0 是场景 A 中 full 的全部三次跳变,65ns 拉高、75ns 拉低;FULL_DURATION_NS: 10.0 是两个 marker 算出的持续时间,恰好一个时钟周期(10ns);ZOOM_MARKED_WINDOW_NS: 45.0..95.331 是事件窗缩放后的实际可视范围;TABS_OPEN: 2 与 SAVE_WRITTEN 确认双标签和 save 文件都成功。
四、常见错与检查点
| 现象 | 检查 | 纠正 |
|---|---|---|
脚本里循环调 findNextEdge 长时间不返回,最后超时 | 是否给它传了信号名参数、是否依赖界面选中态 | 3.3.116 无头模式下该调用结果不可复现;改用 signalChangeList 一次取全表再枚举边沿 |
| 调时间维度菜单命令进程直接崩溃,返回码 139 | 是否用了 gtkwave::/View/Scale_To_Time_Dimension/ns 这类 GUI 菜单命令 | 无头环境该命令稳定 SIGSEGV;时间换算在 Tcl 里做,ps 除以 1000 得 ns |
save 文件里没有 @28 信号段,只剩视图参数 | 是否在 Open_New_Tab 之后才写 save | 新标签成为活动标签后保存的是它的空白视图;先 Write_Save_File_As 再开新标签 |
Tcl 报 invalid command name "2:0" | 总线信号名是否写在双引号或裸串里 | 一律花括号包裹:{tb.dut.cnt[2:0]} |
| 跳变表末尾出现天文数字时间戳 | 是否把哨兵记录当成数据 | 按 1e15 阈值过滤,哨兵值在 9.22e18 附近 |
第三节的 save 文件顺序坑值得多说一句:同一脚本里先开 B 标签再保存,[dumpfile] 会变成 wave_b.vcd 且信号段为空;调整为先保存后开标签,[dumpfile] 才正确指向 wave_a.vcd 并带上全部 8 条信号。这是无头批处理特有的检查点,界面操作时因为当前标签一目了然反而不容易犯。
五、真实跑一遍:快排空与慢排空的基线对照
两个场景共用同一个深度 4 的 FIFO 和同一组生产者激励(连续 8 拍写请求),只改消费者节奏。场景 A 的读使能在突发期间保持拉高,fire_rd = rd_en & ~empty 因此是一段 70ns 到 135ns 的连续电平,只产生一个上升沿,full 仅在 65..75ns 置位一次。场景 B 读 1 拍停 1 拍,fire_rd 变成 6 个离散脉冲,相邻上升沿间隔稳定 20.0ns,full 在排空过程中反复置位三次。
这张卡的 diff 只新增四行:85000/95000 与 105000/115000 两个额外的满标志窗口,正是慢排空多积压出来的两段时间;下方 B_RD_HANDSHAKE_GAPS_NS 五个间隔全部是 20.0ns,与 testbench「读 1 拍停 1 拍」的激励严格吻合。双标签页负责肉眼对照,跳变表 diff 负责可入库的文本结论——回归时波形变没变,diff 一眼可知。VCD 本身打不开或解析报错的前置故障,见 GTKWave VCD 解析失败 与 iverilog 仿真跑完无波形输出。
实验环境
- 机器/系统:macOS 主机 + Docker 容器
geek-eda:0.4-flow(Debian,Xvfb 虚拟显示) - 工具版本:Icarus Verilog version 12.0 (stable);GTKWave Analyzer v3.3.116 (w)1999-2023 BSI(均为容器内
iverilog -V、gtkwave -V真实输出) - 日期:2026-11-06
- 完整命令:
bash run.sh(依次执行版本查询、iverilog -g2012编译与双场景vvp、VCD 声明段检查、xvfb-run gtkwave -S analyze.tcl分析链、双场景跳变表导出、文本 diff、B 场景握手间隔测量、时间维度菜单复现崩溃、save 文件片段检查)
代码获取
本文全部实验代码(sync_fifo.v、tb_fifo.v、analyze.tcl、export_full.tcl、gaps_b.tcl、timedim.tcl、cnt_filter.txt、run.sh)已同步到公开仓库 geek-doc.cn/experiments/flow-gtkwave-vcd-analysis,本机装好 Docker 与 geek-eda:0.4-flow 镜像后执行 bash run.sh 即可复现八段全流程,产物为 analysis.gtkw、双场景跳变表与完整运行记录。
这一篇在知识体系里的位置
本篇讲波形「打开之后」的分析动作:波形怎么导出来看 VCD 波形导出与 GTKWave 查看:dump 任务、分层导出与 .gtkw 布局,分析现场怎么存成 save 文件并批量化复现看 GTKWave 脚本化查看流程:.gtkw 保存文件与 tcl 批导出,VCD 文本格式本身看 VCD 与 SAIF 数据格式;两个相邻故障页是 GTKWave VCD 解析失败 与 iverilog 仿真跑完无波形输出。
总结
GTKWave 波形分析流程是一条五步动作链:SST 正则搜索定位信号(实测 26 个 facility 命中 8 条)、信号入窗并装值翻译过滤器、主 marker 与 baseline marker 卡沿测距(场景 A 的 full 持续 10.0ns)、缩放加高亮聚焦事件窗、新标签页配合跳变表 diff 做双 VCD 基线对照(场景 B 多两个满窗口、读脉冲间隔稳定 20.0ns);无头脚本里沿查找统一走 signalChangeList 并过滤表尾哨兵,时间维度菜单命令会 SIGSEGV(rc=139)需改用 Tcl 换算,save 文件必须在开新标签之前写,八段流程在容器内全部真跑通过。