1. Quartus II 开发环境本质认知

Quartus II 是 Altera 公司(2015 年被 Intel 收购)为其 FPGA 和 CPLD 器件量身定制的完整 EDA 工具链。它并非一个通用的编程 IDE,而是一个高度集成的硬件设计实现平台,其核心价值在于将抽象的硬件描述语言(HDL)代码,经过一系列严格的、物理可实现的转换步骤,最终映射到目标芯片的物理资源上。理解这一点至关重要:我们不是在“编写软件”,而是在“构造硬件”。每一个 always 块、每一个 assign 语句,最终都对应着芯片内部真实的逻辑门、触发器、布线通道和 I/O 引脚。

这种硬件构造过程天然具有不可忽视的物理约束。与软件运行在通用 CPU 上不同,FPGA 的逻辑单元数量、嵌入式 RAM 块(M9K/M10K)、乘法器(DSP Block)、时钟网络(Global Clock Network)以及可用的 I/O 引脚数量都是固定且有限的。Quartus II 的整个流程——从综合(Synthesis)到布局布线(Place & Route)——本质上就是一场在这些硬性约束下进行的、精密的资源分配与优化博弈。任何脱离物理约束去谈 HDL 设计,都是空中楼阁。因此,一个合格的 FPGA 工程师,必须时刻将代码与芯片的物理资源挂起钩来。当你写下 reg [31:0] counter; 时,你心里要清楚,这将消耗多少个逻辑单元(LE);当你使用 * 运算符时,你要明白它很可能调用了一个 DSP Block,而这个资源是稀缺的。

2. Quartus II 核心开发流程解析

Quartus II 的开发流程是一条严谨、不可逆的流水线,每个环节都有其明确的工程目的和严格的输入输出规范。跳过任何一个环节,或者对某个环节的目的理解模糊,都会导致后续步骤失败或产生不可预测的硬件行为。

2.1 工程创建:奠定物理世界的基石

工程创建是整个设计的起点,其核心任务是为设计划定一个精确的物理边界。这绝非简单的“新建文件夹”操作,而是向工具明确宣告:“我的设计将运行在哪个具体的硅片上”。

  • 工程路径与名称 :路径必须为纯英文、数字及下划线( _ )构成,严禁中文、空格及特殊字符。这是由底层编译工具链(如 Tcl 解释器、Makefile 工具)的跨平台兼容性决定的。任何中文路径都会导致工具在解析路径字符串时崩溃,错误信息往往晦涩难懂,徒增调试时间。
  • 器件选型(Device Selection) :这是最关键的一步。以本例中的 EP4CE120F17C8 为例,其型号编码蕴含了全部物理信息:
  • EP4CE : Cyclone IV E 系列,低功耗、高性价比。
  • 120 : 逻辑单元(Logic Element, LE)数量约为 120,000 个。
  • F17 : FBGA 封装,17x17mm,256 个引脚。
  • C8 : 商业级温度范围(0°C 至 85°C),速度等级为 -8(数字越小,速度越快,延迟越低)。

选择错误的器件,后果极其严重。若误选为更小的 EP4CE6F17C8,当设计规模超出其 LE 容量时,布局布线(Fitter)阶段会直接报错 Error (171000): Can't fit design in device ,整个工程无法生成配置文件( .sof )。反之,若选用过大器件,则会造成资源浪费,且可能因引脚约束不严而掩盖潜在的时序问题。

2.2 设计输入:用代码定义硬件行为

设计输入是将工程师的意图转化为机器可读形式的过程。Quartus II 支持原理图(Schematic)、VHDL 和 Verilog HDL 三种方式。然而,在现代 FPGA 开发中,原理图输入已基本被淘汰,原因有三:

  1. 可维护性差 :一个包含数千个逻辑门的复杂系统,用原理图表达无异于绘制一张超大尺寸的电路图,查找、修改、复用异常困难。
  2. 可移植性为零 :原理图是特定于 Quartus II 工具的二进制格式,无法被其他厂商(如 Xilinx 的 Vivado)识别,彻底锁死技术栈。
  3. 抽象层级过低 :它强迫工程师在门级电路层面思考,而非在行为级或寄存器传输级(RTL)思考,极大降低了开发效率。

