Quartus与Modelsim联合仿真的高效配置指南
1. 环境准备与软件版本选择:别在第一步就踩坑
很多朋友刚开始接触FPGA开发,一上来就卡在了软件安装和版本匹配上,我自己也吃过亏。Quartus和Modelsim的联合仿真,听起来像是两个软件手拉手干活,但要是版本没选对,它们俩可能连话都说不上一句,直接给你摆个“无法初始化”的脸色看。所以,咱们第一步,得先把这对“搭档”的脾气摸清楚。
首先说Quartus。Intel(以前是Altera)的这款FPGA开发工具,版本迭代挺快的。对于新手或者在校学生,我强烈建议从Quartus Prime Lite Edition(免费版) 开始。别一上来就追求最新版,比如最新的23.x版本,虽然功能新,但可能和你手头的教材、例程或者一些老IP核不兼容,容易出些莫名其妙的问题。像18.1 Standard Edition就是一个非常经典且稳定的版本,网上资料多,社区支持好,足够你完成从入门到进阶的大部分学习。安装时记得勾选安装ModelSim - Intel FPGA Starter Edition,这是Intel为Quartus定制的免费版ModelSim,虽然功能有精简,但用于学习和小型项目的仿真完全够用。
然后是ModelSim。这里有个关键点:如果你安装的是Quartus自带的Starter Edition,那路径什么的Quartus基本都帮你搞定了,省心。但如果你因为项目需要,单独安装了功能更全的ModelSim SE或QuestaSim,那就需要注意了。务必确认你的ModelSim是64位版本,并且Quartus也是64位的,否则后面联合仿真根本调不起来。我见过有人装了32位的ModelSim去配64位的Quartus,折腾半天找不到问题所在。安装路径尽量简单,别放在中文目录或者带空格的目录下,比如D:\EDA\ModelSim就比D:\编程软件\Model Sim 10.1c要靠谱得多,这是无数前辈踩坑换来的经验。
最后提一嘴操作系统。Windows 10或Windows 11是主流,基本没问题。如果你的工作环境是Linux,配置流程在原理上类似,但一些细节路径和脚本命令需要调整,不过那又是另一个话题了。咱们今天先聚焦在Windows环境下,把这条最通用的路走通。
2. 核心配置:让Quartus找到Modelsim
软件装好了,就像两个演员都到了片场,但导演(Quartus)还不知道该去哪儿叫另一位演员(Modelsim)上场。所以,我们需要在Quartus里做个明确的“人员登记”。这一步是联合仿真的基石,配置错了,后面所有操作都白搭。
2.1 设置EDA工具路径
打开Quartus,别急着新建工程。我们先点击菜单栏的 Tools -> Options。在弹出的窗口左侧,找到 General 下面的 EDA Tool Options。这里就是告诉Quartus各种外部EDA工具住在哪里的地方。
你会看到一个 ModelSim 的输入框。如果你安装的是独立的ModelSim SE,就需要手动把路径指过去。点击后面的 ... 按钮,找到你的ModelSim安装目录下的 win64 文件夹(如果是64位软件)。注意,不是选择 modelsim.exe 这个文件,而是选择它所在的文件夹。典型路径类似 C:\modeltech64_10.5\win64。填好后,Quartus就知道:“哦,我的ModelSim搭档住在这里。”
注意:如果你用的是Quartus自带的Starter Edition,这个框很可能是灰的,或者已经自动填好了路径,这说明Quartus已经内部集成好了,你不需要手动设置,这是最省心的情况。
2.2 为工程指定仿真工具与语言
上一步是全局设置,告诉Quartus“我家有ModelSim”。但具体到每个工程,我们还得明确说:“这个工程,我就要用ModelSim来仿真,而且我用的是Verilog语言。” 这个设置必须在创建测试文件模板之前完成,否则会出问题。
现在,我们新建一个工程,或者打开一个已有的工程。然后点击 Assignments -> Settings。在设置窗口左侧,找到 EDA Tool Settings 下的 Simulation。
这里有几个关键选项:
- Tool name:从下拉菜单里选择
ModelSim。这就是你为这个工程指定的“御用仿真器”。 - Format for output netlist:通常保持默认的
Verilog即可。这决定了Quartus综合后生成给ModelSim看的网表格式。 - NativeLink settings:这是联合仿真的核心机制,确保勾选
Run gate-level simulation automatically之类的选项(不同版本表述略有差异),这样在执行时序仿真时才能一键直达。
更重要的是,在这个界面的下方,你需要选择仿真语言。如果你写的是Verilog代码,就选 Verilog HDL;如果是VHDL,就选 VHDL。这个选择会直接影响后续自动生成的测试文件模板的语法。这一步千万不能错,不然生成的模板语言不对,你后面还得自己大改。
3. 创建测试平台:自动化与手动的艺术
测试平台,也就是Testbench,是我们给设计代码施加“刺激”、观察其“反应”的地方。Quartus提供了自动生成模板的捷径,但理解手动创建也同样重要,这能让你在复杂场景下更灵活。
3.1 让Quartus帮你写模板(推荐新手)
这是最快上手的方法,尤其适合测试简单的顶层模块。但有个重要前提:你必须先对设计文件进行“分析与综合”(Analysis & Synthesis)。
- 确保你的顶层设计文件(比如
my_design.v)已经添加到工程中。 - 在Processing菜单里,点击
Start->Start Analysis & Synthesis,或者直接点击工具栏上对应的图标。这个过程会检查你的代码语法,并把它转换成基本的逻辑电路表示。只有经过这一步,Quartus才认识你的模块接口,才能为你生成对应的测试模板。 - 综合完成后,再点击
Processing->Start->Start Test Bench Template Writer。
神奇的事情发生了。Quartus会自动在你的工程目录下,生成一个路径类似 simulation/modelsim/my_design.vt 的文件。这个 .vt 文件就是一个Verilog测试文件的骨架。它已经帮你完成了两件最重要也最繁琐的事:声明了所有输入输出端口,以及实例化了你的待测设计模块。你打开这个文件,会看到类似下面的框架:
`timescale 1 ps / 1 ps // 这就是你在Settings里设置的时间单位
module my_design_vlg_tst();
// 这里自动声明了你设计模块的所有端口
reg each_input_port;
wire each_output_port;
// 实例化你的设计
my_design i1 (
.port_name (port_name_signal),
// ... 所有端口连接
);
initial
begin
// 这里只有简单的初始化,真正的测试激励需要你来编写
$display("Running testbench");
end
endmodule
这个模板极大地节省了时间,特别是当你的模块有几十个端口时,手动敲一遍很容易出错。
3.2 手动创建测试文件(掌握主动权)
虽然自动生成方便,但 .vt 文件有个小缺点:有些第三方文本编辑器(比如非常好用的Notepad++)默认的语法高亮规则可能不识别 .vt 后缀,导致代码全是单色,看着费劲。而 .v 后缀是通用的Verilog文件后缀,任何编辑器都能正确高亮。
因此,很多有经验的开发者喜欢手动创建 .v 的测试文件。方法很简单:
- 在工程文件夹里,新建一个
sim或testbench文件夹,专门存放仿真相关文件,这样工程结构更清晰。 - 在里面新建一个文本文件,重命名为
tb_my_design.v(tb_是testbench的常用前缀)。 - 用任何文本编辑器(VSCode、Notepad++、甚至Quartus自带的编辑器)打开,从头开始编写测试模块。
手动编写给了你完全的控制权。你可以自由组织代码结构,比如把时钟生成、复位控制、测试用例都写成独立的 task 或 function,可读性和可复用性更强。当然,这要求你对Verilog测试平台的写法更熟悉。对于复杂测试场景,比如需要读取文件数据作为输入,或者将输出结果写入文件进行比对,手动编写是必须的。
4. 编写测试激励:让电路“动”起来
现在,无论你是用自动生成的 .vt 骨架,还是自己创建的 .v 文件,我们面对的都是一个只有端口声明和模块实例的“空壳”。接下来,就要往里注入灵魂——编写测试激励。这就是在 initial 块和 always 块里写代码,模拟真实世界输入信号的变化。
假设我们测试一个简单的2输入与门模块 and_gate。自动生成的模板里已经实例化好了 and_gate i1 (...);。我们的任务就是去驱动它的两个输入 a 和 b。
initial
begin
// 初始化输入信号
a = 1'b0;
b = 1'b0;
#100; // 等待100个时间单位(根据`timescale,可能是100ps或100ns)
// 测试用例1: 0 & 0
$display("Test Case 1: a=0, b=0");
#100;
// 测试用例2: 0 & 1
a = 1'b0;
b = 1'b1;
$display("Test Case 2: a=0, b=1");
#100;
// 测试用例3: 1 & 0
a = 1'b1;
b = 1'b0;
#100;
// 测试用例4: 1 & 1
a = 1'b1;
b = 1'b1;
#100;
// 所有测试完成,结束仿真
$stop;
end
这是最基础的激励写法。但实际项目中,时钟和复位信号是最常见的。对于时钟,我们通常用 always 块来产生:
// 生成一个周期为20个时间单位的时钟(假设`timescale 1ns/1ps`,即20ns周期,50MHz)
reg clk;
initial begin
clk = 0;
end
always #10 clk = ~clk; // 每10个时间单位翻转一次,周期就是20
对于复杂的总线信号或者需要重复的测试序列,我们可以封装成 task:
task send_packet;
input [7:0] data;
begin
start = 1'b1;
data_bus = data;
@(posedge clk); // 等待一个时钟上升沿
start = 1'b0;
// ... 其他握手信号
end
endtask
// 在initial块中调用
initial begin
// ... 初始化
send_packet(8'hAA);
send_packet(8'h55);
end
编写激励是仿真的核心乐趣所在,你需要像导演一样,设计各种场景(测试用例)去考验你的设计,确保它在各种边边角角的情况下都能正确工作。记得多使用 $display 在仿真控制台打印信息,这是调试的重要依据。
5. 关联测试文件与设置仿真参数
测试激励写好了,我们还得正式地“通知”Quartus和Modelsim:“嘿,这个文件就是用来测试那个设计的。” 同时,一些仿真本身的参数也需要提前设定好,让仿真过程更受控。
回到 Assignments -> Settings -> EDA Tool Settings -> Simulation 这个界面。这次我们关注下半部分。
-
添加Test Bench文件:点击
Test Benches...按钮,会弹出一个新窗口。再点击New...。- Test bench name:给你这个测试平台起个名字,比如
tb_and_gate。 - Top level module in test bench:这是最容易填错的地方! 这里要填的是测试文件(.vt或.v)里面的顶层模块名,而不是你待测的设计模块名。比如,你的测试文件开头是
module and_gate_tb();,那这里就填and_gate_tb。如果你打开的是Quartus自动生成的.vt文件,模块名通常是xxx_vlg_tst。 - Design instance name in test bench:这个一般不用改,保持默认的
i1即可,除非你在测试文件里把实例化名改成了别的。 - File name:点击
...,找到并添加你编写好的测试文件(tb_and_gate.v或and_gate.vt)。
- Test bench name:给你这个测试平台起个名字,比如
-
设置仿真运行时间:在同一个界面,留意
End simulation at这个选项。这里你可以设置仿真自动停止的时间。比如你填1 us,那么仿真运行到1微秒时就会自动停止。我强烈建议你根据测试内容估算一个时间并填上。如果不填,仿真会一直运行下去,直到你在ModelSim里手动点击停止。我就有过惨痛教训,跑一个复杂仿真忘了设时间,出去吃个饭回来发现还在跑,浪费了大量时间。 -
确认仿真选项:确保
Simulation mode选择正确。如果是验证逻辑功能,选Functional(功能仿真);如果需要考虑布局布线后的真实延迟,选Timing(时序仿真)。第一次验证通常先用功能仿真。
6. 启动仿真与结果分析:见证设计的运行
一切就绪,激动人心的时刻到了——启动仿真。Quartus提供了非常便捷的一键启动方式。
- 启动RTL(功能)仿真:在菜单栏点击
Tools->Run Simulation Tool->RTL Simulation。 - 启动门级(时序)仿真:点击
Tools->Run Simulation Tool->Gate Level Simulation。
点击之后,Quartus会自动在后台做几件事:1. 综合你的设计(如果之前没综合过);2. 根据设置生成仿真网表;3. 启动ModelSim软件;4. 在ModelSim中自动执行编译、加载设计、运行仿真等一系列脚本命令。你会看到ModelSim窗口弹出,命令行界面滚动着一行行信息,最后波形窗口(Wave)会自动打开,里面已经添加了你设计模块的所有信号。
第一次运行时,ModelSim可能会弹出一个“License”相关的提示,如果用的是Starter Edition,直接点“OK”或“Close”就行,这是免费许可的提示,不是错误。
在ModelSim的波形窗口里,你可以看到信号随着时间变化的波形。通过工具栏的放大、缩小、全屏显示按钮,可以仔细检查你的设计行为是否符合预期。比如,检查与门的输出是否只在两个输入都为1时才变高。
如果发现波形不对,就需要调试。除了看波形,ModelSim控制台(Transcript)窗口里 $display 打印的信息是重要线索。你也可以在ModelSim中手动添加其他内部信号到波形窗口观察:在“Objects”窗口找到你的设计实例(如 i1),展开它,选中想看的信号,右键点击 Add to -> Wave -> Selected Signals。
仿真完成后,不要直接关闭ModelSim。你可以修改测试激励文件,保存后,在ModelSim的Transcript窗口输入 restart 命令回车,然后输入 run -all 再次运行,即可看到新的仿真结果,无需重新从Quartus启动,这样调试效率更高。
7. 进阶技巧与常见问题排坑
走通了基本流程,你可能还会遇到一些“小石头”绊脚。这里分享几个我踩过的坑和对应的解决办法,希望能帮你更顺畅地前行。
问题一:仿真时提示“找不到模块”或“未定义”。
这通常是因为ModelSim没有编译你的设计源文件。虽然Quartus的NativeLink机制应该自动处理,但有时会漏掉一些非顶层的子模块文件。解决办法是,在Quartus中,确保所有用到的 .v 文件都通过 Project -> Add/Remove Files in Project... 添加到了工程里。更彻底的方法是,在ModelSim中手动建立一个工程,把所有设计文件和测试文件都加进去编译一遍,但这稍微麻烦些。
问题二:波形窗口没有信号,或者信号全是红色(高阻态Z)。
红色高阻态通常意味着该信号没有被任何驱动源驱动。检查你的测试激励,是否所有输入信号都赋予了初始值?是否有时钟信号在正常翻转?对于输出信号是红色,可能是你的设计内部逻辑有误,导致输出端没有被赋值。确保你的设计代码中的 always 块或 assign 语句覆盖了所有情况。
问题三:仿真速度非常慢。
时序仿真比功能仿真慢很多是正常的,因为要计算延迟。如果功能仿真也慢,可以尝试:1. 在仿真设置中,适当缩短仿真总时间(End simulation at)。2. 在波形窗口中,不要添加过多信号,尤其是像大型存储器这样的信号,添加它们会极大增加数据记录量。3. 检查测试激励中是否使用了过于精细的时间控制(如 #1),在验证主要功能时,可以适当加大时间间隔。
问题四:如何对仿真结果进行自动化比对?
对于大型设计,光靠人眼看波形效率太低。可以在测试平台中使用Verilog的 $fopen, $fwrite, $fclose 系统任务,将仿真结果输出到文本文件中。然后,用同样的方法输出一份预期结果(黄金参考文件),最后写一个简单的脚本(比如Python或Perl)来比较两个文件是否一致。这样,每次修改代码后,跑一遍仿真和比对脚本,就能快速知道功能是否正确。
问题五:联合仿真脚本的奥秘。
当你点击 RTL Simulation 时,Quartus实际上调用了一个名为 mentor.tcl 的脚本(具体名字可能因版本而异)。这个脚本位于你的工程目录下的 simulation 文件夹里。如果你遇到一些自动流程解决不了的问题,比如需要特殊的编译选项,可以研究一下这个脚本。更高级的用法是,你可以编写自己的 .do 文件(ModelSim的批处理脚本),然后在Quartus的仿真设置里指定这个自定义脚本,从而完全掌控ModelSim的启动和编译流程。这给了你极大的灵活性,但需要对Tcl脚本有一定了解。
配置联合仿真的过程,就像是在搭建一座连接设计和验证的桥梁。一开始可能会觉得步骤繁琐,但一旦跑通,你会发现它带来的效率提升是巨大的。你可以快速迭代你的设计,在烧写到实际的FPGA芯片之前,就通过仿真发现并修复绝大部分的逻辑错误。记住,仿真的深度和质量,直接决定了你硬件设计的可靠性和后期调试的难度。多花时间在仿真上,往往能在后期节省数倍的时间。刚开始的时候,严格按照步骤来,别跳步。等熟练了,你就会形成自己的配置习惯和仿真方法论,这套工具链就会成为你手中得心应手的利器。
更多推荐


所有评论(0)