第七章 点亮LED灯
开发环境搭好了,是时候真刀真枪跑一个完整实验了。本章以最简单的"点亮LED 灯"为例,手把手走通FPGA 开发的完整流程,同时验证上一章安装的软件能否正常工作。麻雀虽小,五脏俱全——这个实验覆盖了从需求分析到上板验证的全部环节。
本章在讲什么
- FPGA 开发流程——标准的12 步开发流程总览;
- FPGA 工程管理——工程文件的统一目录组织;
- 点亮LED 灯——完整实验:功能分析→硬件介绍→系统设计→模块设计→绘制波形图→编写RTL→Modelsim 仿真→Vivado 新建工程→设计输入→分析与综合→管脚约束→设计实现→下载验证;
- 本章总结与拓展训练。
核心概念拆解
1. FPGA 开发流程:12 步标准流程

- 需求分析:提取项目需要实现的功能;
- 系统设计:划分模块、梳理数据流和控制流、评估带宽和工作频率;
- 硬件选型:选择合适规模和性能的FPGA 芯片和板卡(与成本、容量、性能、熟悉度相关);
- 绘制系统框图:确定子模块结构层次和模块间交互信号;
- 绘制波形图:结合数据手册或接口协议,用绘图软件画出各模块的时序波形;
- 编写RTL 代码:严格按照波形图编写各模块代码;
- 软件仿真:编写TestBench 对RTL 仿真,对比仿真波形与手绘波形是否一致,不一致就改代码;
- 新建工程:在FPGA 开发平台新建工程并添加RTL 代码;
- 分析与综合:综合编译并查看报告,发现最常见的设计错误;
- 约束输入:对工程引脚进行约束(还有时序约束);
- 设计实现:布局布线,把综合生成的逻辑网表配置到具体FPGA 芯片上,生成下载文件;
- 上板验证及调试:下载到开发板验证现象,不正常则迭代前面的步骤或在线调试。
2. FPGA 工程管理
开发过程中会产生很多不同功能的文件,统一管理便于查找、维护和移植。所有工程按功能分四个文件夹:
led/ # 工程根目录(路径只能含英文、数字、下划线)
├── doc/ # 辅助文档(Visio 画的框图、波形图等)
├── prj/ # Vivado 工程文件
├── rtl/ # RTL 代码文件
└── sim/ # 仿真工程与仿真文件
注意:FPGA 开发工具对工程路径很敏感,路径中不能出现中文或特殊字符,否则无法识别。
3. 实验功能分析与硬件介绍
功能:按键PL_KEY0 控制PL_LED0 的亮灭——未按下时LED 常灭,按下点亮,松开熄灭。
硬件原理:
- PL_LED0:阴极经470Ω 限流电阻接地,阳极由PL IO 控制。IO 输出高电平1 点亮,低电平0 熄灭;
- PL_KEY0:一端接地,另一端经10K 电阻(限流,防止按下时电源直接接地短路)接+3.3V。未按下为高电平,按下为低电平。


4. 系统设计与模块设计
本实验只需一个输入(key)一个输出(led),单模块即可实现,不需要模块划分。(实际项目通常需要多模块配合,系统架构设计能实现多人协同、提高效率、便于管理移植。)
用Visio 绘制模块框图:

5. 绘制波形图
由硬件特性可知:key=1(未按下)时led=0(灭);key=0(按下)时led=1(亮)。真值表:
| 输入 PL_KEY | 输出 PL_LED |
|---|---|
| 1 | 0 |
| 0 | 1 |
波形图绘制习惯:绿色字体=输入信号,红色字体=输出信号,蓝色字体=中间变量。

