2. Quartus工程结构解析

Quartus Prime 工程并不是一个单一文件,而是一组围绕“工程”这一概念协同工作的文件集合。理解这些文件各自的职责、生成顺序和依赖关系,是做好工程目录组织与版本控制的前提。很多团队在接手他人工程时出现“打开 .qpf 后编译报错”“换台电脑就找不到器件”“.sof 与 .pof 混用导致配置失败”等问题,根源往往是对工程文件体系缺乏清晰认识。

一、工程文件的分层职责

Quartus 工程文件可以按“描述—约束—中间—产物”四个层次来理解:

  • .qpf(Quartus Project File):工程入口文件,记录工程名称、顶层实体名、器件系列、Quartus 版本、修订版本(Revision)等元信息。双击它即可打开整个工程。它本身很小,但决定了“用哪个器件、哪个顶层、哪个版本”来编译。
  • .qsf(Quartus Settings File):工程设置与约束文件,是工程中信息量最大的文本文件。它包含引脚分配、时序约束(SDC 引用)、器件型号、编译选项、源文件列表、IP 引用、全局赋值等。.qsf 是纯文本、可读、可 diff,是版本控制的核心对象。
  • .qpf 与 .qsf 的关系:一个 .qpf 可以对应多个修订版本,每个修订版本有独立的 .qsf。例如 uart_top.qpf 下可存在 uart_top.qsf(默认修订)与 uart_top_fast.qsf(高速修订),通过 Quartus 的 Revision 切换。
  • .sof(SRAM Object File):编译生成的 SRAM 配置目标文件,用于 JTAG 在线下载到 FPGA 的 SRAM 中,掉电即失。适合调试阶段反复下载。
  • .pof(Programmer Object File):面向配置闪存(EPCS/EPCQ 等)的编程文件,通过 JTAG 间接写入配置芯片,上电后由配置芯片加载 FPGA。适合量产固化。
  • .jic(JTAG Indirect Configuration):由 .sof 转换而来,用于通过 JTAG 将配置数据写入配置闪存,是 .pof 之外的另一条常用固化路径。

此外还有若干中间与辅助文件:.qws(工作区,记录打开的窗口布局)、.qpgm(编程器设置)、db/(编译数据库)、incremental_db/(增量编译数据库)、output_files/(编译输出)、greybox_tmp/(灰色盒临时文件)等。这些文件大多可由工具重新生成,不应纳入版本控制。

Quartus 工程文件体系与编译数据流 描述层 .qpf 工程入口 .v / .sv / .vhd 源码 约束层 .qsf 设置与引脚 .sdc 时序约束 中间层 db/ 编译数据库 incremental_db/ 产物层 .sof / .pof / .jic .rbf / .hex Quartus 编译引擎 Analysis & Synthesis Fitter(布局布线) Assembler Timing Analyzer 读取 .qsf / .sdc 输出 .sof / .pof JTAG 在线调试 .sof → FPGA SRAM 掉电丢失,适合迭代 SignalTap / 在线调试 配置闪存固化 .pof / .jic → EPCS/EPCQ 上电自动加载 适合量产交付 版本控制友好文件 .qpf / .qsf / .sdc / .v .ip / .tcl / .qip 实线:编译与下载数据流 虚线:版本控制纳入关系

二、工程目录组织建议

一个可维护的 Quartus 工程目录,应当把“人写的”和“工具生成的”严格分开。推荐结构如下:

uart_project/
├── prj/                    # 工程文件
│   ├── uart_top.qpf
│   ├── uart_top.qsf
│   └── uart_top.sdc
├── rtl/                    # 设计源码
│   ├── uart_top.v
│   ├── uart_tx.v
│   └── uart_rx.v
├── ip/                     # IP 与约束
│   ├── pll_50m.qip
│   └── pll_50m/
├── sim/                    # 仿真
│   ├── tb_uart_top.v
│   └── wave.do
├── scripts/                # 自动化脚本
│   ├── build.tcl
│   └── program.tcl
├── output_files/           # 编译产物(不纳入版本控制)
├── db/                     # 编译数据库(不纳入版本控制)
└── .gitignore

关键原则有三点:第一,源码与工程文件分离,.qsf 中引用源文件时使用相对路径,避免绝对路径导致换机失败;第二,产物与中间文件不入库,通过 .gitignore 排除 db/、incremental_db/、output_files/、greybox_tmp/、*.qws、*.qpgm;第三,IP 目录整体保留,因为 .qip 与 IP 生成文件共同决定 IP 的可复现性。

三、版本控制友好型布局要点

Quartus 的 .qsf 是纯文本,但默认会包含大量自动生成的赋值语句,直接 diff 会产生噪声。建议在 .qsf 中启用 set_global_assignment -name AUTO_EXPORT... 相关选项,或使用 Tcl 脚本统一管理设置,使 .qsf 保持精简。同时,引脚约束建议单独放在 .qsf 的引脚段或独立的 .tcl 中,便于按板卡版本切换。

实践建议:在 .qsf 中显式写入 set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files,把所有编译产物集中到 output_files/,这样 .gitignore 只需排除一个目录即可,工程根目录保持干净。

四、.sof 与 .pof 的选择判断

调试阶段优先使用 .sof:通过 JTAG 直接下载到 FPGA SRAM,下载速度快、无需擦写配置芯片,适合反复迭代。当设计稳定、需要脱机运行时,再生成 .pof 或 .jic 写入配置闪存。判断依据可以简化为:是否需要掉电保持——需要则用 .pof/.jic,不需要则用 .sof。需要注意的是,.pof 的生成依赖器件是否支持主动配置以及配置芯片型号,在 .qsf 中需正确设置 CONFIGURATION_DEVICE 等参数。

常见误区:把 .sof 直接改名为 .pof 使用,或把 .pof 当作 .sof 通过 JTAG 下载到 SRAM,都会导致配置失败。两者格式与用途不同,必须通过 Quartus 的 Convert Programming Files 工具进行转换。

五、可复现构建的最小文件集

要让另一位工程师仅凭版本库就能完整重建工程,至少需要提交以下文件:.qpf、.qsf、.sdc、所有 RTL 源码、IP 的 .qip 及其生成文件、必要的 .tcl 脚本、以及一份说明器件型号与 Quartus 版本的 README。其余文件均可由工具重新生成。这样既保证了可复现性,又避免了仓库膨胀。

掌握这套文件体系后,后续的编译流程、增量编译、SignalTap 调试以及交付打包,都会建立在清晰的工程结构之上,减少因文件混乱带来的返工。