Vivado FPGA工程创建全流程:从Verilog到比特流下载
写 Verilog 也有段时间了前面几篇笔记一直在讲语法、计数器、状态机这些偏逻辑层面的东西但很多朋友留言说代码能看懂一打开 Vivado 就懵不知道从哪儿下手建工程。这篇笔记我就把基于 Vivado 创建 FPGA 工程这件事从头到尾拆开讲一遍从软件安装、工程目录规划、写 Verilog、写 Testbench 仿真到约束、综合、生成比特流、下载到板子每一步该干什么、为什么要这么干一次说清楚。不管你是刚接触 FPGA 的学生还是想从软件转硬件的工程师这篇笔记都能帮你把“从零到能跑”这条路走通。我尽量不堆术语该解释的地方用大白话补上遇到坑也会直接告诉你。1. 工程创建这件事为什么值得单独写一篇很多教程喜欢直接甩一句“打开 Vivado点 Create Project下一步下一步就完事了”好像创建工程是一件无脑操作。但实际用下来你会发现工程创建的背后藏着整套 FPGA 开发流程的骨架这里面的细节直接影响你后面几个小时的调试幸福感。1.1 一个FPGA项目的完整生命周期FPGA 开发听起来很硬核其实流程非常套路化。先写好 Verilog 描述电路功能然后用仿真工具验证逻辑对不对再通过约束文件把代码里的信号绑定到芯片实际的引脚上接着让工具做综合和布局布线生成比特流最后下载到芯片里运行。这一整条链路里工程创建是第一步你把工程结构搭得清爽后面每一环都会省力不少。以 Xilinx现在叫 AMD的 Vivado 为例一个工程实际上是一个管理框架它帮你组织源文件、约束文件、IP 核、仿真文件和生成结果。如果你不建工程直接拿命令行跑综合也不是不行但对绝大多数人来说图形界面加上工程管理能少记一堆命令和路径配置这才是主流用法。1.2 别小看“工程目录规划”这个动作我见过不少新手工程建完以后所有文件一股脑堆在默认目录里到了后面要复用模块、要打包给别人、要版本管理的时候那叫一个痛苦。我自己的习惯是每个工程单独一个文件夹里面按src、sim、constrs、ip、docs几个子目录归好类。Vivado 的工程文件本身放在一个子目录里叫project或者vivado_prj这样就算工程文件坏了源文件也还在重新建一个工程分分钟。提示Vivado 对中文路径的支持一直不太好建议工程路径和所有源文件路径全部用英文不要出现“新建文件夹”这种中文目录。这个坑我踩过波形仿真和 IP 生成经常会莫名其妙报错最后排查半天发现是路径里带中文。2. Vivado 的下载安装与版本选择既然要基于 Vivado 创建工程第一步自然是把软件准备好。这一步没什么技术含量但里面有几个容易忽略的点我说一下我的经验。2.1 版本那么多该装哪个Vivado 版本更新很快每年都会出新的比如 2024.2、2025.1 这种命名方式。版本之间差异不小但核心操作界面和逻辑基本一致所以不必纠结“装新版还是旧版”。我的建议是如果你是跟着学校课程或某本书学习那老师或书里用的哪个版本你就装哪个版本免得工程文件版本不兼容打不开。如果是自己玩装最新的稳定版就行官网上直接下载。这里要提一句Vivado 有三种版本WebPACK免费版、Design Edition、System Edition。对于学习和小型项目WebPACK 完全够用它支持主流的中小容量器件比如 Artix-7、Spartan-7 系列。你只要用 AMD 官网账号登录申请一个免费的 WebPACK license 就能正常用不需要额外花钱。注意安装时体积很大完整版可能几十 GB。如果硬盘紧张选 “Vivado HL WebPACK” 安装即可别装那些用不到的器件系列能省不少空间。2.2 安装过程中的典型失败与对策很多人卡在安装这一步最常见的问题有三个第一安装到一半提示缺少依赖库尤其 Linux 系统。解决办法是提前把系统依赖包装好Windows 底下相对省心。第二下载速度慢。官网下载服务器在国外国内经常很慢。除了用浏览器直接下也可以试试用下载工具开多线程或者找个网络好的时间段再下。第三安装路径不要有中文这个前面说过还包括不能有空格吗空格其实问题不大但为了保险建议用纯英文路径。还有一个小问题安装时如果提示winpcap或wireshark相关组件失败这通常不影响 Vivado 主流程但会影响网络抓包调试功能。如果你是做以太网相关项目就需要单独补装 WinPcap否则可以忽略。3. 创建工程的完整实操流程现在进入正题打开 Vivado创建一个全新工程。我以 2024.2 版本为例界面布局大同小异。3.1 从 Create Project 到工程选项配置启动 Vivado 后在欢迎页直接点击 “Create Project”会弹出向导。第一步让你填工程名称和路径。工程名称建议用能表达功能的英文短词比如led_blinky、uart_rx_top不要用proj1、test这种毫无辨识度的名字。路径就选你规划好的目录。接下来是选择工程类型默认是 “RTL Project”我们学 Verilog 选这个就行。如果你要基于现成的 Block Design 做 Vivado 的 IP 集成可以选 “Block Design”但那是后续话题。然后是选择目标器件。这里有两种方式一种是根据开发板型号选比如很多入门板用的 Artix-7 系列里的 XC7A35T另一种是通过开发板厂商提供的 Board 文件直接选板卡。如果你用的是最常见的开发板网上能找到对应板卡的 Board 文件装上以后在这里可以直接选Vivado 会自动把板载时钟引脚、LED、按键这些约束配好省很多事。没有 Board 文件也没关系手动选芯片型号就行。3.2 添加源文件别急着点 Finish向导中间会问你要不要现在就添加源文件。很多人在这里直接跳过等工程建完再手动加。也可以但我建议把写好的 Verilog 文件在这里就添加进去这样工程一创建完就能直接看到代码。约束文件XDC也可以在这里添加但通常一个工程会有多个约束文件我习惯先加主约束后面再补。注意这一步只是把现有文件引入工程如果你要新建文件点 “Add Sources” 窗口里的 “Create File” 也行。Vivado 会允许你创建.v文件并直接打开编辑器。最后确认没有遗漏点 “Finish”工程生成了。3.3 工程界面速览工程创建完成后左边会有一个 “Flow Navigator” 面板里面从上到下排列着Project Manager、IP Integrator、Simulation、RTL Analysis、Synthesis、Implementation、Program and Debug。这就是整个开发流程的导航栏每一步都能点进去。刚打开工程时你看到的是 Sources 窗口显示你的设计文件和约束文件上面有个层级视图可以点展开看模块间的调用关系。右上角的 “Tcl Console” 窗口很值得注意你在图形界面上做的每一步操作Vivado 都会生成对应的 Tcl 命令显示在这里。时间长了你会发现用 Tcl 脚本建工程比用鼠标点高效得多我后面也有计划写一两篇 Tcl 脚本的笔记。4. 在工程里写第一个 Verilog 模块工程建好了现在往里面填代码。我建议用最有代表性的“LED 流水灯”作为入门实例麻雀虽小五脏俱全它涉及时钟、复位、计数器和输出引脚恰好覆盖了 FPGA 开发的基本要素。4.1 一个规范的模块骨架不啰嗦直接给代码。新建一个led_blinky.vmodule led_blinky( input wire clk, input wire rst_n, output reg [3:0] led ); parameter CLK_FREQ 50_000_000; parameter TOGGLE_MS 500; reg [31:0] cnt; wire tick; // 时钟计数产生 500ms 的脉冲 always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 32d0; else if (cnt CLK_FREQ / 1000 * TOGGLE_MS - 1) cnt 32d0; else cnt cnt 1b1; end assign tick (cnt CLK_FREQ / 1000 * TOGGLE_MS - 1); // 每个 tick 翻转一次 LED实现流水 always (posedge clk or negedge rst_n) begin if (!rst_n) led 4b1000; else if (tick) led {led[2:0], led[3]}; end endmodule看起来很简单但注意两个细节。第一计数器宽度我用了 32 位因为 50MHz 时钟下要数 25_000_000 次10 位计数器的最大值 1023 根本不够所以我先估算需要的计数范围再决定位宽。写 Verilog 时位宽不够或者溢出是新手最常见的隐藏 bug。第二流水灯用的是循环移位{led[2:0], led[3]}这个写法比led led 1更直观也比用移位加判断分支更简洁。你可能会问为什么不用led led 1因为左移后最低位永远是 0没法循环。4.2 时钟与复位的处理原则在这个模块里所有 always 块都用posedge clk作为触发这是同步设计的核心习惯。异步复位rst_n采用低有效这也是大多数开发板按键复位的默认极性。实际项目中复位信号建议做异步复位、同步释放处理防止复位释放时与时钟沿竞争产生亚稳态。这里先用最简形式后面讲高级主题时再展开。写代码的时候顺便提一个 Vivado 编辑器的小技巧它能自动补全信号名也能实时检查语法错误。如果你的代码中模块名字和文件名不一致Vivado 通常会给出 warning但不会报错。为了工程整洁强烈建议一个文件只放一个模块且文件名与模块名保持一致。4.3 在 Vivado 里写代码的体验与技巧Vivado 的文本编辑器不算特别强但它集成了语言模板Language Templates可以从菜单Edit里调出来里面有各种常用语句和原语模板比如 FIFO、BRAM 的例化模板对刚写代码的人来说非常友好。另一个技巧是写完代码后按CtrlS保存Vivado 会自动做一次 RTL 语法检查错误会显示在 “Messages” 窗口。如果有红色 error点进去可以直接跳到报错位置。我个人习惯是用 VS Code 写 Verilog再用 Vivado 做编译仿真。Vivado 也能启用外部编辑器在Tools - Settings - Text Editor里设置这样既能享受 VS Code 的插件补全和格式化又能保留 Vivado 的工程管理能力相当顺手。5. 仿真验证不仿真就跑板是在赌运气工程里代码写完第一件事不是综合下载而是仿真。很多人觉得仿真麻烦直接往板子上烧点几个灯亮了就觉得万事大吉。但一个复杂项目如果不在仿真阶段尽量把逻辑 Bug 揪出来等上了板子再用示波器、ILA 调试效率低到你想哭。5.1 Testbench 到底在模拟什么Testbench 可以理解成一个“虚拟的测试平台”它不是你要设计的电路而是用来给被测模块DUT喂输入信号的。你需要在这里产生时钟、复位、按顺序给输入数据然后观察输出是否符合预期。Vivado 自带的仿真器xsim简单易用完全够应付入门和多数开发场景。写 Testbench 的要点是只要在 initial 块里把激励描述清楚再 generate 时钟剩下的就是跑波形。直接看例子。5.2 流水灯的 Testbench 实例新建一个tb_led_blinky.vtimescale 1ns / 1ps module tb_led_blinky(); reg clk; reg rst_n; wire [3:0] led; // 生成 50MHz 时钟 initial clk 1b0; always #10 clk ~clk; // 复位信号 initial begin rst_n 1b0; #100; rst_n 1b1; end // 例化被测模块 led_blinky dut( .clk(clk), .rst_n(rst_n), .led(led) ); endmodule注意这个模块内部没有initial $finish仿真会永远跑下去。你在 Vivado 仿真设置里可以限定仿真时间比如跑 10ms。因为我们的流水灯设计是 500ms 才翻转一次 LED如果你没修改参数直接跑 10ms波形里什么变化都看不到你会以为代码错了。这时候可以在 Testbench 里临时把参数改小比如把TOGGLE_MS改成 5只要记住这会在综合时被真实参数覆盖即可。5.3 仿真波形调试的关键操作点击 Flow Navigator 里的Run Simulation - Run Behavioral SimulationVivado 会自动把 Testbench 添加到仿真源文件并启动。仿真窗口出来后默认只有你的模块端口信号如果你要观察内部信号比如cnt右键点击模块里的信号选择Add to Waveform即可。这里有个实用技巧在 Waveform 窗口里可以用鼠标中键缩放也可以用Ctrl 滚轮。信号多了以后可以给波形分组比如把时钟复位放一组数据信号放一组看起来清爽很多。还有如果仿真长时间没反应看看是不是不小心把TOGGLE_MS设成了 500 毫秒时钟周期只有 20ns你跑 10ms 仿真需要仿真 50 万个时钟周期xsim 虽然能跑但热敏波形看起来像一条直线容易让人误判。提示写 Testbench 时一定要把timescale 写对timescale 1ns / 1ps 表示仿真精度到皮秒。如果精度设置太粗一些短脉冲信号可能被过滤掉你的仿真结果会和预期不符。6. 约束文件与综合实现仿真通过只代表功能正确但 FPGA 是物理器件你要告诉编译器代码里的clk信号实际接到芯片的哪个物理引脚上LED 又是哪几个引脚。这就是约束文件的作用。6.1 XDC 约束语法速通Vivado 的约束文件后缀是.xdc本质是一堆 Tcl 命令。最常用的两条是create_clock和set_property看例子# 定义时钟 create_clock -name sys_clk -period 20.0 [get_ports clk] # 引脚约束 set_property PACKAGE_PIN E3 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property PACKAGE_PIN F1 [get_ports led[0]] set_property IOSTANDARD LVCMOS33 [get_ports led[0]]第一行create_clock很关键它告诉编译器这个时钟的频率是多少综合工具会根据这个约束去做时序分析。如果你不写时钟约束综合也能过但时序报告会告诉你没有时钟约束设计时序完全没保障。我见过有人不写时钟约束导致上板后偶发卡顿最后发现是高频率设计没有加约束导致的时序违例。PACKAGE_PIN是芯片引脚编号IOSTANDARD是电气标准。FPGA 的引脚不能乱接一定要对照开发板的原理图来查。不同开发板的 LED 引脚完全不同所以千万不要从网上随便抄一个约束必须用自己的板子原理图。6.2 综合、实现与生成比特流的流程写完约束保存文件接下来就按照 Flow Navigator 一步步点Run Synthesis综合完成后会生成网表文件。如果综合报错通常有语法错误或端口不匹配回到 RTL 检查即可。综合通过后点Run Implementation这一步会把逻辑电路映射到 LUT、FF 等真实资源上并进行布局布线。实现完成后可以打开Report Timing Summary看看有没有时序违例。最后点Generate Bitstream生成.bit位流文件。如果前面步骤都通过这一步通常很快。生成完成后Vivado 会提示你可以打开硬件管理器进行烧录。6.3 综合与实现阶段的高频报错综合和实现阶段最常见的错误有两个。第一个是[Synth 8-3331] design xxx has unconnected port意思是某个端口没连上。这个一般是顶层模块例化子模块时漏掉了端口检查一下实例化代码。第二个是[Place 30-574] Poor placement for routing between IO pin and ...这个和引脚约束不合理有关通常是因为你把某个差分信号约束到了普通 IO 脚或者引脚分配的物理位置距离对应的逻辑太远。还有一类问题与「生成比特流失败」有关最常见原因是综合阶段选中了Vivado Synthesis但Implementation阶段出现 DRC 错误。遇到 DRC 错误直接在排错窗口点开错误详情大多数会告诉你哪个引脚约束冲突或者时钟没有连接。按提示把 XDC 改对再重跑即可。7. 下载到开发板与在线调试一切顺利的话你现在手里有了一份.bit比特流下一步就是把代码灌进开发板看灯是不是按预期亮起来。7.1 连接硬件与烧录步骤用 JTAG 线通常 USB 转 JTAG连接开发板和电脑打开Open Hardware Manager。如果连不上板子常见原因有两个一是驱动没装好尤其 Windows 系统插上开发板后右键“设备管理器”看是否识别为Digilent USB Device或类似名称如果显示带问号的设备手装一下对应驱动。第二个原因是开发板供电不足这种通常表现为 JTAG 连接有概率掉线换个 USB 口或者用外部电源供电能解决。识别到设备后点Program Device选择生成的.bit文件然后点击 Program几秒钟之后板上的 LED 就应该开始跑了。7.2 用 ILA 抓实时波形如果你遇到板上现象不对但仿真又没问题这时候 ILAIntegrated Logic Analyzer能救你。ILA 是 Xilinx 提供的一个在线调试 IP 核它可以把 FPGA 内部信号的实时波形抓出来通过 JTAG 上传到 Vivado 显示。在 Vivado 里使用 ILA 的方法不难可以在 Block Design 里实例化 ILA 的 IP也可以直接在 RTL 代码里通过综合属性来标记要抓取的信号。简单说在你的代码里对某个 wire 或 reg 加上(* mark_debug true *)综合后打开Set Up Debug向导工具会自动帮你创建 ILA 核。下载以后在 Hardware Manager 里点击运行触发条件就能看到内部信号的波形。不过我要提醒一下ILA 会消耗额外的 Block RAM 和逻辑资源所以不要一路 mark_debug 标记几十个信号抓取深度也不用设太大1024 一般够用了。调试完记得把 mark_debug 去掉再重新生成比特流否则最终版本会占用很多资源。8. 工程创建路上的高频问题速查表最后把我在做 FPGA 工程时碰到过、以及周围同学同事问得最多的问题整理一下做成一个速查表方便你以后直接翻。现象可能原因解决办法Vivado 打不开工程提示文件版本太新工程由更高版本 Vivado 创建升级 Vivado 或让高版本生成兼容版本综合报大量cannot find port顶层模块端口与例化不对应检查模块例化名和端口名大小写仿真波形全是高阻 Z被测模块未正确复位或时钟未翻转检查 Testbench 里时钟生成和复位释放下载时找不到 JTAG 设备USB 驱动问题或线材问题更换 USB 线或重装 Digilent 驱动生成比特流失败报 DRC引脚约束冲突或时钟未约束看 DRC 详情修正 XDC板上运行与仿真结果不一致时钟频率过高或时序违例查看时序报告降低时钟频率或优化代码Vivado 界面全英文看不懂暂时没有官方改中文的方法用浏览器查单词或者习惯英文界面源文件路径含中文导致 IP 生成失败Vivado 对中文路径支持差把工程移到全英文路径并重新添加源文件计数器位宽不够导致溢出计数范围的位宽估算错误用$clog2()或者手工计算最大计数值这个表远远没有覆盖所有坑但覆盖了新手期的大部分烦恼。遇到问题先别慌看 Messages 窗口把红色和橙色消息点开看具体内容80% 的错误自己能解决。9. 工程管理的一些个人习惯写到这里该收尾了。最后分享几个我在实操里养成的习惯可能对你有用。第一个习惯是每个工程建好之后第一时间把src、sim、constrs目录建好并放入一个 README 文件用一两句话记录这个工程是干什么的、板上哪块芯片、有什么坑。别小看这个动作等你三个月后再打开一个老工程你会感谢自己当初写下的那句话。第二个习惯是每次修改代码前先编译一次确保基线是正确可用的再动手改。就像做版本管理不要在代码已经报错的基础上继续开发否则你很难分清新错误到底是谁引入的。推荐同时把工程纳入 Git 管理Vivado 的工程文件很庞大但核心的源码、约束、脚本和 XDC 必须入库。第三个习惯是多花点时间理解 Vivado 生成的 log 文件。综合和实现之后工具会在工程目录下生成runme.log里面记录了所有警告和详细信息。很多问题图形界面没显示但 log 里写得明明白白。学着看 log你的排错能力会提升一大截。做 FPGA 开发工程创建只是万里长征第一步但这一步走得稳后面整个流程都会顺畅很多。现在我每次在 Vivado 里建工程都会先在 Tcl Console 里敲几条命令把源文件目录加进去熟练之后一分钟就能搭好一个干净的框架。希望这篇笔记也能帮你把这一步走扎实后面继续写代码、调仿真的时候就再也不会被“怎么建工程”这种问题绊住脚了。