因此,Verilog HDL 成为工业界的绝对主流。其优势在于:
- 标准化 :IEEE 1364 标准确保了代码在不同工具间的可移植性。
- 高抽象度 always @(posedge clk) 块清晰地表达了同步时序逻辑,无需关心底层触发器的具体连接。
- 强大的建模能力 :不仅能描述组合逻辑( assign ),还能精确建模时序逻辑、状态机、总线协议等复杂行为。

在 Quartus II 中,设计输入文件( .v 文件)必须被明确添加到工程中。这一步骤看似简单,实则关乎工程的完整性。未添加的文件,即使存在于项目文件夹中,Quartus II 在综合时也会完全忽略,导致“找不到顶层模块”或“未定义的信号”等致命错误。这也是为什么教程中强调要勾选“Add file to current project”。

2.3 分析与综合:从代码到网表的第一次蜕变

“分析与综合”(Analysis & Synthesis)是 Quartus II 流程中第一个真正的编译环节,其核心任务是将符合语法规范的 Verilog 代码,翻译成一个与目标器件物理结构无关的、由基本逻辑单元(如 LUT、FF)组成的中间网表(Netlist)。

  • 分析(Analysis) :这是静态检查阶段。Quartus II 的分析器会逐行扫描源代码,检查所有语法错误(Syntax Error)和严重警告(Warning)。例如, wire [7:0] data; assign data = 8'hFF; 是合法的,但 assign data = 9'h1FF; 则会报错,因为右侧常量位宽超出了左侧 wire 的定义。这类错误必须在此阶段修复,否则综合无法继续。
  • 综合(Synthesis) :这是逻辑转换的核心。综合器会根据代码的语义,将其映射为最优化的逻辑结构。例如,一个 case 语句可能被综合为一个多路选择器(MUX),也可能被综合为一个优先级编码器,具体取决于 case 项的覆盖情况和综合器的优化策略。工程师可以通过设置综合属性(如 (* syn_encoding = "one_hot" *) )来指导综合器的行为,但这需要对底层硬件有深刻理解。

此阶段的输出是 .qsf (Quartus Settings File)和 .qip (Quartus IP File)等配置文件,它们记录了综合后的逻辑结构和初步的资源占用报告。此时,设计尚未与任何物理引脚绑定,因此无法下载到板子上运行。

2.4 引脚分配:建立硬件与现实的桥梁

引脚分配(Pin Assignment)是将虚拟的逻辑信号与物理世界的真实焊点连接起来的关键步骤。它解决了“我的 led[0] 信号,到底应该连到开发板上的哪一个金属引脚?”这个根本问题。

  • 依据是原理图 :分配的唯一权威来源是开发板的原理图(Schematic)。例如,正点原子新起点开发板的用户手册会明确指出:“LED0 对应 FPGA 的 PIN_A12”。因此,在 Quartus II 的 Pin Planner 工具中,你必须将 led[0] 这个信号名,手动分配到 PIN_A12 这个物理引脚上。任何凭空猜测或随意分配,都会导致硬件功能完全失效。
  • I/O 标准与电气特性 :引脚分配不仅是“连到哪”,更是“怎么连”。同一个物理引脚,可以配置为不同的 I/O 标准(I/O Standard),如 3.3-V LVTTL (用于驱动普通 LED)、 2.5-V SSTL-2 Class I (用于 DDR 内存接口)或 1.8-V HSTL (用于高速 SerDes)。这些标准定义了引脚的驱动电压、电流能力和时序参数。错误的 I/O 标准设置,轻则导致外设无法通信,重则因电压不匹配而损坏芯片。
  • 时钟引脚的特殊性 :FPGA 的全局时钟(Global Clock)引脚是稀缺资源,它们连接着专用的、低抖动、全芯片覆盖的时钟网络。你的主时钟信号(如 clk_50m )必须被分配到一个带有 GCLK 前缀的引脚(如 PIN_W15 ),否则,综合器会将其当作普通信号处理,导致严重的时序违例(Timing Violation)和系统不稳定。

