4、Quartus Prime与ModelSim入门:工程创建、代码编写、仿真设置、波形查看
说实话,很多刚入行的朋友问我:「FPGA 到底难在哪?」
我通常回答:「不是逻辑难写,是工具链太折腾。」
Quartus Prime 和 ModelSim,就是咱们绕不开的两座山。但别怕,翻过去之后,你会发现其实就那么回事。
4.1 工程创建:别小看第一步
我个人习惯,每次新建工程前,先在硬盘里建好文件夹。比如:
D:/FPGA_Projects/MotorCtrl_v1/
├── src/ (放代码)
├── sim/ (放仿真文件)
├── ip/ (放IP核)
└── doc/ (放文档)
嗯,这个习惯救过我很多次。有一次项目赶进度,我直接在默认路径下建工程,结果后来要移植到另一台电脑,文件散落一地,找都找不到。
打开 Quartus Prime,点击 File → New Project Wizard。跟着向导走:
- 工程目录:选你刚建好的文件夹
- 工程名称:建议和顶层模块名一致,比如
motor_ctrl_top - 添加文件:可以先跳过,后面再添加
- 器件选择:根据你手上的板子选,比如 Cyclone IV E 系列
- EDA 工具设置:这里要指定 ModelSim 的路径
重点:在 EDA Tool Settings 里,Simulation 选 ModelSim-Altera,语言选 Verilog HDL。这一步很多人漏掉,导致后面仿真跑不起来。
4.2 代码编写:从点灯到电机控制
咱们写一个最简单的分频器,用来给电机驱动提供基础时钟。你想想看,电机控制里,PWM 的基频就是这么来的。
module clk_divider (
input wire clk_in, // 50MHz 输入时钟
input wire rst_n, // 复位,低有效
output reg clk_out // 分频输出
);
parameter DIV = 50_000_000; // 分频系数,输出 1Hz
reg [25:0] cnt;
always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
clk_out <= 0;
end else begin
if (cnt == DIV/2 - 1) begin
cnt <= 0;
clk_out <= ~clk_out;
end else begin
cnt <= cnt + 1;
end
end
end
endmodule
这里有个小坑。我在项目中遇到过,分频系数算错了,导致电机转速完全不对。后来我习惯在代码里加注释,把目标频率写清楚,比如 // 50MHz → 1Hz,这样回头检查时一目了然。
4.3 仿真设置:ModelSim 的第一次握手
写完了代码,别急着上板子。先仿真,这是铁律。
在 Quartus 里,点击 Assignments → Settings → Simulation,确保:
- Simulation input file:指向你的 testbench 文件
- Compile test bench:勾上,并指定顶层模块
然后写一个简单的 testbench:
`timescale 1ns / 1ps
module tb_clk_divider;
reg clk_in;
reg rst_n;
wire clk_out;
clk_divider #(
.DIV(10) // 为了仿真快一点,分频系数改小
) uut (
.clk_in (clk_in),
.rst_n (rst_n),
.clk_out(clk_out)
);
initial begin
clk_in = 0;
rst_n = 0;
#100 rst_n = 1;
#1000 $stop;
end
always #5 clk_in = ~clk_in; // 100MHz 时钟
endmodule
我的习惯:仿真时把分频系数改小,比如改成 10,这样几微秒就能看到波形翻转。等验证逻辑正确了,再改回真实值做时序仿真。
4.4 波形查看:读懂硬件的「心电图」
点击 Tools → Run Simulation Tool → RTL Simulation,ModelSim 就会自动启动。
在 ModelSim 里,你会看到:
- Library 窗口:找到你的工程库,展开,双击
tb_clk_divider - Objects 窗口:显示所有信号,选中
clk_in、clk_out、cnt - 右键 → Add to Wave
- 点击 Run -All,跑 1us 看看
波形出来了。你会看到 clk_out 每 5 个时钟周期翻转一次,正好是 10 分频。
我曾经犯过一个低级错误:仿真时忘了加 $stop,结果 ModelSim 一直跑,电脑风扇呼呼转,我还以为死机了。嗯,后来我学乖了,testbench 里一定加个仿真结束条件。
注意:波形查看时,记得把 cnt 信号改成 Radix → Unsigned,否则默认显示二进制,看计数器值很费劲。
4.5 本章知识体系
下面这张图,是我自己总结的入门流程。你跟着走,基本不会迷路。
4.6 常见问题与避坑
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| ModelSim 启动后无波形 | 没添加信号到 Wave 窗口 | 在 Objects 窗口选中信号,右键 Add to Wave |
| 仿真一直跑不停 | Testbench 缺少 $stop | 加 #时间 $stop; 或设置运行时间 |
| 波形显示为红色/高阻 | 信号未驱动,或复位没释放 | 检查 initial 块里的复位时序 |
| Quartus 找不到 ModelSim | EDA 工具路径没配置 | Tools → Options → EDA Tool Options 设置路径 |
一个小技巧:在 ModelSim 里,按 Ctrl + W 可以快速打开 Wave 窗口。按 g 键可以放大波形,按 Shift + g 缩小。这些快捷键用熟了,效率翻倍。
好了,这一章的内容就这些。你跟着步骤走一遍,应该就能跑通第一个仿真了。记住,工具只是手段,理解硬件行为才是目的。