第七章 点亮LED灯

开发环境搭好了,是时候真刀真枪跑一个完整实验了。本章以最简单的"点亮LED 灯"为例,手把手走通FPGA 开发的完整流程,同时验证上一章安装的软件能否正常工作。麻雀虽小,五脏俱全——这个实验覆盖了从需求分析到上板验证的全部环节。

本章在讲什么

  1. FPGA 开发流程——标准的12 步开发流程总览;
  2. FPGA 工程管理——工程文件的统一目录组织;
  3. 点亮LED 灯——完整实验:功能分析→硬件介绍→系统设计→模块设计→绘制波形图→编写RTL→Modelsim 仿真→Vivado 新建工程→设计输入→分析与综合→管脚约束→设计实现→下载验证;
  4. 本章总结与拓展训练

核心概念拆解

1. FPGA 开发流程:12 步标准流程

图7.1.1 FPGA 开发流程图

  1. 需求分析:提取项目需要实现的功能;
  2. 系统设计:划分模块、梳理数据流和控制流、评估带宽和工作频率;
  3. 硬件选型:选择合适规模和性能的FPGA 芯片和板卡(与成本、容量、性能、熟悉度相关);
  4. 绘制系统框图:确定子模块结构层次和模块间交互信号;
  5. 绘制波形图:结合数据手册或接口协议,用绘图软件画出各模块的时序波形;
  6. 编写RTL 代码严格按照波形图编写各模块代码;
  7. 软件仿真:编写TestBench 对RTL 仿真,对比仿真波形与手绘波形是否一致,不一致就改代码;
  8. 新建工程:在FPGA 开发平台新建工程并添加RTL 代码;
  9. 分析与综合:综合编译并查看报告,发现最常见的设计错误;
  10. 约束输入:对工程引脚进行约束(还有时序约束);
  11. 设计实现:布局布线,把综合生成的逻辑网表配置到具体FPGA 芯片上,生成下载文件;
  12. 上板验证及调试:下载到开发板验证现象,不正常则迭代前面的步骤或在线调试。

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。未按下为高电平,按下为低电平

图7.3.2 PL_LED0 灯电路原理图

图7.3.3 PL_KEY0 按键电路原理图

4. 系统设计与模块设计

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

用Visio 绘制模块框图:

图7.3.6 模块框图与端口

5. 绘制波形图

由硬件特性可知:key=1(未按下)时led=0(灭);key=0(按下)时led=1(亮)。真值表:

输入 PL_KEY 输出 PL_LED
1 0
0 1

波形图绘制习惯:绿色字体=输入信号,红色字体=输出信号,蓝色字体=中间变量

图7.3.12 点亮LED 灯实验波形图

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 操作流程

  1. File→New→Project,工程名建议按"tb_+模块名"命名(如tb_led),路径选工程的sim 文件夹;
  2. 选择"Add Existing File"添加led.v 和tb_led.v(加错了可右键Remove from Project);
  3. Compile→Compile All 编译全部。编译状态有三种:√(通过)、红×(语法错误,按提示修改)、√+黄三角(通过但有警告——功能仿真时影响不大,但可能在综合和时序仿真中造成错误,建议也修改);
  4. Simulate→Start Simulation,在Design 标签选择work 库中的tb_led 模块,勾选"Enable optimization",并点击"Optimization Options"对所有模块应用完全可见性(full 调试模式)——不配置的话Modelsim SE 2020.4 会报错;
  5. 右键u_led→Add Wave 添加信号到波形窗口;
  6. 设置仿真时间10us,点Run 运行。

仿真按钮:Restart(复位仿真,改代码重编译后不必重开仿真界面)、Run Length+Run(按设定时长运行)、ContinueRun(继续运行)、Run-All(一直运行直到Stop)、Break、Stop。注意:纯组合逻辑(无时钟)的工程用Run-All 只能跑到激励结束,想一直运行必须有持续时钟输入。

常用小工具:放大/缩小/全局显示三个放大镜按钮;黄色竖线标记工具(可对齐到信号上升/下降沿)。

仿真结果与手绘波形对比一致,说明代码功能正确:

图7.3.46 仿真结果

提醒:仿真复杂工程里的IP 核时,必须事先将IP 核的库文件加载到Modelsim 库,否则会因缺少库文件报错。

8. Vivado 新建工程

  1. 打开Vivado→Create Project→Next;
  2. 工程名"led"(名字要能反映功能),路径选led/prj,取消勾选"Create project subdirectory"(因为我们已自己管理目录结构,不需要Vivado 再建同名文件夹);
  3. 工程类型选"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(模板工程);
  4. 添加源文件:Target language 选Verilog,Simulator language 选Mixed,点Add Files 添加led.v;
  5. 约束文件页直接Next(之后创建);
  6. 选择芯片型号: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 三项即可定位型号;
  7. 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. 下载验证

  1. 点击"Generate Bitstream"生成比特流文件(.bit 后缀);
  2. 点击"Open Hardware Manager"进入硬件管理;
  3. 硬件连接:Xilinx 下载器一端接电脑,另一端接开发板JTAG 接口;接电源适配器,打开电源开关。BOOT 拨码开关设为JTAG 模式(1:ON、2:ON)

图7.3.127 开发板连接实物图

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

图7.3.132 点亮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)。

初学者容易踩的坑

  1. 工程路径含中文:新建工程时路径必须全英文,含中文工程会创建失败;
  2. 忘了取消"Create project subdirectory":勾选后Vivado 会自动建同名文件夹管理文件,与自建目录结构冲突(虽然不是错误,但会造成文件混乱);
  3. 仿真报错不知原因:Modelsim SE 2020.4 仿真前必须在Optimization Options 里对所有模块应用完全可见性(full),否则会报错;
  4. Run-All 跑不完:纯组合逻辑无时钟时仿真只能跑到激励结束,这不是Bug;
  5. 引脚约束错或漏约束:引脚号和电平标准都要对——从原理图查引脚号,从Bank 供电电压定电平标准(本例都是LVCMOS33);
  6. 时序约束周期设得比实际大:布局布线轻松满足约束,但实际功能可能不满足;周期应等于或略小于实际值;
  7. 以为程序能断电保存:JTAG 下载的比特流断电即失,固化需要走Vitis 的BOOT.BIN 流程;
  8. 跳过仿真直接上板:仿真阶段发现问题的成本远低于上板后排查,养成"先仿真后上板"的习惯。

小结 & 下一步

本章完整走了一遍FPGA 开发流程:需求分析(按键控制LED)→硬件原理(LED 高电平亮、按键按下为低)→绘制模块框图和波形图→编写RTL(一行assign 取反)→Modelsim 编写TestBench 仿真验证→Vivado 新建工程(选对芯片型号)→分析综合(IBUF/OBUF/LUT1)→管脚约束(XDC:L14/H15/LVCMOS33)→设计实现(布局布线)→生成比特流→Hardware Manager 下载→上板验证成功。同时还学会了工程目录管理(doc/prj/rtl/sim)和两种约束方法。

拓展训练:脱离本文,自己独立按流程复现"点亮LED 灯"实验。

下一章将系统学习Verilog HDL 语法,为编写更复杂的逻辑设计打好语言基础。