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。跟着向导走:

  1. 工程目录:选你刚建好的文件夹
  2. 工程名称:建议和顶层模块名一致,比如 motor_ctrl_top
  3. 添加文件:可以先跳过,后面再添加
  4. 器件选择:根据你手上的板子选,比如 Cyclone IV E 系列
  5. 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 里,你会看到:

  1. Library 窗口:找到你的工程库,展开,双击 tb_clk_divider
  2. Objects 窗口:显示所有信号,选中 clk_inclk_outcnt
  3. 右键 → Add to Wave
  4. 点击 Run -All,跑 1us 看看

波形出来了。你会看到 clk_out 每 5 个时钟周期翻转一次,正好是 10 分频。

我曾经犯过一个低级错误:仿真时忘了加 $stop,结果 ModelSim 一直跑,电脑风扇呼呼转,我还以为死机了。嗯,后来我学乖了,testbench 里一定加个仿真结束条件。

注意:波形查看时,记得把 cnt 信号改成 Radix → Unsigned,否则默认显示二进制,看计数器值很费劲。

4.5 本章知识体系

下面这张图,是我自己总结的入门流程。你跟着走,基本不会迷路。

Quartus + ModelSim 入门流程 1. 创建工程 新建文件夹 → 向导配置 2. 编写代码 RTL 设计 + 参数化 3. 仿真设置 Testbench + 工具链 4. 波形查看 添加信号 → 运行 5. 验证逻辑 检查波形是否符合预期 6. 修改迭代 根据波形调整代码 迭代循环,直到仿真通过

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 缩小。这些快捷键用熟了,效率翻倍。

好了,这一章的内容就这些。你跟着步骤走一遍,应该就能跑通第一个仿真了。记住,工具只是手段,理解硬件行为才是目的。


专注资料整理