6. 编写RTL 代码
推荐用Notepad++ 编写代码(比Vivado 自带编辑器灵活:查找替换方便、有输入提示、保存的代码文件适用多家FPGA 平台)。在rtl 文件夹新建led.v:
module led(
input key , //输入按键
output led //输出LED 灯
);
//*****************************************************
//** main code
//*****************************************************
//上电按键默认高电平,LED 灯保持常灭
//按键被按下,按键值为低电平,LED 灯被点亮
assign led = ~key; //将按键的值取反后赋值给LED 灯
endmodule
核心就一行:assign led = ~key;——组合逻辑,把按键值取反赋给LED。
7. Modelsim 手动仿真
TestBench 的三个任务:①向被测模块输入接口添加激励;②对被测模块顶层接口进行信号例化;③判断输出是否满足设计预期。
在sim 文件夹下新建tb 子文件夹,创建tb_led.v:
`timescale 1ns / 1ns //仿真单位/仿真精度
module tb_led();
//reg define
reg key;
//wire define
wire led;
//信号初始化
initial begin
key <= 1'b1; //按键上电默认高电平
//key 信号变化
#200 //延迟200ns
key <= 1'b1; //按键没有被按下
#1000
key <= 1'b0; //按键被按下
#600
key <= 1'b1;
#1000
key <= 1'b0;
end
//例化led 模块
led u_led(
.key (key),
.led (led)
);
endmodule
TB 要点:
timescale 1ns/1ns定义仿真时间单位,#200即延迟200ns;- 输入信号定义为reg 类型,输出信号定义为wire 类型;
- 初始化与板载按键默认状态一致(高电平),再用延迟语句模拟按键按下/释放;
- 例化被测模块,把激励传入。
Modelsim 操作流程:
- File→New→Project,工程名建议按"tb_+模块名"命名(如tb_led),路径选工程的sim 文件夹;
- 选择"Add Existing File"添加led.v 和tb_led.v(加错了可右键Remove from Project);
- Compile→Compile All 编译全部。编译状态有三种:√(通过)、红×(语法错误,按提示修改)、√+黄三角(通过但有警告——功能仿真时影响不大,但可能在综合和时序仿真中造成错误,建议也修改);
- Simulate→Start Simulation,在Design 标签选择work 库中的tb_led 模块,勾选"Enable optimization",并点击"Optimization Options"对所有模块应用完全可见性(full 调试模式)——不配置的话Modelsim SE 2020.4 会报错;
- 右键u_led→Add Wave 添加信号到波形窗口;
- 设置仿真时间10us,点Run 运行。
仿真按钮:Restart(复位仿真,改代码重编译后不必重开仿真界面)、Run Length+Run(按设定时长运行)、ContinueRun(继续运行)、Run-All(一直运行直到Stop)、Break、Stop。注意:纯组合逻辑(无时钟)的工程用Run-All 只能跑到激励结束,想一直运行必须有持续时钟输入。
常用小工具:放大/缩小/全局显示三个放大镜按钮;黄色竖线标记工具(可对齐到信号上升/下降沿)。
仿真结果与手绘波形对比一致,说明代码功能正确:

提醒:仿真复杂工程里的IP 核时,必须事先将IP 核的库文件加载到Modelsim 库,否则会因缺少库文件报错。
8. Vivado 新建工程
- 打开Vivado→Create Project→Next;
- 工程名"led"(名字要能反映功能),路径选led/prj,取消勾选"Create project subdirectory"(因为我们已自己管理目录结构,不需要Vivado 再建同名文件夹);
- 工程类型选"RTL Project"(最常用的正常设计流程类型),不勾选"Do not specify sources at this time"和"Project is an extensible Vitis platform"。其他类型:Post-synthesis Project(导入第三方综合网表)、I/O Planning Project(早期IO 规划)、Imported Project(从ISE 等导入)、Example Project(模板工程);
- 添加源文件:Target language 选Verilog,Simulator language 选Mixed,点Add Files 添加led.v;
- 约束文件页直接Next(之后创建);
- 选择芯片型号:7020 核心板输入
xc7z020clg400-2,7010 核心板输入xc7z010clg400-1。也可按特征筛选:Category(种类)、Family(系列)、Package(封装clg400)、Speed(速度等级:7020 为-2,7010 为-1)、Temperature(温度等级:7020 为I 工业级,7010 为C 商业级)、Static power(静态功耗——上电后配置完成但用户逻辑未运行时的功耗,工艺尺寸越小静态功耗越大)。一般选Family+Package+Speed 三项即可定位型号; - Summary 页核对后Finish,进入Vivado 主界面。
Vivado 主界面主要窗口:Flow Navigator(从设计输入到比特流的全流程命令入口)、Sources/Netlist(源文件与网表的分层视图)、Properties(所选对象特性)、工作区(Project Summary、Text Editor、Schematic、Device、Package)、结果区(Tcl Console、Messages、Log、Reports、Designs Runs)、主工具栏/主菜单/布局选择器。
9. 设计输入
双击Sources→Design Sources 下的led.v 可查看编辑代码。字体太小可在Settings→Text Editor→Fonts and Colors 中修改Size(默认12,可改为20),修改自动保存。改代码后记得Ctrl+S 保存(保存按钮变灰、文件名的"*"消失)。Vivado 每次保存都会做部分语法检查;顶层模块一般自动识别,也可右键源文件→Set as Top 手动指定。
10. 分析与综合 + 管脚约束
点击Flow Navigator 中"Open Elaborated Design",Vivado 编译RTL 并全面检查语法,Messages 窗口显示Error 和Warning:有Error 必须修改重编;Warning 需逐一查看判断是设计缺陷还是可忽略。(本实验有一个因未用Vivado 默认路径存放源文件导致的警告,不影响功能。)
打开I/O Planning 布局,在I/O Ports 窗口进行IO 分配。主要配置项:Name(端口名)、Direction(方向)、Package Pin(引脚号)、I/O Std(电平标准)、Bank、Drive Strength(驱动强度,默认12mA)、Slew Type(边沿快慢)、Pull Type(上下拉)等。
如何查引脚:查开发板IO 引脚总表,或在底板原理图中搜索"PL_KEY0"和"PL_LED0"——得到PL_KEY0 为L14、PL_LED0 为H15,都在Bank 35;再查核心板原理图确认Bank 35 供电为3.3V,故电平标准用LVCMOS33:
| 信号名 | 方向 | 管脚 | 说明 | 电平标准 |
|---|---|---|---|---|
| key | input | L14 | PL_KEY0(底板) | LVCMOS33 |
| led | output | H15 | PL_LED0(底板) | LVCMOS33 |
两种约束方法:
方法一:在I/O Ports 窗口图形化填写,Ctrl+S 保存为led.xdc。
方法二:手动创建约束文件(Sources 窗口"+"→Add or create constraints→Create File,命名led),然后手敲XDC 命令:
#IO 管脚约束
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports key]
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports led]
XDC 文件规则:#开头是注释(每条注释独占一行);每条约束命令也独占一行,结尾不需要分号;set_property 是命令名,PACKAGE_PIN L14 指定引脚位置,IOSTANDARD LVCMOS33 指定电平标准,[get_ports key] 指定约束对象。
时序约束举例(本实验无时钟,暂不需要):
#时钟周期约束
create_clock -name clk -period 20 [get_ports sys_clk]
create_clock 创建时钟:名称clk、周期20ns(50MHz)、时钟源sys_clk。时序约束就是告诉EDA 工具输入时钟的频率,工具才能按要求优化布局布线。周期应等于或略低于实际值,不能设大,否则布局布线满足了要求但实际功能可能不满足。建议无论设计简单与否都做时序约束,设计复杂或时钟频率高时不约束很容易出意外状况。
约束完成后,点击"Run Synthesis"综合。综合就是把RTL 设计转变成由查找表(LUT)、触发器(FF)等底层电路单元组成的网表,综合器还会做优化(如删除多余逻辑)。
综合完成选"Open Synthesized Design"查看综合后原理图——能直观看到代码与电路的对应关系:IBUF 是输入缓存(Vivado 自动加给输入信号,把管脚信号送入FPGA 内部),OBUF 是输出缓存(送出信号),取反通过一个LUT1 实现。
综合设计界面其他选项:Constraints Wizard(约束向导)、Edit Timing Constraints(编辑时序约束)、Set Up Debug(在线调试)、Report Timing Summary/Clock Networks/Clock Interaction(时序/时钟报告)、Report Methodology(设计方法检查)、Report DRC(设计规则检查)、Report Utilization(资源利用率)、Report Power(功耗)、Schematic(原理图)。
11. 设计实现
点击"Run Implementation"进行布局布线。实现完成后选"Open Implemented Design"查看:Netlist 窗口有Nets 和Leaf Cells 两类选项,点击后右边器件图会高亮对应模块——实现就是把代码映射到了FPGA 的底层资源上。
12. 下载验证
- 点击"Generate Bitstream"生成比特流文件(.bit 后缀);
- 点击"Open Hardware Manager"进入硬件管理;
- 硬件连接:Xilinx 下载器一端接电脑,另一端接开发板JTAG 接口;接电源适配器,打开电源开关。BOOT 拨码开关设为JTAG 模式(1:ON、2:ON);

- 点击Hardware 子窗口的"Auto Connect"连接开发板(未识别到芯片可按论坛提供的排查方法检查);
- 点"Program Device",Bitstream File 自动识别工程比特流,点"Program"下载(默认勾选"Enable end of startup check"下载完成校验,下载失败会返回错误提示);
- 验证现象:PL_LED0 常灭;按下PL_KEY0,LED 点亮;松开,LED 熄灭。与设计预期一致,实验成功!

重要提醒——程序固化:比特流下载后断电即丢失。ZYNQ 无法单独固化比特流文件,因为非易失性存储器(SD 卡、QSPI Flash)的引脚是PS 端专用引脚,只能由PS 的ARM 处理器驱动——需将bit 流文件和elf 文件(软件程序下载文件)合成BOOT.BIN 才能固化,这要用到嵌入式Vitis 开发流程(见《领航者ZYNQ 之嵌入式Vitis 开发指南》的"程序固化实验")。
想深入学习Vivado 可参考官方手册:UG895(System-Level Design Entry)、UG906(Design Analysis and Closure Techniques)。
初学者容易踩的坑
- 工程路径含中文:新建工程时路径必须全英文,含中文工程会创建失败;
- 忘了取消"Create project subdirectory":勾选后Vivado 会自动建同名文件夹管理文件,与自建目录结构冲突(虽然不是错误,但会造成文件混乱);
- 仿真报错不知原因:Modelsim SE 2020.4 仿真前必须在Optimization Options 里对所有模块应用完全可见性(full),否则会报错;
- Run-All 跑不完:纯组合逻辑无时钟时仿真只能跑到激励结束,这不是Bug;
- 引脚约束错或漏约束:引脚号和电平标准都要对——从原理图查引脚号,从Bank 供电电压定电平标准(本例都是LVCMOS33);
- 时序约束周期设得比实际大:布局布线轻松满足约束,但实际功能可能不满足;周期应等于或略小于实际值;
- 以为程序能断电保存:JTAG 下载的比特流断电即失,固化需要走Vitis 的BOOT.BIN 流程;
- 跳过仿真直接上板:仿真阶段发现问题的成本远低于上板后排查,养成"先仿真后上板"的习惯。
小结 & 下一步
本章完整走了一遍FPGA 开发流程:需求分析(按键控制LED)→硬件原理(LED 高电平亮、按键按下为低)→绘制模块框图和波形图→编写RTL(一行assign 取反)→Modelsim 编写TestBench 仿真验证→Vivado 新建工程(选对芯片型号)→分析综合(IBUF/OBUF/LUT1)→管脚约束(XDC:L14/H15/LVCMOS33)→设计实现(布局布线)→生成比特流→Hardware Manager 下载→上板验证成功。同时还学会了工程目录管理(doc/prj/rtl/sim)和两种约束方法。
拓展训练:脱离本文,自己独立按流程复现"点亮LED 灯"实验。
下一章将系统学习Verilog HDL 语法,为编写更复杂的逻辑设计打好语言基础。