ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Verdi基础教程:从FSDB波形生成到数字IC验证调试实战

Verdi基础教程:从FSDB波形生成到数字IC验证调试实战 作为一个常年泡在数字IC验证环境里的工程师我每天打交道最多的工具除了VCS就是Verdi。可以说VCS负责跑仿真Verdi负责让仿真结果变得“看得懂”。很多人刚开始接触验证时习惯用$display打log来排查问题确实能解决一部分问题但一旦涉及到跨时钟域的握手、FIFO的读写竞争、状态机的异常跳转光靠文本log基本就是大海捞针。这个标题下的【文档视频】Verdi基础教程刚好把新手最需要的东西都覆盖了从FSDB波形生成到Verdi界面操作再到联合仿真调试技巧。我结合自己从零开始踩坑的经历把这套东西完整盘一遍希望能帮正在学验证或者刚转行做数字IC的朋友少走弯路。这篇内容适合几类人看刚入门数字验证、想系统学习Verdi使用的在校学生做FPGA原型验证、习惯用Vivado或者Quartus自带波形工具、想切换到行业主流Verdi流程的工程师以及已经用Verdi做日常调试、但想了解更深层功能和排查技巧的初级验证工程师。我尽量把原理和实操都写清楚大家可以跟着步骤直接在自己的环境里试。1. 为什么在验证调试里绕不开Verdi1.1 Verdi在数字IC验证流程中的定位数字IC前端验证的日常说白了就是“搭testbench、跑仿真、查波形、找bug、改代码、再跑仿真”的循环。在这个循环里VCS负责把SystemVerilog代码编译成仿真可执行文件然后跑出结果而Verdi则负责把仿真过程中产生的信号变化直观地呈现出来。它不仅仅是看波形的工具更是一个集源码分析、波形调试、覆盖率收集、断言调试于一体的EDA调试环境。我常跟组里新人打比方VCS相当于“现场记录仪”把芯片内部所有信号在时间轴上的翻转都记录下来Verdi则是“高清回放系统”你可以随时暂停、放大、搜索、对比甚至从波形反查到RTL代码的具体行定位到是哪个信号先出现了异常值再顺着驱动关系找到bug源头。这种“波形反查源码”的能力是Verdi区别于普通文本调试的核心价值。实际工作中我还依赖Verdi做两件很重要的事一是层次化追踪信号比如一个总线信号跑到某个子模块之后变成了什么名字、被哪些逻辑驱动Verdi可以沿着例化层次一层层点下去二是跟UVM验证方法学结合查看sequence、driver、monitor之间的transaction传输过程这大大提高了排查协议类bug的效率。可以说在ASIC和SoC的验证环境里后端讨论综合和时序用PrimeTime前端讨论功能和异常用Verdi几乎是一种行业默认。1.2 Verdi和VCS是怎么配合的VCS和Verdi同属Synopsys工具链但二者定位明确VCS负责仿真计算Verdi负责调试分析。它们之间通过一种中间文件格式来“对话”最常见的就是FSDBFast Signal Database格式。仿真过程中VCS通过PLIProgramming Language Interface机制调用Verdi安装目录下的库文件把指定层次的信号变化周期性地写入FSDB文件。我最初理解这个流程时总以为Verdi是直接挂在VCS上的一个插件后来才搞清楚用户只要在testbench里写好dump波形的系统任务VCS编译时加载对应PLI库仿真运行后就会自动生成.fsdb文件之后再用Verdi打开这个文件就行。整个环节不需要Verdi一直跑在后台所以仿真环境相对独立。从这个角度看Verdi和VCS更像“上下游关系”而不是“父子进程关系”。如果你用的是直接支持FSDB的VCS版本在编译时只需要加一个-fsdb选项系统就会自动关联Verdi的PLI库非常方便。老版本或者特殊环境可能需要手动指定-P参数指向novas.tab和pli.a文件这部分我会在第2节的实操中详细说明。记住一条原则凡是波形文件生成有问题先检查PLI库是否加载成功、FSDB系统任务是否被正确编译进去这两个点覆盖了80%的故障原因。1.3 为什么是FSDB而不是VCD很多从FPGA开发转过来的朋友会问Vivado能导VCDQuartus也能导VCD为什么验证环境里都在用FSDB这个问题很实际。VCDValue Change Dump是IEEE标准波形格式所有仿真器都支持但它有两个硬伤一是文件体积非常大一段几百微秒的复杂SoC仿真VCD能轻松膨胀到几十GB磁盘和IO直接成为瓶颈二是没有层次和信号编码优化加载到波形工具里时渲染速度被文件大小拖累。FSDB是Novas公司后来被Synopsys收购设计的专有格式写入时采用增量压缩和信号编码文件体积通常比VCD小一个数量级。更关键的是FSDB保留了Verilog仿真的层次信息Verdi可以在打开FSDB后直接标记出每个信号属于哪个模块例化、具体在源码的哪一行这种“波形和源码双向关联”的体验是VCD做不到的。我曾测试过一个中型IP验证环境同样的仿真VCD生成8GBFSDB生成不到800MBVerdi加载FSDB几乎秒开而加载VCD要卡好几分钟。所以在项目时间紧张的情况下选择FSDB不是“偏好问题”而是“可行性问题”。Verdi也支持直接打开VCD和EVCD文件方便兼容其他流程但主推方案一定是FSDB。2. 从VCS仿真到Verdi开波形的标准流程2.1 一套可以直接跑的仿真例子先说一套最小可运行的实验环境。假设我有一个简单的FIFO设计fifo.svtestbench是fifo_tb.sv要在这个testbench里把顶层所有信号都dump出来标准写法如下module top_tb; reg clk; reg rst_n; reg push_valid; reg [7:0] push_data; reg pop_valid; wire [7:0] pop_data; wire fifo_full; wire fifo_empty; fifo u_fifo ( .clk (clk), .rst_n (rst_n), .push_valid (push_valid), .push_data (push_data), .pop_valid (pop_valid), .pop_data (pop_data), .fifo_full (fifo_full), .fifo_empty (fifo_empty) ); initial begin $fsdbDumpfile(fifo_tb.fsdb); $fsdbDumpvars(0, top_tb, all); end // 生成时钟和复位、注入激励、检查结果... endmodule第二步是编译和运行。在终端里执行vcs -sverilog -debug_accessall -fsdb \ -f filelist.f \ -o simv ./simv如果你的VCS版本和Verdi版本来自同一套工具链-fsdb选项会在编译时自动找到Verdi安装路径下的PLI库。如果环境比较特殊需要手动指定库位置可以这样写vcs -sverilog -debug_accessall \ -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab \ $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a \ -f filelist.f \ -o simv$VERDI_HOME是环境变量通常指向Verdi的安装目录。这里我用-debug_accessall是为了保证Verdi能采集到完整的信号连接关系尤其是在UVM环境里层次调用信息非常关键。仿真跑完后会生成fifo_tb.fsdb。接着用Verdi打开波形verdi -f filelist.f -ssf fifo_tb.fsdb -ssf表示指定FSDB文件打开后Verdi会自动加载该波形文件并同时显示RTL源码窗口nTrace和波形窗口nWave。这一步如果成功说明从仿真到调试的链路已经通了。2.2 fsdb dump的三种常用姿势第一种是上面示例里的“全量dump”用$fsdbDumpvars(0, top_tb, all)。第一个参数0表示从当前作用域往下所有层次全部记录all表示强制记录所有信号包括那些被综合工具优化掉了的中间信号。优点是信息全缺点是文件大、仿真速度有损耗。小型模块或算法验证阶段用这种方式最省心。第二种是“局部dump”把第一个参数改成从1开始并指定目标层次。比如只想记录DUT内部某个子模块的信号initial begin $fsdbDumpfile(fifo_tb.fsdb); $fsdbDumpvars(1, top_tb.u_fifo.rd_ptr_gen, all); end这种方式的优势非常明显文件小、仿真速度影响小、加载快。我在做大型SoC验证时几乎不会全量dump整个芯片而是先根据log定位到大致问题区域再针对性dump相关子模块。实际工程里这叫“定向波形采集”比无脑全量dump高效得多。第三种是“时间段控制”。有些场景下前100微秒的复位初始化阶段没有问题只有到了某个特定testcase的后半段才出现异常。如果全量记录浪费资源。这时可以用$fsdbDumpoff和$fsdbDumpon控制波形采集区间initial begin $fsdbDumpfile(fifo_tb.fsdb); $fsdbDumpvars(0, top_tb, all); $fsdbDumpoff; // 先关 #1000; $fsdbDumpon; // 到时间再开 end补充一个我每次跑长仿真都会用的技巧在预期仿真结束前加一句$fsdbDumpflush;。仿真进程如果因为异常crashFSDB缓冲区里的数据可能来不及写入文件导致波形不完整。手动flush一下可以在关键节点强制把当前缓冲区的数据刷到磁盘。如果做的是长时间回归我一般会在每个testcase的末尾都加flush宁可损失一点点时间也不让几小时的仿真因为最后几微秒没dump而白跑。2.3 控制dump范围的参数实战很多资料只讲了$fsdbDumpvars的标准用法但实际项目里经常需要灵活控制。有几个任务用得好能省不少功夫。$fsdbDumpvarsByInst可以针对指定模块的多个实例单独dump适合只关心某些特定例化层次时的场景。举个例子我检查一个DUT中两个完全相同的通道chn0和chn1但怀疑它们行为不一致就可以只dump这两个实例initial begin $fsdbDumpfile(channel_compare.fsdb); $fsdbDumpvars(0, top_tb, all); $fsdbDumpvarsByInst(0, top_tb.u_dut.u_chn0, top_tb.u_dut.u_chn1); end$fsdbAutoSwitchDumpfile用来做文件大小控制。长仿真时FSDB文件很容易突破几十GB单文件过大不仅占磁盘加载也慢。这个任务可以设置文件大小上限超过后自动切换到新文件initial begin $fsdbDumpfile(auto.fsdb); $fsdbDumpvars(0, top_tb, all); // 每个文件最大1GB最多保留20个文件 $fsdbAutoSwitchDumpfile(1024, auto.fsdb, 20); end这个功能在跑几天几夜的SoC级仿真时特别有用。另外还有$fsdbDumpMemory它可以dump出仿真过程中内存的分配信息在做固件相关调试或CPU核验证时会用到。总之dump波形不是“写两行代码”就完事理解每个参数的行为边界才能在实际调试中游刃有余。2.4 一套可以直接抄的seed回归配置验证工程师经常需要跑大批量随机种子仿真。如果每个seed都dump完整波形磁盘和IO必然爆炸。我常用的策略是默认seed回归时只dump信号变化级别较高的接口信号模式包括下面几个要点。先写一个统一的dump_common.svh文件把常见的dump变量封装成宏或者函数define DUMP_ALL \ initial begin \ $fsdbDumpfile($sformatf(sim_%0d.fsdb, SEED_NUM)); \ $fsdbDumpvars(0, test_top, all); \ end然后在每个testcase里用不同的宏来控制是否详细dump。具体执行时我会在Makefile里通过defineSEED_NUM$(SEED)传给仿真器。这样做的好处是每个并行跑的仿真任务生成的FSDB文件名都不同不会互相覆盖并且可以通过脚本随时动态决定保留哪些波形。回归失败后我会先看日志里报错的seed号然后手动重跑那个seed并把dump宏切换成全量模式重新生成一份详细的FSDB来分析。这是一种“低成本全量覆盖高价值定向捕获”的实用策略。3. 打开波形之后Verdi高频操作实战3.1 nWave的界面布局和信号操作基础启动Verdi后默认打开的是nTrace源码窗口和nWave波形窗口。很多新手一上来就被满屏信号吓到其实掌握核心操作逻辑后nWave非常顺手。添加信号的方式有很多。在nTrace里选中信号名右键选择Add To Waveform即可也可以按快捷键CtrlW。如果你已经从FSDB里获得了整个设计层次可以直接在左边“Hierarchy”树里展开模块选中多个信号批量添加。另一个非常高效的方式是在nWave窗口下方直接输入信号名会自动匹配并添加。添加完信号后调整视角的快捷键极为重要操作快捷键效果放大Z或Shift滚轮以鼠标为中心放大波形缩小z或Ctrl滚动缩小波形全览f适配所有波形到当前窗口测量区间按A、B添加两个游标显示时间差查找信号CtrlF搜索信号名或数值窗口里右键还能调出数字显示进制二进制、十六进制、十进制、无符号/有符号这在分析总线信号时非常关键。比如地址总线axi_awaddr用十六进制读一目了然而状态机的状态编码则用二进制更直观。信号多的时候nWave支持分组。选中几条相关信号右键Group可以折叠成一个总线组也可以给组改名字、统一换色。我在分析AXI协议时序时通常会把AW通道、W通道、B通道的信号分成三组能极大提高可读性。3.2 快速定位信号驱动关系与对比模式Verdi最强大的一个功能是在nTrace源码窗口里点击一个信号后该信号在RTL中的所有驱动点和负载点会被高亮显示。比如点击push_valid源码里所有给push_valid赋值的always块、assign语句以及所有读取它的地方都会闪亮标记出来。配合左侧的“Driver/Load”面板还能看到它的上一级驱动来自哪个信号、下一级驱动了哪些信号。实际操作中我定位bug最常用的路径是先在nWave里看到某个信号在某个时刻出现了异常值比如应该是递增的计数器突然跳到0然后右键该信号选择Show SourceVerdi会立刻跳转到驱动该信号的RTL代码行。接着我在源码里继续点击相关信号查看它的驱动条件不断追源头直到找到是哪个状态、哪个条件没有约束住。另一种我是强烈推荐的功能是波形对比模式Compare Mode。当你有两份FSDB比如一份是修改前的代码跑出来的一份是修改后的代码跑出来的可以在nWave中同时打开然后选择Tools - Compare - Set/Show Comparison。Verdi会自动对比两份波形中同一信号的差异并用不同颜色标出不一致的区域。这就不用肉眼一条条对对排查代码改动是否导致预期行为变化非常有帮助。这里分享一个我自己的使用习惯打开Verdi时我不只是看FSDB还会打开对应的源码文件和UVM的log文件。Verdi可以加载仿真log让波形、源码、日志三者联动。当我看到log里一条报错信息时直接点击时间戳波形会自动跳到对应时间点。这个功能对时序类问题尤其好用。3.3 总线分析和UVM层次的应用技巧分析总线协议时nWave最有价值的功能之一是“总线解析”。以AXI为例你可以选中整组awaddr、awlen、awsize等信号在nWave里右键设置Radix和Bus属性。更进阶的玩法是使用“Bus Operation”把一组握手信号组合成事务级的事件类似于直观地标出每一次读/写传输的开始和结束。对于UVM环境树形层次窗口默认会显示uvm_top下面的test、env、agent等UVM组件层次。在仿真时加上UVM_VERDI_TRACEVerdi还可以在波形里显示UVM的工厂机制、sequence执行过程。我刚开始学UVM时总搞不清sequence里的item是在哪一个driver那里被消费掉的。后来用Verdi打开UVM trace在波形上直接看到uvm_sequence_item的创建、传递和驱动过程一下子就把整个UVM执行流看明白了。这个功能对应的做法是在VCS编译时加上defineUVM_VERDI_TRACE或者更精细地指定dump内容initial begin $fsdbDumpvars(0, top_tb, all, uvm_verdi_trace); end这么操作后打开FSDB你会在nWave里看到UVM组件之间的TLM通信事件比如analysis port write、sequencer item started等。这对调试sequence-driver之间的时序问题非常有效。3.4 我常用的几个自定义配置用久了Verdi会有一些个人的“手癖”。第一个是把信号名字段和数值段分开显示这样能更清晰地观察数值变化。第二个是调整波形的背景色和信号颜色降低长时间盯屏的疲劳感我自己习惯把关键的控制信号设成亮黄色时钟设成灰色数据总线设成绿色。第三个配置尤其实用打开Verdi时会在当前目录下自动生成verdiLog、verdi.el这些文件。Verdi会把打开过的源码文件列表、波形配置文件、窗口布局等信息保存在这些文件里下次启动时通过-restore选项可以恢复上次的界面状态verdi -f filelist.f -ssf fifo_tb.fsdb -restore 如果你的工程有大量source文件和复杂的波形查看设置用-restore能省掉重复拖窗口、加信号的麻烦。另外我还会把nWave默认显示的时长轴单位改为微秒或纳秒在长仿真中看起来更舒服。这些配置都可以在5分钟之内做好但带来的舒适感能持续整个项目周期。4. 常见问题与排查技巧实录4.1 fsdb文件没生成或者生成后是空的新手最容易踩的坑就是$fsdbDumpfile和$fsdbDumpvars写了但编译时忘了加-fsdb选项或者PLI库路径没配好。如果仿真后根本找不到fsdb文件优先检查编译命令有没有带-fsdb以及环境变量$VERDI_HOME是否指向正确的安装路径。如果fsdb文件存在但打开后波形窗口里什么都没有常见原因有三个第一$fsdbDumpvars的第一个参数写成了非0整数且指定层次的名字拼写错误导致什么都没存进去第二仿真时间太短没有触发到信号变化波形看起来是空的第三仿真进程在结束前被强制kill没有正常flush缓冲区。针对第三种情况除了前面讲到的$fsdbDumpflush还可以在testbench的$finish之前增加一个小的repeat(10) (posedge clk);延时给FSDB足够的收尾时间。我遇到过很多次仿真任务因为超时被杀掉最后保存的波形只有几十纳秒数据加了flush之后就好很多。4.2 打开波形后信号全是X或者Z信号显示成X未知或Z高阻时不要第一反应是Verdi出了问题。绝大多数情况是RTL代码本身就有问题X状态是仿真器对未初始化寄存器或组合逻辑回环的诚实表达。但有一种常见情况跟dump设置有关如果你在dump时没有加all部分被综合工具优化掉的内部节点可能不会被记录打开波形时看到的是断断续续的Z状态。这种问题通常在全编译优化等级较高时出现。解决方式是补充all参数重新仿真。另外做FPGA原型验证的朋友可能会遇到一种现象在Vivado里仿真一切正常到了VCSVerdi流程里全是X。这通常跟VCS的初始化和优化选项有关可以尝试在编译时增加-debug_accessall避免VCS在编译阶段对某些信号做了过度优化。顺便说一句X态分析是验证里很重要的一门手艺Verdi本身也提供了X态追踪工具可以从X信号源头往前推帮你在复杂的组合逻辑里找到X的传播路径。4.3 fsdb文件过大磁盘爆炸或者加载缓慢文件过大几乎每个项目都会遇到。我的处理思路是从“源头削减、过程切换、事后清理”三个方向同时下手。源头削减指的是在testbench里限制dump层次只保存DUT的关键子模块而不是全芯片。过程切换是使用$fsdbAutoSwitchDumpfile让文件超过设定大小后自动切换新文件避免单个文件过大导致打开卡顿。事后清理则是写一个脚本定期清理历史回归中不再需要的FSDB文件。还有个小技巧如果部分信号高频翻转比如时钟、计数器你可以只dump它们的“变化时刻”而不用记录每一个仿真时间点。这在FSDB里可以通过信号过滤设置实现。Verdi的nWave里也有降采样显示Decimation功能加载大文件后可以通过它加速显示虽然这并不会减少文件本身的大小但至少能让你把波形查完。4.4 Verdi打开后源码和波形对不上有时仿真结束后你又改了RTL代码再打开旧的FSDB时Verdi可能会提示源码版本和波形不匹配或者某些信号找不到。这种问题没有特别优雅的解法原因也很简单FSDB记录的是旧版本的仿真结果源码已经是新版本了。我建议的做法是重要的波形文件跟环境变量、编译脚本、源码提交号一起归档。用版本管理工具比如Git给每次仿真打上标签再配合脚本自动命名fsdb文件名例如fifo_tb_${BUILD_ID}.fsdb。这样即使过了几周你回头查旧波形时依然能精确复现当时的源码环境。如果只是临时看一天两天重新仿真一遍也很快别在旧波形上纠结太久。其实这里还牵扯到一个使用习惯就是source文件的全局搜索问题。在Verdi里按CtrlShiftF可以搜索整个工程的信号名和模块名但如果工程路径配置不对搜索结果可能不完整。我一般用filelist.f方式管理源文件配合相对路径保证Verdi在任何工作目录下启动都能正确定位源码。5. 新手怎么学Verdi才比较快5.1 不要从“看手册”开始先跑通一个小demo很多人学习工具的习惯是“先找一本手册从头看到尾”。这个方法在Verdi这里效率很低。Verdi的官方文档确实全面但没有实际波形在手边看很多概念是抽象的。相较之下我建议花二十分钟搭建一个最简单的testbench比如计数器或者FIFO然后跑一遍完整的VCSVerdi流程。当第一次在自己屏幕上看到信号波形随仿真时间往前滚动时工具的整体框架就已经建立起来了。接下来要做的就是在实际调试中去“用”。不用刻意记忆快捷键只要你每天用nWave看波形两周下来常用的几个快捷键自然就记住了。真正需要重点理解的还是FSDB生成机制、dump范围控制、层次化追踪这几个核心概念。这些掌握了你换任何版本的Verdi都不会陌生。如果只打算看一份资料推荐先看官方Training里基础的“nWave Basic Usage”部分再配合视频快速过一遍。现在网络上也能搜到很多Verdi基础教程的视频质量参差不齐但至少能帮你建立“波形窗口和源码窗口如何联动”的直观印象。我自己带新人的时候还会让他们自己造一个故意含bug的小模块用Verdi把bug找出来。用一个真实的问题驱动学习比单纯看教程有效得多。5.2 文档视频怎么配合才高效这个标题叫“文档视频”基础教程我觉得这两者各有不可替代的作用。视频适合第一次接触时看能清晰展示鼠标点击、窗口布局、命令执行的过程比屏幕上满屏的文字更容易接受。但它有个缺点节奏通常比较慢跳着看容易遗漏关键细节。文档则适合作为“工具书”来查比如你想确认某个$fsdbDumpvars参数的含义文档里一句话就能说清但你不太可能用视频去按帧找。我的建议是两轮学习法。第一轮看完视频里的主流程跟着操作一遍目标是跑通demo第二轮做一个小项目来练手遇到不懂的再去看文档对应章节。举个例子你想弄懂“为什么$fsdbDumpvars(0, top_tb, all)能dump所有信号”就在文档里搜fsdbDumpvars和all看解释再回demo里改参数对比结果。这样一轮下来知识点就会变成自己的而不仅仅是“看过”。另外要提醒一句现在网上有很多所谓“绿色版”“一键破解版”的工具我劝大家别碰。EDA工具牵扯的版权风险很大而且这类非正规安装包往往缺少完整的功能库出现兼容性bug时你很难判断是环境问题还是自己的脚本问题。学生可以申请学校里统一安装的版本工作中直接使用公司标准环境踏踏实实搞技术才是正道。6. 额外再分享两个让我效率翻倍的技巧第一个是善用filelist.f和脚本自动化。我们在工程里不只用一个tb文件Verdi加载源码时如果一个个指定文件非常低效。统一用一个filelist.f列出所有RTL和tb文件然后用-f filelist.f加载又快又不容易出错。配合Makefile任何仿真、打开波形、清理日志的操作都能一行命令搞定。我现在的环境里make sim和make verdi已经是肌肉记忆了。第二个技巧是坚持“分层查看”。遇到一个仿真失败先看log再决定打开谁的波形、看哪些信号。不要一上来就把整个芯片的FSDB拉一遍。学会带着问题去查波形而不是在波形里漫无目的地“找东西”能把调试时间缩短一大半。这也是我前面反复强调定向dump的原因。我刚转做验证时总想着把所有信号都放在波形里觉得信息越多越有安全感。后来发现数据太多反而干扰判断。现在我的原则是默认dump范围小一点问题定位到具体模块之后再精确扩大dump范围。先想清楚要验证什么再决定波形记录什么比打开完整波形再层层下钻要高效得多。Verdi这套工具掌握核心操作不难难的是遇到复杂问题时知道该看哪里、怎么从看似杂乱的波形里提炼出有效信息。这需要大量的工程实践积累。希望这篇文章能帮你把基础打牢少踩一点我当年踩过的坑。
返回列表