2.5 全编译:物理实现的终极考验

全编译(Full Compilation)是 Quartus II 流程中最耗时、也最核心的环节。它包含了四个子步骤:综合(Synthesis)、布局(Fitting)、布线(Routing)和时序分析(Timing Analysis)。

  • 布局(Fitting) :将综合后得到的逻辑网表,分配到 FPGA 芯片上具体的逻辑阵列块(Logic Array Block, LAB)和嵌入式 RAM 块中。这是一个复杂的资源调度问题,类似于给一个巨大的拼图寻找最优的摆放位置。
  • 布线(Routing) :在布局完成后,用芯片内部的可编程互连资源(Interconnect),将所有被布局的逻辑单元连接起来,形成完整的数据通路和控制通路。布线的质量直接决定了设计的最高工作频率。
  • 时序分析(Timing Analysis) :这是全编译的“法官”。它会严格检查设计中每一条关键路径(Critical Path)是否满足时序要求。例如,从一个触发器的 Q 输出,经过组合逻辑,再到下一个触发器的 D 输入,这条路径的总延时(Propagation Delay)必须小于时钟周期减去建立时间(Setup Time)。如果分析报告中出现 Critical Warning: Timing requirements not met ,意味着你的设计在目标频率下无法稳定工作,必须通过优化代码(如流水线化)或降低时钟频率来解决。

全编译成功的唯一标志,是生成一个 .sof (SRAM Object File)文件。这个二进制文件,就是可以直接烧录到 FPGA 配置存储器(SRAM)中的“硬件程序”。

3. 项目工程结构最佳实践

一个混乱的工程目录,是项目失败的温床。良好的工程结构,是专业 FPGA 开发者的基本素养,它能极大提升开发效率、团队协作能力和项目可维护性。

3.1 标准四层目录结构

推荐采用如下清晰、可扩展的目录结构:

my_project/          <-- 项目根目录(纯英文,无空格)
├── doc/             <-- 文档目录
│   ├── datasheet/   │   └── EP4CE120F17C8.pdf    # 芯片数据手册
│   └── schematic/   │       └── newstart_v2.pdf  # 开发板原理图
├── rtl/             <-- RTL 源码目录(Register Transfer Level)
│   ├── led_top.v    │   └── counter.v            # 所有 Verilog 模块文件
├── sim/             <-- 仿真目录
│   ├── testbench/   │   │   └── tb_led_top.v     # 测试平台文件
│   └── waveform/    │       └── tb_led_top.vwf   # 波形查看文件(Quartus Native)
└── prj/             <-- Quartus 工程目录(Project)
    └── my_project.qpf # Quartus Project File
  • doc/ 目录 :存放所有与项目相关的参考文档。芯片手册是理解器件特性的圣经,开发板原理图是引脚分配的唯一依据。将它们集中存放,避免了每次需要时都要在硬盘里大海捞针。
  • rtl/ 目录 :这是工程师的“主战场”。所有 .v 文件都应存放于此。模块命名应遵循清晰的规则,如 led_top.v 表示顶层模块, counter.v 表示计数器子模块。避免将所有代码堆在一个大文件里,这违背了模块化设计原则。
  • sim/ 目录 :仿真(Simulation)是验证设计正确性的黄金标准。 testbench 子目录存放测试平台(Testbench)代码,它不被综合,只用于仿真。 waveform 子目录存放波形文件,用于可视化观察信号变化。没有仿真的设计,如同没有测试的软件,其可靠性无从谈起。
  • prj/ 目录 :这是 Quartus II 工程文件的专属领地。 .qpf (Quartus Project File)和 .qsf (Quartus Settings File)等工程配置文件都存放于此。将工程文件与源码分离,使得你可以轻松地为同一套 RTL 代码创建多个不同配置(如不同器件、不同时钟频率)的工程。

