
芯片前端调试这行你要是没跟Verdi打过交道那基本等于白干了几年。不管是学校里跑仿真还是公司里定位RTL bugVerdi都是绕不开的那个工具。以前它还叫Debussy的时候就是调试神器被Synopsys收编改名Verdi之后跟VCS的配合更是顺滑到不行。我这次整理了一套“文档视频”的Verdi基础教程这篇博文就当是配套的精华笔记把从环境准备、FSDB波形生成、再到用波形反推代码问题的全流程都捋一遍。不管你是刚入门的学生还是刚转行做数字验证的新人只要手里写过一点Verilog这篇内容都能帮你省下不少瞎折腾的时间。很多新手最容易犯的毛病就是一上来就盯着快捷键表背结果打开Verdi还是不知道该点什么。Verdi的定位不是“画波形的工具”而是“基于波形反向追踪代码逻辑”的调试平台。它最大的价值在于把波形、源码、原理图、状态机四者关联起来让你从一个信号的反常跳变一路追踪到是哪一行代码、哪个模块例化、哪个状态转移出了问题。所以这篇教程不会只讲按钮在哪我会重点讲清楚每个操作背后的调试思路你照着走一遍比单独闷头看十分钟界面要有用得多。1. 这套教程为什么按“文档视频”双线设计1.1 先看视频建立全貌再看文档补细节我之前带过不少新人发现大家学EDA工具最大的障碍不是工具本身而是不知道自己在干什么。比如视频里演示命令行如果你不理解那行命令是在调哪个环节换个环境你就不会用了。所以我特意把教程拆成“视频文档”两条线视频负责带你在真实界面里走完整流程从启动Verdi到定位一个故意埋下的bug全程手把手文档则负责把每条命令、每个参数、每个窗口的作用讲透方便你随时回来查。两者的关系就跟学菜谱一样视频教你颠锅的动作文档告诉你为什么要热锅凉油。这样设计还有一个实实在在的好处信息密度不同。视频适合建立感觉比如看波形缩放、点击信号反标到源码这种操作文字写一百句不如眼睛看三秒但文档适合记录那些必须精确的东西比如$fsdbDumpvars的参数含义、PLI文件的完整路径、快捷键的准确拼写这些单纯靠视频反而容易记错。所以我建议你的学习顺序是先完整过一遍视频心里有个大体框架然后打开这篇文档跟着动手敲遇到记不清的参数再回头翻。1.2 学Verdi之前要具备的几个基本认知在正式动手之前有几个概念必须脑子里先立住否则后面容易绕晕。Verdi和仿真器是两码事Verdi主要负责“看”和“分析”仿真本身还是由VCS、Questa、Xcelium这些仿真器负责。Verdi读取的是仿真器吐出来的波形文件不是说装了Verdi就不用VCS了。两者要做的是联合仿真Verdi负责接收仿真数据并实时展示。FSDB是Verdi的亲儿子格式FSDBFast Signal DataBase是Verdi的原生波形格式压缩率高、加载速度快支持增量保存。VCS可以配合PLI接口把仿真数据实时传给Verdi这也是“VCS与Verdi联合仿真”最常用、最顺滑的原因。nWave与nTrace要分清Verdi启动后你通常会看到两类窗口nWave是看波形的主窗口nTrace是看源码和原理图的窗口。很多新手搞不清“点哪里看波形”“点哪里看代码”其实就是这两个窗口的切换逻辑没建立起来。想明白这三点后面所有操作你就知道是在给谁下命令、最终结果会显示在哪。这个前置概念比记住100个快捷键都重要。2. 环境准备与第一个Verdi会话2.1 设置环境变量与启动命令无论你用的是公司服务器还是自己的虚拟机第一步都是让终端能找到Verdi的可执行文件和库路径。常见的环境变量配置如下一般会写在~/.bashrc或~/.cshrc里取决于你用的shell# 如果用的是 bash export VERDI_HOME/home/tools/synopsys/verdi # 换成你自己实际安装路径 export PATH$VERDI_HOME/bin:$PATH export LD_LIBRARY_PATH$VERDI_HOME/share/PLI/lib/linux64:$LD_LIBRARY_PATH注意不同版本、不同系统下的PLI路径会有差异一定以你自己机器上$VERDI_HOME/share/PLI目录下的实际结构为准。很多“启动失败”“找不到共享库”的问题九成都是这个路径配错了。配置好之后终端输入verdi就能进入图形界面。但我建议你从一开始就别裸启动而是连文件列表一起带上verdi -f filelist.f -top tb_top -ssf sim.fsdb 这条命令干了三件事读入整个工程文件列表、指定顶层模块、把之前仿真得到的FSDB波形一并加载进来。这样打开以后源码树、波形、模块层次全部就位马上就能开始干活。后面小节的实操也都是在这个模式下展开的。2.2 认识nWave与nTrace两大主窗口Verdi乍一看有点唬人因为窗口多、工具栏多。但你只需要先盯住两个核心区域nWave窗口和nTrace窗口。nWave在界面的右下区域是看波形的地方。默认情况下如果你没有加载任何波形它是一片空白你需要在nTrace的源码窗口里点选信号或者通过“Get Signals”面板添加信号波形才会显示出来。波形窗口下方有缩放、测量、光标工具几十个按钮看着多实际上最常用的就那几个f键全览、z/shiftz缩放、c放光标、x交叉线取值。nTrace在界面的左侧和中部区域是看源码和结构的地方。它有两类视图一类是工程文件树按模块层次展开另一类是源码窗口显示Verilog代码。还有一个容易被忽略但极好用的视图是原理图视图你可以在nTrace工具栏点开SchematicVerdi会根据你的RTL代码自动生成模块例化关系和信号连线对理解跨模块交互非常有帮助。新手最容易犯的错是在nWave里疯狂找“怎么看代码”的按钮。其实逻辑正好反过来——双击nWave里的信号nTrace会自动跳到驱动或使用这个信号的源码行。这两个窗口是联动的不是在各自孤岛里操作。2.3 加载已有波形并添加信号假设你现在只有一份仿真完的FSDB文件没有历史会话那么可以用最简单的方式打开verdi -f filelist.f -top tb_top 界面起来以后打开波形文件# 在Verdi的command line窗口里输入或者用菜单 File - Open Waveform fsdbOpen sim.fsdb但此时波形窗口还是空的因为不知道你想看哪些信号。最常用的添加信号方式有两种在nTrace源码窗口中点选你想要观察的信号名然后右键选择Add to Waveform信号就会出现在nWave里。或者先打开Get Signals面板从模块层次树里勾选所需信号再拖动到波形窗口。有一点要提醒加载FSDB的时候最好连同filelist一起加载不要只打开一个孤零零的波形文件。因为Verdi需要源码工程来把信号名映射到具体代码行如果只开波形你看到的信号没有源码关联双击信号也无法反标定位等于废了一半武功。3. 从仿真到FSDB波形完整联调流程3.1 为什么首推FSDB格式而不是VCD或WLF很多从学校出来的人第一次接触波形文件用的是VCD因为VCS里$dumpvars就能生成简单省事。但等设计规模一大VCD文件体积膨胀得离谱打开慢、加载慢而且Verdi对它的支持不如FSDB精细。FSDB的优势有三个文件体积小、加载速度快、带增量信息。Verdi可以直接读取仿真器共享内存里的数据完成“流式显示”也就是说仿真还在跑波形已经可以边跑边看了这对长仿真定位crash时间点非常有用。VCS配合Verdi时只要在编译阶段加上PLI接口Testbench里用$fsdbDumpvars即可生成FSDB替代VCD基本是无痛的。另外还有人会问Questasim里喜欢用WLF格式能不能用Verdi看答案是可以但一般建议转化成FSDB。Verdi自带vcd2fsdb工具可以把VCD转成FSDB后面我会细说。3.2 Testbench里添加FSDB Dump语句这是联合仿真最核心的一步。在Testbench顶层比如tb_top.sv里加上类似下面的语句initial begin $fsdbDumpfile(dump.fsdb); $fsdbDumpvars(0, tb_top, all); end解释一下三个关键参数$fsdbDumpfile(dump.fsdb)指定波形文件名。$fsdbDumpvars(0, tb_top, all)第一个参数0代表从tb_top这个起点开始递归dump所有层次的信号如果你只想dump某个子模块tb_top.u_dut就把第二个参数改成它同时第一个参数改成对应层级数。all表示包含所有类型信号比如wire、reg、integer等不加的话默认只dump部分类型调试时容易漏信息。注意$fsdbDumpvars的第一参数并不是“0代表全部、1代表当前层”而是从起点模块向下展开的层次数。设成0等同于无限深设成1只dump起点模块自身。如果你只想看某个子模块正确写法是$fsdbDumpvars(0, tb_top.u_dut, all)起点变了层次自然跟着变。如果你用的是VCS做仿真器编译命令需要加上PLI接口选项这样$fsdbDumpvars才能被识别vcs -f filelist.f \ -debug_accessall \ -fsdb \ -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ -o simv注意两点第一-debug_accessall是为了让VCS生成足够的调试信息否则某些内部信号无法回溯第二-P后面的.tab和.a文件路径必须与你的VCS版本、操作系统匹配。如果你用的是Questa或XceliumPLI库路径会不同但整体思想是一样的告诉仿真器去哪里找Verdi的PLI接口。编译通过后运行./simv仿真结束后目录下就会生成dump.fsdb接下来就可以verdi -f filelist.f -top tb_top -ssf dump.fsdb 进入调试。3.3 实时打开仿真没跑完就能看波形很多时候我们并不想等仿真全部跑完再打开波形尤其遇到长时间仿真crash的情况。Verdi和VCS联合仿真支持边仿边看也就是业界常说的“流式调试”。做法是在运行仿真时直接打开Verdi并加载共享内存里的波形verdi -f filelist.f -top tb_top -ssf dump.fsdb 关键就在这个-ssf参数。它有两种用法如果dump.fsdb已经在磁盘上它会读文件如果仿真器正在运行并占用这个FSDB文件Verdi会以流式模式连接波形窗口实时更新不需要等仿真结束。实测下来对于跑几个小时的回归用例这种模式可以在仿真中途就把信号拉出来看定位问题的时间大大提前。需要注意一点流式模式下不要对波形文件做“另存为”或“重新加载”操作否则可能中断连接。看到想深挖的信号直接在nWave里操作就好不需要重新打开文件。3.4 没有Verdi PLI环境时怎么处理实际工作中有时候你手里只有VCD文件或者仿真器不支持$fsdbDumpvars这时候别慌。Verdi自带转换工具vcd2fsdb用法很简单vcd2fsdb dump.vcd dump.fsdb转换完成后直接用Verdi打开dump.fsdb即可。不过VCD本身不含Verdi的一些扩展信息比如某些性能分析数据所以如果条件允许还是优先在Testbench里直接生成FSDB。如果你的仿真器是Questa也可以编译Verdi的Questa PLI库Testbench里同样用$fsdbDumpvars。路径通常在$VERDI_HOME/share/PLI/QUESTA或$VERDI_HOME/share/PLI/MODELSIM下具体参考安装目录说明。核心思路始终不变让仿真器在dump数据时能调用Verdi的接口函数。4. 用Verdi定位一个RTL问题的完整实操4.1 从波形异常反推到源码行下面我用一个最常见的调试场景来演示全流程。假设你写了一个简单的AXI-Lite从机模块读寄存器的时候数据总是不对。仿真结束后打开波形第一步先在nWave里把读数据通道的rdata信号加进来。正常情况下它应该等于寄存器里写进去的值但波形上看到的却是32h0前后不一致。这时先不要急着改代码用波形窗口里的光标工具把时间光标拖到首个异常数据的位置观察rdata变化沿附近raddr、arready、rvalid这些信号到底是什么状态。你会发现有可能raddr指向的地址在那一拍确实没有实际寄存器对应也有可能是状态机早走了一拍导致读使能拉高时数据总线还没准备好。看到这个疑点后在nWave波形窗口双击rdata信号nTrace源码窗口会自动跳到驱动rdata的那段assign语句或always块。你就能顺着这条线检查地址译码逻辑里是不是出现了地址区间重叠或者寄存器写模块的更新时序比读模块晚了一个时钟周期。这个“先看波形异常再锁定时间点然后双击反标代码”的闭环流程是Verdi最核心的使用方法没有之一。多数调试工作80%都是在这个循环里完成的。4.2 状态机视图让跳转逻辑原形毕露如果你调试的是控制逻辑比如总线仲裁、协议处理这类模块光看波形上一堆valid/ready信号来回翻效率其实很低。Verdi的**状态机视图FSM Viewer**在这里能救命。在nTrace源码窗口打开一个包含状态机描述的always块工具栏上会有一个类似“状态转移图”的图标点击后Verdi会自动识别parameter定义的各个状态并把状态跳转条件以图形方式画出来。而且这个图是和仿真波形联动的当你把仿真时间光标拖到某个时刻状态机图上会高亮当前所处状态你就能直观看到“哦原来它在这里多停留了一个周期因为req没有拉高”。我第一次用这个功能排查一个死锁问题的时候真香得不行。状态机有十几个状态光看代码推演了半天没想明白后来打开状态机视图把时间轴一拖发现有两个状态之间没有任何跳转路径信号组合逻辑给不出有效条件直接一眼定位。这个功能对有限状态机的调试价值大于背十页快捷键文档。4.3 原理图视图与Trace功能追踪信号的驱动链还有一种情况信号在波形上看确实不对但双击反标之后发现直接驱动它的是一个组合逻辑表达式里面还有一堆别的信号参与。这时候你需要的是Trace功能——也就是沿着信号的前级驱动链一级一级往上追。在nTrace源码窗口选中某个信号右键选择Trace或按下对应的Trace按钮Verdi会以图形化方式展开这个信号的完整驱动链从寄存器输出、经过组合逻辑、再到上游模块的输出端口每一级都显示出来。你可以在原理图视图里点任意一级再跳到对应源码行。这个过程就像“跟着水管找漏水点”非常符合直觉。配合这个功能日常最实用的操作是在原理图里点击一根网络nWave里就高亮对应信号。这样当你在波形窗口观察到一段毛刺切到原理图视图里看清这段毛刺是从哪个模块传过来的定位跨模块问题十分高效。5. 高频操作与效率提升快捷键、命令行与脚本5.1 波形窗口快捷键清单Verdi里快捷键很多但我不建议一开始就背全表。真正高频到“不用就不会干活”的其实就那么十几个整理如下操作目的快捷键/操作说明波形全部显示f当前窗口所有波形适配视图放大波形shiftz或Ctrl滚轮上以光标为中心放大缩小波形z或Ctrl滚轮下以光标为中心缩小放置光标c在波形上放置一个时间光标查看光标处信号值x显示所有信号在此时刻的数值跳到下一个变化沿d顺序查找跳变点跳到上一个变化沿D反向查找跳变点移动信号上下位置拖拽信号名直接拖比重新add快删除信号选中信号名按Delete批量选中可一次删除在源码中反标双击信号名跳转nTrace源码对应行查找信号g或Ctrlf在工程/当前窗口搜索信号名数值进制切换右键信号 - Radix二/八/十/十六进制切换我在实际调试过程中最常用的组合就是f全览 -c放光标 -x看值 -d跳沿。这四个键串起来基本可以解决90%的波形分析需求。其他花哨的按钮如果你暂时不知道用途放着不管也不影响。5.2 常用启动参数与命令行选项除了GUI点选Verdi的启动命令行参数非常值得花点时间记住因为它们能让你一打开就是“可干活”的状态verdi -f filelist.f -top tb_top -ssf dump.fsdb -sv -nologo -f filelist.f加载文件列表可以是Verilog或SystemVerilog代码。-top tb_top指定设计顶层nTrace里的层次树才能正确展开。-ssf dump.fsdb同时加载FSDB波形。-sv启用SystemVerilog语法支持代码里用了interface、class、assert就必须加。-nologo跳过欢迎页。-2001按Verilog-2001标准解析老代码兼容性更高。另外Verdi内部还有一个命令行输入框通常在界面下方的“Command”区域支持类似open、fsdbOpen、add这类操作。用熟了以后很多GUI点选都可以用命令行代替效率提升非常明显。5.3 用脚本实现批量加信号与自动化检查调试大模块时手动在几十个模块里一个个勾选信号人很容易崩溃。Verdi支持Tcl脚本可以把重复性操作批量化了。比如我想把u_dut内部所有axi_*信号一次性加到波形窗口可以在命令行里执行open filelist.f open top tb_top open fsdb dump.fsdb waSetScope -scope tb_top.u_dut -win Waveform2 signalListFind -scope tb_top.u_dut -filter axi_* -win Waveform2 waAddSignal -win Waveform2这几行命令连起来会打开工程、打开波形、定位到子模块、筛选出所有axi_开头的信号并加到波形窗口。等你在界面里手动重复了三次以上的操作就值得停下来想想能不能写成脚本。Verdi还有一个非常实用的功能叫做“Powerful Find”你可以用它搜索信号名、实例路径甚至注释文本在大工程里找信号比在几十个文件夹里乱翻快一个量级。快捷键是Ctrlf建议在任何窗口都先按一下试试。6. 我实际踩过的坑以及几个建议6.1 波形文件生成了但Verdi里信号是空的这种情况很常见八成是层次设置错了。有一次我帮同事看问题他信誓旦旦说Testbench里已经加了$fsdbDumpvars(0, tb_top, all)但打开波形就是看不到某个子模块的信号。我一看他的代码原来他在文件中间写了两条$fsdbDumpfile第二条把第一条的覆盖了而$fsdbDumpvars挂的是第一条的上下文。这个错误很隐蔽因为编译不报错仿真也不报错就是波形信号丢失。另外还要检查$fsdbDumpvars第一个参数是不是真的足够大。如果你指定了某个子模块但它内部还有多层实例0的递归深度才保险写成1或者2就会漏掉深层信号。6.2 打开Verdi后界面卡死或加载慢如果你的设计很大而filelist里包含了所有仿真库文件Verdi启动时会花很长时间做编译和elaboration。我建议把Verdi运行时不需要的库文件从filelist里摘出去只保留RTL和Testbench。像工艺库、标准单元库的仿真模型在Verdi里通常用不到却会让启动慢得让人怀疑人生。如果加载FSDB本身很慢也可以确认一下是不是用了流式模式而FSDB在持续增大。流式模式下Verdi要对增量数据保持实时刷新如果仿真进程没停界面会一直处于高负载状态这时候并不代表Verdi卡死而是它在等仿真产生新数据。6.3 不要用$display大海捞针用波形直接看新人调试的时候特别喜欢在Testbench里加一堆$display打印跑完仿真在终端里翻日志。这个方法不是完全没用但在大型设计里效率极低而且print语句本身会影响仿真性能。我的习惯是先跑仿真生成FSDB再用Verdi看波形——定位一个信号为什么异常比从几万行日志里搜一个打印值要直观得多。如果你确实需要在仿真时观察某个关键点与其疯狂$display不如在波形里多dump几个中间信号然后用Verdi的搜索功能找到你关心的那个时间点。一套组合拳下来调试速度和准确性都上了一个台阶。6.4 关于授权和版本兼容性的一点提醒使用Verdi前确认你的授权文件路径配置正确环境变量里LM_LICENSE_FILE或SNPSLMD_LICENSE_FILE要指向有效的授权文件。启动时如果报“license”相关错误先检查环境变量再看授权服务是否启动。另外Verdi版本与VCS版本之间偶尔会有兼容性问题尽量选择同一套工具链发布批次对应的版本否则PLI编译阶段容易报接口不匹配。不同版本下share/PLI/里的目录名、库文件名会有变化换成新版本后旧编译命令里的路径记得同步更新。最后再分享一个我个人的练手方法。学Verdi最忌讳的就是光看不练你可以随便写个小模块比如一个8位计数器或一个UART接收机然后在Testbench里故意少写一个复位条件或者把一个比较器的等号改成不等号制造一个明显的功能错误。接下来用Verdi打开波形按我上面说的流程走一遍加信号、放光标、双击反标源码、用Trace追驱动链把这个bug揪出来。这样完整跑两三次之后你对Verdi的整个调试闭环就会有肌肉记忆以后再遇到复杂问题就不会慌着翻教程了。这个工具的难点从来不是按钮而是你有没有建立起“从波形反推代码执行逻辑”的调试思维方式。