资讯动态

FPGA波形仿真全攻略:Quartus与ModelSim从入门到实战

发布时间:2026/10/7 1:26:27 来源:尧图企业网站定制
写这篇是因为我自己从Quartus 9.1一路用到现在Quartus Prime系列发现一个很普遍的现象不少人写Verilog、搭工程都挺顺利一到波形仿真就卡壳。要么Modelsim打不开要么仿真出来的波形是一堆红线要么直接不知道该从哪里下手。波形仿真这个环节说难不难但确实有很多约定俗成的操作坑教材上往往一句话带过实际动手就到处碰壁。这篇我就从波形仿真的完整流程讲起覆盖Quartus内置波形编辑器和ModelSim跑testbench两条路线把仿真前要准备什么、仿真时怎么设置激励、仿真后怎么看波形这些问题挨个说清楚。适合正在做数字电路课程设计、FPGA入门自学、或者被“仿真红线”折磨到怀疑人生的同学参考内容尽量做到跟着操作就能跑通不绕弯子。1. 环境准备与版本选型先选对工具再干活1.1 Quartus版本怎么选Lite版、标准版与ModelSim的匹配关系现在说起Quartus市面上流传的版本特别多从老的Quartus II 9.0、9.1、13.0、13.1到新的Quartus Prime 18.1、20.1、25.1名字都不一样。这里先帮大家理清楚Intel原Altera从18.0开始把产品线改成了Quartus Prime又分成Pro版、标准版Standard和精简版Lite三个层级。对我们做课程设计、日常学习来说用Lite版就够了免费、不需要许可证、功能覆盖大部分中低端FPGA比如Cyclone IV、Cyclone 10系列。很多教程还在用Quartus II 13.1配ModelSim-Altera 10.5d原因是很多高校实验箱用的是Cyclone II、Cyclone III这类老器件只有老版本官方才提供完整的器件库支持。如果你手头是这类老实验板装13.1没毛病。但如果你是自己买的学习板基本是Cyclone IV起步那么Quartus Prime Lite 20.1或25.1都是更好的选择——安装包小、启动快、界面比老版本清爽而且不涉及破解问题。仿真工具之间的关系要单独说清楚Quartus自带一个轻量的波形仿真器直接可以生成矢量波形文件适合简单教学。而ModelSim是独立的仿真软件功能更强大支持testbench、支持复杂激励生成。从Quartus Prime 19.1开始Intel官方主推的是Questa-Intel版但ModelSim的使用逻辑和Questa基本一致学ModelSim的操作思路换到Questa也不会陌生。我下面的讲解以最常见的“Quartus Prime ModelSim”组合为例老版本Quartus II 13.1操作路径也差不多只是菜单位置略有差异。1.2 安装ModelSim的几个注意点路径、环境变量、器件库安装这一步如果没装好后面仿真会报出一堆莫名其妙的错误。我踩过最深的坑是路径有中文。Windows用户建文件夹时习惯写“新建文件夹”结果ModelSim一运行就提示找不到工作库怎么改都改不对。强烈建议所有跟FPGA相关的软件装到纯英文路径下工程文件同样不要放中文目录。ModelSim装好之后需要在Quartus里做关联。打开Quartus进入Tools - Options - EDA Tool Options在ModelSim那一栏填上ModelSim的安装路径比如C:\modeltech64_10.5。如果不做这一步后面Quartus调用ModelSim会提示找不到可执行文件。还有一个坑独立ModelSim的用户容易踩直接用ModelSim打开Quartus生成的工程结果报错找不到altera_mf、altera_lnsim这类Altera器件库。这是因为Quartus和ModelSim各自版本的器件库不匹配需要在ModelSim里先编译Altera库。具体做法是把Quartus安装路径下的eda/sim_lib目录里的altera_mf.v、altera_primitives.v等文件编译进ModelSim的工作库。如果你是安装ModelSim-Altera版本老版Quartus II自带的那种库是预编译好的一般没这个问题。我的建议是新用户直接用Quartus Prime Lite ModelSim套装省去很多库匹配的麻烦老用户不动稳定就好。# 在ModelSim命令窗口手动编译Altera基础库的示例命令 vlib work vlog -work work C:/intelFPGA_lite/20.1/quartus/eda/sim_lib/altera_primitives.v vlog -work work C:/intelFPGA_lite/20.1/quartus/eda/sim_lib/220model.v vlog -work work C:/intelFPGA_lite/20.1/quartus/eda/sim_lib/altera_mf.v不同版本安装路径不同请按实际目录修改。编译完成后ModelSim对Altera器件的原语就不会再报错。2. 波形仿真到底在干什么功能仿真与时序仿真的区别2.1 两种仿真模式验证逻辑正确性与验证时序收敛性很多初学者搞不清楚功能仿真和时序仿真有什么区别一上来就直接在Quartus里点“RTL Simulation”看到波形出来就以为完事了。其实这两者差别非常大。功能仿真也叫RTL仿真、行为仿真验证的是你的逻辑代码写没写对它不管信号在FPGA内部走线经过了多少延时所有门电路都当成理想器件来处理。这在早期排查逻辑错误阶段非常高效。时序仿真则是在编译布线完成之后把实际走线延时和逻辑单元延时反标到网表里再仿真这时你能看到信号的真实波形包括毛刺、竞争冒险等现象。时序仿真跑出来的结果才是你烧进FPGA芯片之后真正会发生的信号变化。简单类比一下功能仿真相当于看设计图纸判断电路原理对不对时序仿真相当于把实际元器件焊好之后用示波器量波形。图纸上信号完美跳变不代表实物上没有毛刺和延时。不过在课程设计和入门阶段绝大部分问题都出在逻辑本身所以功能仿真用得多。时序仿真因为要跑完整布线耗时长通常等设计已经通过功能仿真、准备烧板之前才做一次确认。2.2 仿真时间、时钟周期与复位时序一个计数器例子讲明白波形仿真最核心的概念就是“时间”和“状态”。仿真软件做的事就是在一个虚拟的时间轴上按照你设定的输入信号变化逐拍计算输出信号的状态最终形成一条连续波形。这里面有几个基本概念新手容易绕晕仿真时间单位由timescale控制比如timescale 1ns/1ps表示时间精度为1ns、步长为1ps仿真总时长需要手动设置否则可能只跑了几十纳秒还没看到完整波形就停了。我以一个最典型的带异步复位的模10计数器为例来说明波形仿真需要关注哪些时间节点。假设模块代码如下module counter_10( input wire clk, input wire rst_n, output reg [3:0] q ); always (posedge clk or negedge rst_n) begin if (!rst_n) q 4d0; else if (q 4d9) q 4d0; else q q 1b1; end endmodule这段代码的逻辑很简单复位信号拉低时计数器清零时钟上升沿到来时在0到9之间循环计数。波形仿真要验证的东西就是复位拉低时q是否为0复位释放后q是否在每个时钟上升沿加1加到9之后是否跳回0。仿真时给时钟一个周期比如20ns频率50MHz先在0到80ns期间保持rst_n为低然后在某个时刻拉高观察q从0开始递增。复位必须持续几个时钟周期不能只拉低1ns就松开否则在仿真波形上你很可能看不到q被清零的过程。3. 实操用Quartus内置波形编辑器完成第一次仿真3.1 最小工程搭建从新建工程到编译通过在仿真之前你得先把工程建好。新建工程的流程本身不复杂但版本不同界面有差异我以Quartus Prime Lite 20.1为例。打开软件后选择New Project Wizard工程目录不要用中文器件型号选择你实际板子上的FPGA型号。比如我常用的Cyclone IV系列EP4CE6E22C8如果你不确定具体型号也可以随便选一个同系列的器件不影响功能仿真。工程建好之后把上面的计数器代码保存为counter_10.v并添加到工程里然后执行全编译Start Compilation。这一步是必须的很多人跳过编译直接开仿真结果仿真窗口里根本找不到设计的引脚和内部信号因为Quartus还没有为设计生成网表信息。编译成功后在Compilation Report里能看到逻辑单元占用情况、引脚分配信息这个时候你的设计就已经被Quartus认可后续仿真才有意义。需要特别提醒如果你用的FPGA型号很新而ModelSim版本很旧仿真阶段很可能报“器件库不支持”的错误。这时候要么升级仿真工具要么在Settings里把仿真用的目标器件换成一个老型号临时用。我遇到过几次这种情况都是因为课程设计里选了个最新款FPGA而实验室的ModelSim是10年前的老版本。3.2 用Vector Waveform File画输入波形并启动仿真编译通过之后就可以创建波形文件了。在Quartus里执行File - New - University Program VWF有些版本叫Vector Waveform File。这个VWF文件本质上就是一个图形化的激励编辑器你可以在上面直接画出输入信号随时间变化的波形不需要写testbench。创建好文件后要先把设计里的信号添加进来。双击Name列下方的空白区域或者右键选Insert Node or Bus弹出Node Finder窗口。注意在Filter下拉框里选择Pins: all点List按钮就能看到计数器模块的clk、rst_n、q这三个端口。把它们都添加到波形文件里。这里有个小技巧如果Filter默认是Pins: all有时候会漏掉内部寄存器信号想看内部信号时要把Filter切换成Register: pre-synthesis这样才能把综合前的寄存器节点列出来。接下来是设置激励。先设置仿真总时长菜单Edit - End Time我通常设成1us对这个计数器来说足够跑几十个周期了。然后设置时钟选中clk信号那一行的波形区域右键选择Overwrite - Clock在弹出的对话框里设置时钟周期这里设20ns占空比50%也就是高电平10ns低电平10ns。设置完时钟后再设置复位信号选中rst_n信号先用Overwrite - Clock把它设成周期合适的信号是不对的做法复位信号不能用时钟来驱动正确做法是先用鼠标框选波形区域通过Value选项强制拉低或拉高。具体来说先把整个仿真区间的rst_n设成高电平因为它是低有效复位然后框选前80ns的区域在选中区域右键选Value - 0将这部分拉低。这样波形看起来就是前80ns复位有效之后一直为高计数器正常工作。q信号不需要手动赋值它是输出信号仿真器会根据设计自动算出来。设置完毕后保存文件执行Processing - Start SimulationQuartus会自动进行仿真并打开波形窗口。首次运行如果弹出一个脚本错误大概率是仿真工具路径没配置好回到1.2节检查。仿真跑完后在波形窗口里你应该能看到clk连续跳变rst_n在开始一段为低q信号从0到9循环递增。如果q一直是一条直线检查一下仿真结束时间是不是太短或者复位信号是否从头到尾一直有效。3.3 内置仿真器看内部信号Node Finder的Filter切换VWF仿真的一个优势是可以在Quartus里直接观察内部信号不需要像ModelSim那样手动添加。在波形文件里右键Name列选择Insert Node or BusFilter切换成Register: post-fitting或Register: pre-synthesis就能列出设计中的内部寄存器节点。比如我想看计数器的q[0]、q[1]每一位的变化就可以单独添加这些位信号。这对调试状态机内部状态、分频器内部计数特别有用。不过内置仿真器也有明显的局限性激励只能画简单的时钟、常数、随机序列无法描述复杂的时序关系仿真速度也比ModelSim慢工程大了之后特别明显。而且VWF文件无法做参数化设计改一个仿真条件就要重新画一遍波形。所以做正式一点的实验、课程设计我建议尽早转向ModelSim。内置仿真器适合快速验证小模块比如课堂上写了个加法器想立刻看结果。4. 进阶用ModelSim跑testbench做更专业的波形仿真4.1 写testbench的基本模板时钟生成、复位控制与仿真结束ModelSim的核心用法是跑testbench也就是用一个Verilog或SystemVerilog文件来产生激励并监测输出。testbench不参与综合语法上可以更随意可以使用initial、#延时、$display等行为级描述。这里给一个最简单但完整的testbench模板配合前面的计数器模块使用timescale 1ns / 1ps module counter_10_tb; reg clk; reg rst_n; wire [3:0] q; counter_10 uut( .clk(clk), .rst_n(rst_n), .q(q) ); initial begin clk 0; forever #10 clk ~clk; // 20ns周期时钟 end initial begin rst_n 0; #80; rst_n 1; // 80ns后释放复位 #1000; $finish; // 仿真结束 end endmodule这段testbench有几个点值得注意。timescale放在文件最开头声明仿真时间单位是1ns精度是1ps。时钟用forever #10实现无限翻转复位信号在initial块里延时80ns后拉高。$finish用于结束仿真如果没有它ModelSim会一直跑下去你不知道什么时候该手动停。实际仿真时你可以把#1000改大改小控制观察窗口的长度。testbench文件不需要加到Quartus工程里参与综合但如果你的工程同时包含RTL代码和testbench建议命名为*_tb.v并设置成Simulation源文件类型。在Quartus里右键该文件PropertiesType改成Simulation这样综合时它会自动忽略testbench。如果不改类型Quartus综合时会尝试把testbench也当成可综合代码大概率报错。4.2 Quartus调用ModelSim的完整流程从编译到看波形在Quartus里跑ModelSim功能仿真正确流程是这样先在Settings - EDA Tool Settings - Simulation里把仿真工具指定为ModelSim并设置Test Bench名字。其次在Source面板里把testbench文件添加进工程。最后执行Tools - Run Simulation Tool - RTL SimulationQuartus会调用ModelSim并自动完成编译、加载、启动仿真。这里容易出问题的是Test Bench设置。很多人把testbench文件加进工程之后没有在Assignments - Settings里告诉Quartus“这个文件是顶层测试文件”结果仿真时ModelSim虽然打开了但run没反应或者提示找不到testbench。正确做法是在Settings的Simulation - Compile test bench里点NewTest bench name填counter_10_tb然后在File name里添加counter_10_tb.v顶层模块名也填counter_10_tb。保存后再启动RTL SimulationQuartus会自动把testbench注入到ModelSim的仿真脚本里。ModelSim弹出来之后默认会停留在命令行界面波形窗口需要手动打开。在ModelSim的sim面板里右键点击uut实例选择Add Wave或者选中信号后按CtrlW把感兴趣的信号添加到波形窗口。然后在命令行输入run -all波形窗口就会开始显示仿真结果。如果你发现波形窗口一片空白最常见的原因是没有添加信号其次是仿真时间太短run -all没跑完就停了。用run 1us可以精确控制仿真时长。ModelSim和Quartus联调时还有一个细节信号名可能带/counter_10_tb/uut/clk这样的完整层次路径新手容易因为找信号找半天而烦躁。用CtrlW之前先选中uut下的信号添加的就是这个子模块内部的信号波形窗口显示名称会更简洁。另外右键波形窗口里的信号选择Radix - Unsigned Decimal可以把q从二进制显示改成十进制看起来直观很多。4.3 时序仿真在ModelSim里加载布线后的延时信息做完功能仿真如果你还想进一步看时序仿真操作路径从Tools - Run Simulation Tool - Gate Level Simulation进入。Quartus会先完成一次全编译生成带延时信息的网表和时序文件.vo/.sdo然后把ModelSim加载到这个网表上。这时候仿真出来的波形包含了逻辑单元和布线延时你能看到时钟到输出有延时、组合逻辑有毛刺。时序仿真比功能仿真慢一大截因为ModelSim要处理海量的延时信息。我在做课程设计时通常只对最终版本跑一次时序仿真用来确认工作频率和数据建立时间是否有问题。如果时序仿真发现严重的毛刺首先检查是不是组合逻辑存在未消除的竞争其次检查异步复位是否同时作用于多个触发器导致亚稳态。对于教学实验来说时序仿真结果和功能仿真大致一致就说明设计基本可靠。5. 仿真结果怎么看红线、高阻态与毛刺5.1 波形颜色与状态值的含义为什么你的波形是红线很多新手第一次在ModelSim或Quartus波形窗口里看到一大片红色第一反应是“完了代码写错了”。其实红色并不总是错误它代表的是仿真信号处于未知状态X态或者高阻态Z态。搞清楚这些状态值是学会看波形的前提。波形状态颜色/显示含义常见原因0蓝色/低逻辑低电平正常赋值1绿色/高逻辑高电平正常赋值X红色/红叉未知状态信号未初始化、寄存器未复位、多驱动竞争Z红色/高阻高阻态三态门未使能、信号悬空U红色未定义变量未赋初值仿真库或初始块缺失W橙黄弱信号网络冲突或拉电阻场景红线最常见的原因就是X态。比如计数器的q信号如果testbench里没有给rst_n拉低过一段时间寄存器初值是未知的ModelSim就给你显示X。解决办法也很简单仿真开始后先让复位信号有效一段时间比如80ns对寄存器进行初始化。另外一个原因是你看到了VWF文件里某些输入信号没有赋值它们的X态会一路传播到输出。还有一种情况是输出全是Z态。Z态几乎都是因为设计里有三态门比如inout端口在没使能时输出高阻。如果不需要用三态尽量避免在内部逻辑中用inout如果一定要用确认三态使能信号在testbench里被正确驱动了。5.2 常见仿真失败/异常问题速查表我整理了一份波形仿真阶段最常见的异常现象、可能原因和解决办法覆盖了绝大多数初学者会碰到的情况现象可能原因解决办法波形窗口一片空白没添加信号到Wave窗口选中信号按CtrlW添加波形只有一条直线仿真总时长不够用run -all或加大仿真时间波形全是X态红线没有复位、初始值未定义在testbench里加复位信号保持一段低电平波形全是Z态三态输出未使能、信号没驱动检查三态门控制信号确认驱动源ModelSim启动即报错Quartus与ModelSim版本不匹配换用对应版本的ModelSim或Questa找不见altera_mf等库未编译Altera仿真库手动编译sim_lib目录下文件到work库Quartus运行仿真没反应Test Bench设置错误在Settings里正确配置testbench文件波形有异常毛刺组合逻辑竞争冒险做时序仿真确认或改设计避免组合环路时钟没有翻转时钟生成方式错误检查forever #10 clk~clk写法和代码块位置仿真非常慢器件库过大或时序仿真功能仿真关闭波形刷新加速这些坑我基本都踩过一遍。比如“波形只有一条直线”这个我最早做实验时以为是自己代码写错了反复检查了一个多小时最后发现只是仿真时间设成了50ns而系统时钟周期是20ns相当于只跑了两拍多波形看起来就像没变化。把仿真时长从50ns改成2us波形立刻出来了。所以遇到问题先别怀疑设计先怀疑设置按表里列的顺序排查会快很多。5.3 避坑技巧路径、复位、激励这些细节别忽视说几条我多年用下来的实操经验每一条都是真金白银换来的。第一工程目录和ModelSim工作目录千万不要有中文。有次课程设计同学把工程放在桌面“新建文件夹”里ModelSim编译报了一堆错我花20分钟才定位到是中文路径问题。Windows用户名的默认目录如果是中文也容易触发这个坑建议直接把整个FPGA工作目录放到某个盘的根目录比如D:/fpga_all。第二reset信号持续时间不要太短。有些同学写testbench复位只拉低1ns就释放功能仿真时可能因为复位宽度太窄导致寄存器没有完成复位。我习惯把复位保持时间设成至少3到5个时钟周期比如时钟周期20ns复位就拉低80ns。这样即使有异步复位/异步置位路径上的微小延时也不会导致复位失败。第三仿真用的时钟频率不一定要和板上实际频率一样。功能仿真关注逻辑行为有时为了快速看到波形会把时钟周期设得很长比如100ns让计数器每拍都清清楚楚。时序仿真才需要用真实的时钟约束频率。对初学者来说仿真时钟先放慢把逻辑跑通再逐步靠近真实频率是更稳妥的学习路径。第四不要忽略$finish。ModelSim测试脚本里如果不写$finish仿真会一直运行你只能手动点break。课程设计里我会在testbench末尾加#1000 $finish;确保仿真到指定时间后自动停止否则在批量跑多个测试用例时ModelSim会互相干扰。6. 波形仿真的几个学习建议6.1 先跑通最小工程再改复杂设计我见过太多人一上来就想仿真一个完整的串口收发器或者VGA显示控制器结果被各种模块间的信号连接、时序关系绕晕最后连波形窗口都操作不熟。我的建议是第一次做波形仿真一定要用一个只有时钟和复位的最小模块比如本文的计数器。把工程创建、编译、VWF画激励、ModelSim关联、testbench写法这五件事完整跑通一遍形成肌肉记忆。之后再上复杂设计遇到问题就知道问题是出在设计本身还是仿真流程上。6.2 波形仿真不是终点仿真、板级调试和逻辑分析仪配合使用上了实际板子之后你会发现波形仿真的作用仍然不可替代但它只能验证逻辑正确性不能替代板级调试。FPGA板上的真实信号受到时钟抖动、电源噪声、IO驱动能力等影响仿真里看不到这些。课程设计做到后期仿真通过只能说明设计思路正确务必用开发板实测一下如果板级行为与仿真不一致优先检查引脚约束是否正确、时钟是否送到了对应的全局时钟引脚、复位按键是否接了对的电平。另外Quartus内置的SignalTap逻辑分析仪也是很有用的工具。它可以像示波器一样观察FPGA内部信号不需要额外硬件。当你遇到仿真正常但板上不正常的诡异问题时SignalTap比反复猜测有效得多。把SignalTap和波形仿真配合使用一个在虚拟时间域验证逻辑一个在真实时间域抓信号基本能覆盖绝大多数调试场景。6.3 代码风格会影响仿真结果尽量用同步逻辑、少用组合环路最后提醒一个很多人忽略的点代码风格直接影响仿真和实际行为的一致性。组合逻辑中的反馈环路在仿真时会出现“死循环”或未知态比如assign q ~q;这种写法功能仿真可能直接让ModelSim卡死。计数的加法器、状态转移逻辑尽量都放到always (posedge clk)块里用同步逻辑描述。异步复位信号最好统一用negedge rst_n风格避免和同步逻辑混在一起产生竞争。良好的代码风格不仅让仿真更顺利也会让后面学习时序约束、静态时序分析时省力很多。我自己的体会是波形仿真这项技能花几个小时认真跑通一次后面所有数字逻辑实验都会顺畅很多。看到波形里计数器一排排变化的那一刻你对FPGA内部行为的理解会比只看书深刻得多。也建议你跑通本文计数器例子之后顺手把它改成模60计数器或者带使能信号的版本多折腾几个小实验仿真操作自然就熟练了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