3.2 工程文件管理要点

  • 版本控制友好 :上述结构天然适合 Git 等版本控制系统。你可以将 prj/ 目录加入 .gitignore ,因为 .qpf .qsf 文件包含了大量与本地环境相关的路径信息,不适合共享。而 rtl/ doc/ 目录则是团队协作的核心。
  • 避免“一键式”工程 :不要将所有文件(代码、工程、文档)都放在一个大文件夹里。这会导致文件查找困难,也容易在复制、移动工程时遗漏关键文件。
  • 顶层模块命名一致性 :在创建工程时指定的顶层模块名(Top-Level Entity),必须与 rtl/ 目录下顶层 Verilog 文件中的 module 名称完全一致(包括大小写)。这是 Quartus II 能够找到设计入口的唯一方式。

4. Quartus II 用户界面深度配置

Quartus II 的默认界面设置,对于初学者而言并不友好。对其进行合理配置,是提升开发体验和效率的必备技能。

4.1 文本编辑器(Text Editor)配置

文本编辑器是工程师每天打交道最多的地方,其配置直接影响编码效率和代码可读性。

  • 字体与字号 :在 Tools > Options > Text Editor 中,将字体设置为等宽字体(如 Consolas , Courier New ),字号设置为 14 16 。等宽字体确保代码对齐(如 begin...end 块)清晰可辨,更大的字号则缓解长时间编码带来的眼部疲劳。
  • Tab 键行为 :将 Tab Size 设置为 4 ,并勾选 Insert spaces for tab 。这意味着每次按下 Tab 键,编辑器会插入 4 个空格,而不是一个 \t 字符。这是 Verilog 社区的通用约定,能保证代码在不同编辑器、不同操作系统下显示一致,避免因制表符解释差异导致的缩进混乱。
  • 自动备份(Auto Save) :取消勾选 Create backup files 。Quartus II 的自动备份功能会在每次保存时生成一个 filename.bak 文件,这不仅占用磁盘空间,而且在使用 Git 时会产生大量无意义的备份文件,污染版本库。专业的做法是依赖 Git 进行版本管理,而非编辑器的简陋备份。

4.2 编译与报告配置

  • 编译日志级别 :在 Assignments > Settings > Compiler 中,将 Informational Warning 级别的消息设置为 Show 。不要屏蔽警告!许多警告(如 Warning (10230): ... can be implemented as a register )实际上揭示了代码中潜在的、可被优化的时序瓶颈。将它们视为宝贵的性能调优线索。
  • 时序报告生成 :确保 Timing Analysis 选项被启用。编译完成后,务必打开 Report > Timing Analyzer > Summary 报告,重点关注 Slack (松弛量)一栏。一个健康的 Slack 值应为正数(如 +1.234 ns ),表示时序裕量充足;负值(如 -0.567 ns )则意味着时序违例,设计在该频率下无法可靠工作。

5. 实战:基于 EP4CE120F17C8 的流水灯工程详解

现在,我们将前述所有理论知识,融入一个具体的、可运行的工程实践中。本例将以正点原子新起点 V2 开发板上的“流水灯”功能为例,完整演示从零开始的 Quartus II 工程构建流程。

5.1 硬件需求分析

首先,我们必须从开发板的物理世界出发,进行需求分析:
- 目标功能 :让开发板上的 8 个 LED(LED0 至 LED7)依次点亮,形成“流水”效果。
- 硬件资源
- LED :共 8 个,均为低电平有效(即 led_n[7:0] 0 时点亮)。
- 时钟源 :开发板提供一个 50MHz 的晶振,连接到 FPGA 的 PIN_W15 (GCLK0)。
- 复位按键 :一个按键(KEY0)连接到 PIN_T14 ,按下时为低电平。

这些信息全部来源于 doc/schematic/newstart_v2.pdf 原理图。

5.2 RTL 设计实现

基于以上分析,我们在 rtl/ 目录下创建 led_top.v 文件。其核心逻辑是:
1. 使用一个 26 位计数器对 50MHz 时钟进行分频,得到约 1Hz 的时钟( 50_000_000 / 2^26 ≈ 0.745 Hz )。
2. 使用一个 3 位计数器,在分频后的时钟上升沿,循环递增,产生 000 111 的 8 个状态。
3. 将该 3 位计数器的状态,通过一个 case 语句,映射到 8 位的 led_n 输出上,实现单灯点亮的流水效果。

