ARTICLE DETAIL

资讯详情

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

Quartus II波形仿真:从自带仿真器到ModelSim红线排查

Quartus II波形仿真:从自带仿真器到ModelSim红线排查 1. 波形仿真在FPGA开发里的真实定位刚接触Quartus II的人往往会有一个错觉代码写完、引脚分配好、下载进板子能跑就算完事了。但真正做过几个项目之后就会明白能把设计烧进去只是最后一步而把问题在执行之前就摁死才是省时间的关键。波形仿真就是干这件事的。它的本质是把你的Verilog或VHDL代码当成一个黑盒子你在外面给它按时序施加激励信号然后观察输出响应是不是你想要的。整个过程不依赖任何实物板卡改一行代码就能重跑一次这种迭代速度是上板调试完全比不了的。Quartus II作为Altera现Intel的老牌FPGA开发套件它内部集成了两套走仿真路线的方案。一套是自带的Quartus II Simulator用波形矢量文件Vector Waveform File后缀.vwf手绘激励上手门槛低另一套是联合ModelSim-Altera做仿真功能强、波形可读性好但配置环节多一些。对于初学者来说绝大多数教程都会让你先跑通自带仿真器因为不需要额外装工具工程里点几下就能看到波形。我当初学的时候踩过一个特别典型的坑代码逻辑明明在脑子里过了一遍没问题仿真一跑输出波形一大片红色看着像是设计死了。后来才知道在ModelSim的波形窗口里红色代表的是未知态X不是“错误”也不是“报错”它只是在说“这个信号此刻没有确定值”。搞懂颜色的含义之后排查方向就清晰多了反而没那么慌。这篇文章就按我自己的学习路径来写。先讲清楚仿真在整个开发流程里的位置再把工程搭建、激励波形编辑、运行仿真这条主线走一遍最后重点聊聊波形变红这类高频问题的排查思路。内容偏向新手但其中的参数设置和踩坑经验做过几个项目的人回头看也会有收获。2. 动手之前把工程骨架搭对2.1 版本选择与安装时容易忽略的两个点Quartus II的版本迭代非常多从9.x到13.x再到后面的15.x、17.x每个版本的界面和菜单位置都有差别。如果你跟着网上教程走发现菜单找不到十有八九是版本对不上。我的建议是学仿真阶段固定用一个版本13.0sp1是很多老教程和教材的基准版本功能够用资料也最全。如果你已经在用更新的Quartus Prime界面变化不大仿真流程的核心逻辑是一样的只是菜单名字可能有微调。安装环节有两个点特别值得提醒。第一安装路径不要出现中文、空格和特殊符号。这一点看起来是常识但ModelSim这类工具对路径中的空格极其敏感一旦路径里有空格NativeLink调用的时候会直接失败而且报错信息往往很含糊你根本猜不到问题出在路径上。第二安装时如果勾选了ModelSim-Altera要注意它是Starter Edition入门版这个免费版本对可编译的代码行数有上限超过限制会编译失败。学生做课程设计、小型项目完全够用但如果你的设计规模很大仿真时就要留意这个限制。安装完之后还需要做一步授权配置。Quartus II需要license文件才能正常使用完整功能网上能找到对应的配置方法按照官方说明把license路径设好即可。这一步不做的话新建工程时可能直接提示无授权。2.2 新建工程时器件型号到底要不要选对New Project Wizard的流程大家都熟工程名、路径、顶层文件名一路下一步。到了器件选择那一页很多人会犯难我手上没有板子或者板子型号和列表里对不上随便选一个行不行答案是纯功能仿真阶段器件型号影响不大。因为功能仿真验证的是RTL代码的逻辑正确性不涉及具体的时序延迟和器件资源你选一个同系列的器件就能跑。但有一点要注意器件系列尽量和你的目标板卡一致。原因在于不同系列支持的IP核、引脚标准、资源类型不太一样如果你后续要做时序仿真或者真正下载器件型号必须和实物匹配否则引脚分配会出问题。所以养成习惯从一开始就选成目标器件后面少折腾。工程创建完成后把写好的Verilog或VHDL源文件添加进来然后做一次Analysis Elaboration分析并细化。这一步相当于让Quartus先检查一遍语法和基本逻辑结构不做实际综合。它的作用很直接如果代码里有明显的语法错误、模块例化错误这一步就会报出来不用等到仿真才发现。我见过不少人跳过这步直接去点仿真结果仿真器报了一堆看不懂的错其实是代码本身的问题。2.3 顶层文件与测试平台的边界要分清这里有个概念必须掰清楚不然整个仿真流程会糊。你的设计代码比如一个计数器模块是被测对象它需要输入信号才能工作。而给它提供输入信号的那层代码叫测试平台也就是testbench。在Quartus自带仿真器这条路上激励信号是用波形文件画的你不需要手写testbench代码但在ModelSim联合仿真这条路子上你需要写一个testbench文件在里面用代码产生时钟、复位和数据。这两条路最核心的差别就在于激励是画出来的还是写出来的。画波形的方式直观适合组合逻辑和小规模时序逻辑的快速验证写testbench的方式灵活可以产生复杂的随机激励、循环测试适合大设计和自动化回归。新手先从画波形入手理解清楚了激励和响应的关系再去学写testbench过渡会很自然。另外要强调一点testbench文件不参与综合它只是仿真用的辅助代码所以在综合时要把它的属性设成不被综合否则会引入一堆莫名其妙的错误。在Quartus里设置EDA工具时可以把testbench文件标记为仿真专用这一步后面讲配置的时候会细说。3. 用自带仿真器跑通第一条波形3.1 生成功能仿真网表这一步不能省很多人卡在第一步写完代码直接去找运行仿真的按钮发现是灰的点不动。原因就是没有生成功能仿真网表。Quartus II的仿真流程不是直接拿你的源文件跑而是先生成一个专门用于仿真的网表文件再把这个网表丢给仿真器。这个网表叫Functional Simulation Netlist生成它的操作在菜单里是Processing - Generate Functional Simulation Netlist。为什么非要这一步因为仿真器需要的是展开后的、门级或寄存器传输级的结构描述而不是你原始的、带有各种语法糖的源代码。生成网表的过程会做一次逻辑展开把模块层次拉平把信号名整理好。这一步和综合有本质区别综合会做逻辑优化和映射到具体器件功能仿真网表不做这些它保留的是纯粹的逻辑结构所以仿真速度更快结果也更能反映代码本意。这里有个很关键的顺序问题每次修改源代码之后都必须重新生成一次功能仿真网表。我刚开始学的时候就是改了代码直接重跑仿真结果波形还是老的白白怀疑了半天人生。原因是仿真器读的是上一次生成的网表源文件的改动没被同步进去。记住这个顺序改代码、存盘、重新生成网表、重跑仿真。3.2 新建波形矢量文件并添加节点网表生成之后新建一个Vector Waveform File快捷键通常是CtrlShiftW或者从File - New里选。新建出来的是一个空白波形窗口左边是信号名列表右边是波形编辑区。接下来要把需要观察的信号加进来操作是右键左侧空白区选Insert Node or Bus然后点Node Finder。Node Finder这个对话框是Quartus里最常用的工具之一。在Filter下拉框里选Design Entry (all names)或者Post-fitting然后点List中间会列出所有可选信号。这里的技巧是如果你只是做功能仿真选Design Entry (all names)能看到设计里的所有信号包括内部寄存器如果你做时序仿真选Post-fitting看到的是经过布局布线后的信号。初学者做功能仿真选前者。加信号的时候不要一股脑全加。全加进来的后果是波形窗口密密麻麻关键信号淹没在里面。我的做法是先加三类信号时钟、复位、以及你要验证的核心输出。比如做一个LED闪烁的计数器那就加clk、rst_n、cnt内部计数寄存器、led_out。先把这几条波形看明白再逐步加其他信号辅助定位。这个习惯在多模块设计里尤其重要能帮你在几百个信号里快速锁定问题。3.3 用Overwrite功能给时钟和复位画激励信号加进来之后默认都是未初始化的状态画面上看不出什么。接下来要手工编辑输入信号。波形编辑区左侧有一排小图标分别是Overwrite、Overwrite Clock、Invert、Group等用它们来产生激励。最常用的是时钟信号。选中clk这一行点Overwrite Clock图标弹出对话框设置Period周期和Duty Cycle占空比。周期怎么定这要看你设计里的时钟频率。如果你打算让设计跑50MHz那周期就是20ns。当然仿真里不一定非要用真实频率为了看得清楚很多人会用100ns的周期减慢时序方便观察。这里我的经验是仿真时钟周期可以适当放慢但相互之间的时序关系必须和实际一致。比如你的设计依赖某个信号在时钟上升沿前后多少纳秒有效那这个相对关系不能改否则仿真通过上板可能有问题。复位信号用Overwrite图标设置。选中rst_n行点Overwrite把这一段的值改成0或者1。一般做法是开头几个时钟周期让复位拉低有效之后拉高模拟上电复位的时序。这里要注意复位持续的时间太短的话内部寄存器可能还没被复位彻底就已经开始工作了。我的习惯是至少给复位留两到三个时钟周期稳妥一些。输入信号画好之后要设置仿真结束时间。从Edit菜单里选End Time输入一个足够长的值。这个时间怎么估算最笨但最稳的办法是按你设计里最慢的那个时序事件来定。比如一个每秒变化一次的计数器仿真时间至少要设到几秒否则你还没看到它变化就结束了。我一般会先设一个较大的值比如10us跑一次看波形如果还没看到变化再加。3.4 运行仿真与波形结果解读设置好之后点Simulation - Run Functional SimulationQuartus会自动调用仿真器跑完后弹出波形窗口。这时候重点来了怎么看波形。先看时钟确认它是不是按你设定的周期在翻转。如果时钟本身就是一条直线那说明激励设置有问题后面都不用看了。再看复位确认它在开头有一段有效电平之后释放。然后看核心输出它是不是按预期变化了。举一个我实际做过的例子一个简单的按键消抖电路。仿真跑完波形上按键信号抖动的时候输出一直是稳定的抖动结束之后输出才变化说明消抖逻辑生效了。如果输出跟着抖动乱跳那就是消抖的计数器阈值设小了。这种问题在仿真里改一个参数再跑一次前后不超过两分钟但如果上板才发现你得重新综合、布局布线、下载一圈下来十几分钟没了。波形解读的核心思路是建立时序预期。在你的脑子里应该先想好“这个信号在第几个时钟沿应该变成什么值”然后拿仿真波形去对。对不上就顺着信号往回找看是哪一级逻辑出了问题。这种“带预期地看波形”比漫无目的地扫一遍高效太多了。4. 联合ModelSim仿真与波形红线的真相4.1 NativeLink配置的完整步骤当你需要写更复杂的激励或者想用ModelSim更好的波形分析功能时就要走联合仿真的路子。Quartus II通过NativeLink机制自动调用ModelSim配置过程如下。第一步设置EDA工具。从Assignments - Settings进入找到EDA Tool Settings - SimulationTool name选ModelSim-AlteraFormat按你的代码语言选Verilog或VHDL。这里把所有需要编译到仿真库里的文件列出来注意只列设计文件testbench单独在后面配置。第二步配置NativeLink settings。在同一个页面里找到NativeLink settings点Compile test bench后面的Test Benches按钮。弹出对话框后点New填三个关键信息Test bench name测试平台名称、Top level module in test bench测试平台的顶层模块名这个必须和你testbench文件里的module名完全一致大小写都不能错、Simulation run time仿真运行时长。然后点File Name后面的浏览按钮把testbench文件加进去。第三步触发仿真。做完上面两步点Tools - Run Simulation Tool - RTL SimulationQuartus会自动编译、加载并启动ModelSim跑完后弹出波形窗口。配置里最容易出错的就是顶层模块名这一项。我遇到过一个情况testbench文件里写的module名是tb_counter但配置里手滑填成了counter_tb结果仿真一启动就报找不到顶层模块报错信息也很生硬。后来养成习惯配置完先回testbench文件里对着module那一行核对一遍省得浪费时间。4.2 波形显示红色到底在表达什么热词里提到“modelsim仿真波形是红线”这是新手最常遇到的问题没有之一。在ModelSim的波形窗口里信号的颜色是有明确含义的颜色含义典型原因绿色有效的0或1信号被正常驱动红色未知态X信号未初始化、无驱动、或存在冲突驱动黄色高阻态Z三态门未使能、输出悬空蓝色弱信号多驱动中的弱驱动源所以红色不是“错误报警”它是在说这个信号的值目前无法确定。理解这一点之后排查方向就变成了为什么这个信号没有被赋予一个确定的值。最常见的原因有四类。第一类是信号没有驱动源。比如你声明了一个wire类型的信号但忘了给它赋值或者赋值它的模块没有被正确例化那它自然就是X。第二类是复位没有正确作用。寄存器在复位之前保持未知态如果你的复位信号在仿真开始时没拉低或者testbench里根本没给复位激励那寄存器输出就一直是红的。第三类是时钟没有接入。时序逻辑没有时钟就不会更新状态输出停在初始的未知态。第四类是多驱动冲突。同一个信号被两个地方同时驱动或者一个用always块一个又用assign仿真器无法确定该听谁的结果也是X。排查的时候有个屡试不爽的顺序先看时钟再看复位最后看数据。时钟不对后面全白搭复位不对时序逻辑全停在X这两样都没问题再去查数据路径上的逻辑。我自己在实际项目中有一次排查了大半天最后发现是testbench里时钟的周期设成了0仿真器根本没产生翻转所有依赖时钟的寄存器全是红的。这种问题在波形上一眼就能看出来只是当时注意力全放在数据逻辑上了。4.3 testbench里那几个容易写错的细节走ModelSim这条路testbench得手写这里有几个细节值得单独拎出来说。时钟产生。标准的写法是用initial块配合forever或者always块。参数化的写法更优雅定义一个CLK_PERIOD然后always #(CLK_PERIOD/2) clk ~clk。这样改频率只需要改一处。注意initial块里要给clk赋初值不然它从X开始虽然always块很快会把它驱动起来但仿真最开头的几个周期可能是X。复位产生。常见写法是initial块里先让rst_n 0然后用#延迟一段确定的时间再拉高。这段延迟长度是新手容易拍脑袋的地方。我的建议是至少覆盖三个时钟周期并且写在时钟产生逻辑之后保证时钟已经在跑了。如果延迟设得太短比如比一个时钟周期还短有可能复位释放的时刻正好卡在时钟沿附近产生亚稳态一样的效果虽然仿真里不一定报错但逻辑行为会变得难以预测。仿真结束的处理。如果不主动结束仿真会一直跑到你设定的运行时长浪费时间。可以在testbench里加一个initial块用$finish在延迟一段时间后结束。不过注意$finish不能让所有工具都干净退出有时候会留下一些临时文件。用$stop也行但会停在仿真窗口里等你手动点继续。按需选择。5. 踩坑记录与问题速查5.1 高频问题速查表现象可能原因解决方向仿真按钮灰色不可点未生成功能仿真网表先执行Generate Functional Simulation Netlist波形全是红线时钟或复位未加激励检查激励波形确认clk在翻转、rst有动作改了代码仿真结果没变网表未重新生成重新生成网表后再跑找不到信号名信号被综合优化掉加综合属性保留信号或用Post-fitting filterModelSim启动报路径错误安装路径含空格或中文换到纯英文无空格路径顶层模块找不到testbench模块名填错核对testbench文件名与module名仿真时间不够用End Time设太短按最慢时序事件估算适当放大输出一直不变时钟周期设成0或信号被固定检查时钟激励参数5.2 我摸索出来的几条实操心得第一条仿真时间宁大勿小。初学者最常见的浪费就是设了一个刚刚好的仿真时长结果波形跑到一半就停了什么也没看出来。我现在的习惯是先设一个大值比如实际需要的两到三倍跑一次看大致轮廓如果发现需要更长时间再调整。反正跑仿真比上板快得多。第二条关键信号单独拉出来看。波形窗口里信号堆多了眼睛会花。我的做法是把核心输出信号拖到最上面时钟和复位紧随其后中间信号放下面。ModelSim支持给信号分组和重命名把关注的信号单独组一个Group排查的时候一目了然效率提升很明显。第三条状态机的仿真要专门造激励。如果你的设计里有状态机不要只给一个简单的输入就完事。要让输入在所有状态之间跳转一遍观察状态切换是否符合预期。我见过状态机在某个特定输入组合下跳到了一个未定义状态仿真时被自己忽略了上板之后进入死循环才发现。所以状态机的分支覆盖要尽量全。第四条波形文件要保存配置要复用。Vector Waveform File和EDA工具设置都是可以存进工程里的。下次打开工程直接就能重跑仿真不用重新配一遍。尤其是testbench那一堆设置重配一次很费时间做好了就保存下来这是提升效率的小细节。第五条建立自己的testbench模板。写多了之后会发现时钟产生、复位产生、$finish这几段几乎每回都一样。把这些固定成模板新建testbench时直接复制只改被测模块的例化和激励部分能省下大量重复劳动。这是我做了几个项目之后才意识到的早点养成这个习惯前期学习会顺畅很多。6. 从跑通到跑好仿真这门手艺的进阶方向功能仿真跑通之后下一步自然就是时序仿真也叫后仿真。它和功能仿真的最大区别在于时序仿真的网表里包含了布局布线之后的实际延迟信息更能反映真实芯片上的行为。如果你的设计在功能仿真里一切正常时序仿真却出现了建立时间或保持时间违例那说明设计里存在时序问题需要通过约束、流水线或者改变逻辑结构来解决。不过对于初学者我的建议是先把手上的功能仿真彻底玩明白。包括波形编辑器的各种操作、Node Finder的筛选用法、ModelSim波形的颜色含义、testbench的基本结构这些都是基础中的基础。基础打牢了再上时序仿真你会发现很多概念是相通的学起来很快。反过来如果功能仿真都没搞利索就急着做时序仿真一旦遇到问题你可能连是逻辑错还是时序错都分不清排查会非常痛苦。我自己的体会是波形仿真的核心价值不在于“用工具”而在于培养一种“在脑子里面跑时序”的能力。当你看到一个设计能本能地在脑海里勾勒出每个时钟沿上各信号应该是什么状态然后拿仿真去验证这个预期你就真正跨过了FPGA开发的第一道门槛。这道门槛跨过去之后后面无论是写testbench、做时序约束还是上板调试都是在同样的思维方式上叠加工具技能而已。最后分享一个我平时用的小习惯每次仿真之前先在纸上或者注释里写清楚“这次仿真我要验证什么现象预期是什么”。跑完对着预期一条条核对对上了就换个场景继续对不上就顺着波形往回找。这样做仿真不是漫无目的地看波形而是带着明确的目标去验证效率会比瞎跑高很多。仿真跑得多不等于学得好带着问题去跑才是。
返回列表