// led_top.v
module led_top (
    input  wire         clk_50m,   // 50MHz system clock
    input  wire         rst_n,     // active-low reset
    output wire  [7:0]  led_n      // active-low LED outputs
);

// Internal signals
reg  [25:0]  cnt_26b;  // 26-bit counter for 1Hz clock division
wire         clk_1hz;
reg  [2:0]   cnt_3b;   // 3-bit counter for LED selection

// Generate 1Hz clock from 50MHz
always @(posedge clk_50m or negedge rst_n) begin
    if (!rst_n) begin
        cnt_26b <= 26'd0;
    end else begin
        cnt_26b <= cnt_26b + 1'b1;
    end
end

assign clk_1hz = cnt_26b[25]; // The MSB toggles at ~1Hz

// Generate LED pattern
always @(posedge clk_1hz or negedge rst_n) begin
    if (!rst_n) begin
        cnt_3b <= 3'd0;
    end else begin
        cnt_3b <= cnt_3b + 1'b1;
    end
end

// Output logic: active-low LEDs
assign led_n = {
    (cnt_3b == 3'd0),  // LED0
    (cnt_3b == 3'd1),  // LED1
    (cnt_3b == 3'd2),  // LED2
    (cnt_3b == 3'd3),  // LED3
    (cnt_3b == 3'd4),  // LED4
    (cnt_3b == 3'd5),  // LED5
    (cnt_3b == 3'd6),  // LED6
    (cnt_3b == 3'd7)   // LED7
};

endmodule

这段代码体现了典型的 RTL 设计思想:所有时序逻辑均在 always @(posedge clk or negedge rst_n) 块中完成,所有组合逻辑均用 assign 语句完成。 led_n 的赋值采用了简洁的位拼接( {} )语法,清晰地表达了每一位 LED 的亮灭状态。

5.3 引脚约束文件(.qsf)配置

在 Quartus II 中,引脚分配有两种方式:图形界面(Pin Planner)和文本文件(.qsf)。后者是更专业、更易版本控制的方式。我们在 prj/ 目录下创建 led_top.qsf 文件,并添加以下约束:

# Set the top-level entity
set_global_assignment -name TOP_LEVEL_ENTITY led_top

# Assign clock pin
set_location_assignment PIN_W15 -to clk_50m
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk_50m

# Assign reset pin
set_location_assignment PIN_T14 -to rst_n
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to rst_n

# Assign LED pins (active-low)
set_location_assignment PIN_A12 -to led_n[0]
set_location_assignment PIN_B12 -to led_n[1]
set_location_assignment PIN_C12 -to led_n[2]
set_location_assignment PIN_D12 -to led_n[3]
set_location_assignment PIN_E12 -to led_n[4]
set_location_assignment PIN_F12 -to led_n[5]
set_location_assignment PIN_G12 -to led_n[6]
set_location_assignment PIN_H12 -to led_n[7]

set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to led_n[*]

这份 .qsf 文件是 Quartus II 工程的“宪法”,它用 Tcl 脚本语言精确地定义了所有物理约束。 set_location_assignment 命令指定了物理引脚, set_instance_assignment 命令则定义了电气标准。 led_n[*] 是一个通配符,表示对 led_n 总线的所有成员应用相同的 I/O 标准。

5.4 编译与下载

完成所有配置后,点击 Processing > Start Compilation 启动全编译。编译过程通常需要数分钟。成功后,你会在 prj/ 目录下看到 led_top.sof 文件。

最后,连接开发板的 USB-Blaster 下载器,点击 Tools > Programmer ,在 Programmer 窗口中选择 led_top.sof ,然后点击 Start 。几秒钟后,开发板上的 8 个 LED 就会以约 1 秒的间隔,开始流动起来。这一刻,你亲手用代码构造的硬件,真正活了过来。

我在实际项目中遇到过无数次因 .qsf 文件中一个引脚号写错而导致 LED 不亮的情况。排查过程往往是先检查代码逻辑,再检查原理图,最后才想到去核对 .qsf 文件。后来我养成了一个习惯:每次修改 .qsf 后,都会用 diff 工具与上一个已知正确的版本进行比对,确保万无一失。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