一、概述
本文档详细记录了将UART循环打印程序固化到Zynq FPGA开发板的完整流程。通过FSBL(First Stage Boot Loader)引导程序,实现上电后自动运行UART打印程序。
二、固化前准备
2.1 硬件准备
-
Zynq开发板(如ZedBoard、ZCU102等)
-
USB-JTAG下载器
-
USB转串口线
-
存储介质(SD卡或QSPI Flash)
-
电源适配器
2.2 软件准备
-
Vivado Design Suite(包含Vitis)
-
串口调试助手(如Putty、SecureCRT)
-
硬件描述文件(.xsa文件)
三、文件系统结构
3.1 所需文件清单
text
固化项目/
├── fsbl_platform/ # FSBL平台工程
├── uart_app/ # UART应用程序工程
├── hardware/
│ └── system.bit # 硬件比特流文件
└── output/
├── fsbl.elf # FSBL引导程序
├── uart_app.elf # UART应用程序
├── system.bit # 硬件比特流
├── boot.bif # 引导镜像描述文件
└── boot.bin # 最终固化文件
3.2 文件说明
| 文件名 | 后缀 | 作用 | 来源 |
|---|---|---|---|
| fsbl.elf | .elf | 第一阶段引导程序 | Vitis生成 |
| uart_app.elf | .elf | UART应用程序 | Vitis编译 |
| system.bit | .bit | 硬件配置比特流 | Vivado导出 |
| boot.bif | .bif | 引导镜像描述文件 | 手工创建 |
| boot.bin | .bin | 最终固化文件 | bootgen生成 |
四、详细操作步骤
4.1 步骤1:创建FSBL引导程序
4.1.1 在Vitis中创建Platform Project
-
打开Vitis软件
-
File → New → Platform Project
-
输入项目名称:
fsbl_platform -
选择硬件描述文件(.xsa文件)
-
点击Finish
4.1.2 编译FSBL
-
在Project Explorer中右键点击
fsbl_platform -
选择Build Project
-
等待编译完成
4.1.3 获取fsbl.elf文件
编译完成后,文件位于:
text
fsbl_platform/export/fsbl_platform/sw/fsbl_platform/boot/fsbl.elf
4.2 步骤2:创建UART应用程序
4.2.1 创建Application Project
-
File → New → Application Project
-
项目名称:
uart_app -
选择平台:
fsbl_platform -
选择模板:Hello World(自动生成main.c)
-
点击Finish
4.2.2 替换代码
-
打开
uart_app/src/main.c -
删除原有代码,粘贴以下UART测试代码:
c
#include <stdio.h>
#include "xil_printf.h"
#include "xil_types.h"
#include "xparameters.h"
#include "xuartps.h"
// 全局变量
XUartPs UartInst;
u8 data_buffer[64];
u32 counter = 0;
// 初始化UART
int init_uart() {
XUartPs_Config *Config;
int Status;
// 查找UART配置
Config = XUartPs_LookupConfig(XPAR_PS7_UART_0_DEVICE_ID);
if (Config == NULL) {
return XST_FAILURE;
}
// 初始化UART
Status = XUartPs_CfgInitialize(&UartInst, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置波特率115200
XUartPs_SetBaudRate(&UartInst, 115200);
// 自检
Status = XUartPs_SelfTest(&UartInst);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
// 主函数
int main() {
int i;
// 初始化UART
if (init_uart() != XST_SUCCESS) {
return -1;
}
// 发送启动信息
XUartPs_Send(&UartInst, (u8*)"\r\n=== UART Test Application ===\r\n", 34);
XUartPs_Send(&UartInst, (u8*)"Starting continuous transmission...\r\n", 39);
// 主循环
while (1) {
// 填充数据
for (i = 0; i < 64; i++) {
data_buffer[i] = (counter + i) & 0xFF;
}
counter++;
// 发送数据
XUartPs_Send(&UartInst, data_buffer, 64);
// 发送换行
XUartPs_Send(&UartInst, (u8*)"\r\n", 2);
// 延时约1秒
for (i = 0; i < 10000000; i++);
}
return 0;
}
4.2.3 验证链接地址
-
打开
uart_app/lscript.ld链接脚本 -
确认关键配置:
ld
MEMORY { ps7_ddr_0 : ORIGIN = 0x00100000, LENGTH = 0x1FF00000 /* ... */ } .text : { *(.text) } > ps7_ddr_0 /* 确保.text段在DDR中 */ -
关键点 :应用程序起始地址必须是 0x00100000
4.2.4 编译应用程序
-
右键点击
uart_app项目 -
选择 Build Project
-
验证编译成功(无错误)
4.3 步骤3:验证应用程序地址
4.3.1 方法一:使用objdump命令
bash
# 在Vitis Terminal中执行
arm-none-eabi-objdump -h uart_app.elf | grep ".text"
正确输出示例:
text
.text 00000500 00100000 00100000 00001000 2**2
关键检查 :第三列(VMA)必须是 00100000
4.3.2 方法二:查看.map文件
-
打开
uart_app/Debug/uart_app.map -
搜索
.text查看地址
4.4 步骤4:生成BOOT.BIN文件
4.4.1 准备三个必需文件
-
fsbl.elf - 从FSBL项目复制
-
system.bit - 从Vivado硬件工程导出
-
uart_app.elf - 从UART项目编译得到
将三个文件复制到同一目录(如 output/)
4.4.2 创建BIF描述文件
在 output/ 目录下创建 boot.bif 文件,内容:
text
the_ROM_image:
{
[bootloader] fsbl.elf
system.bit
uart_app.elf
}
4.4.3 生成BOOT.BIN
方法A:使用Vitis GUI
-
菜单栏:Xilinx → Create Boot Image
-
点击 Add 依次添加:
-
fsbl.elf(类型选择bootloader) -
system.bit -
uart_app.elf
-
-
设置输出路径
-
点击 Create Image
方法B:使用命令行
bash
cd output
bootgen -image boot.bif -arch zynq -o boot.bin -w on
4.4.4 验证生成结果
-
生成的文件:
boot.bin -
文件大小:约3-5MB
-
如果失败,检查文件顺序和类型设置
4.5 步骤5:烧写到存储设备
4.5.1 方法一:SD卡启动(测试用)
-
格式化SD卡为FAT32格式
-
复制boot.bin到SD卡根目录
-
设置开发板为SD卡启动模式(跳线设置)
-
插入SD卡,连接串口线
-
上电启动
4.5.2 方法二:QSPI Flash固化(永久)
-
连接JTAG和串口线
-
打开Vivado → Hardware Manager
-
Open Target → Auto Connect
-
右键开发板 → Add Configuration Memory Device
-
选择Flash型号(常见:
mt25ql256aba) -
选择
boot.bin文件 -
点击 Program,等待完成
-
重要:烧写完成后
-
断开JTAG
-
关闭电源
-
设置为QSPI启动模式
-
重新上电
-
4.6 步骤6:测试验证
4.6.1 串口设置
-
串口软件:Putty/SecureCRT
-
波特率:115200
-
数据位:8
-
停止位:1
-
校验位:None
-
流控制:None
4.6.2 预期输出
上电后串口应输出:
text
=== UART Test Application ===
Starting continuous transmission...
[然后是连续的十六进制数据]
4.6.3 成功标志
-
上电后自动运行,无需JTAG
-
串口连续输出数据
-
断电重启后仍然正常运行
五、故障排除
5.1 常见问题及解决
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无串口输出 | 波特率不匹配 | 检查串口设置为115200 |
| 卡在FSBL阶段 | 应用程序地址错误 | 验证.elf文件起始地址为0x00100000 |
| 程序不运行 | BIN文件生成顺序错误 | 确保顺序:fsbl → bit → app |
| 启动失败 | 启动模式设置错误 | 检查开发板跳线设置 |
| 部分运行后停止 | 堆栈大小不足 | 修改链接脚本增加堆栈空间 |
5.2 调试技巧
5.2.1 添加调试信息
在应用程序开头添加:
c
// 在main函数开始处
XUartPs_Send(&UartInst, (u8*)"App Started!\r\n", 14);
5.2.2 验证硬件连接
c
// 强制发送测试字符
volatile u32 *uart = (u32*)0xE0001000;
*uart = 'A';
5.3 关键检查点清单
-
FSBL编译成功(fsbl.elf存在)
-
应用程序编译成功(uart_app.elf存在)
-
应用程序起始地址为0x00100000
-
boot.bif文件顺序正确
-
boot.bin生成成功
-
SD卡为FAT32格式
-
boot.bin在SD卡根目录
-
开发板启动模式正确
-
串口波特率115200
-
串口线TX/RX连接正确
六、进阶配置
6.1 修改启动延时
在FSBL中可修改启动延时,给用户中断引导的机会:
c
// 在fsbl_main.c中添加
int timeout = 3; // 3秒延时
while (timeout-- > 0 && !CheckUartInput()) {
fsbl_printf("Booting in %d seconds...\r\n", timeout);
Delay(1000);
}
6.2 多应用程序支持
可通过修改FSBL实现多应用程序选择:
c
// 根据按键选择不同应用
if (ReadBootPin() == 0) {
// 启动UART测试程序
HandoffAddress = 0x00100000;
} else {
// 启动其他应用程序
HandoffAddress = 0x00200000;
}
6.3 安全性增强
-
添加应用程序校验和验证
-
实现安全启动流程
-
加密固件保护
七、注意事项
7.1 重要提醒
-
不要修改FSBL的跳转逻辑,除非你完全理解其工作原理
-
应用程序不能使用地址0x00000000-0x000FFFFF区域(FSBL使用)
-
固化前务必先用SD卡测试,确认程序能正常运行
-
QSPI Flash烧写后必须断电重启,不能热复位
7.2 文件管理建议
-
保留每个版本的.elf和.bin文件
-
记录每次固化的配置参数
-
使用版本控制系统管理源码
7.3 性能优化
-
减少启动时间:优化应用程序初始化代码
-
减小固件大小:移除不必要的库和代码
-
提高可靠性:添加看门狗和错误恢复机制
八、附录
8.1 相关命令参考
bash
# 查看ELF文件信息
arm-none-eabi-objdump -h file.elf
arm-none-eabi-readelf -a file.elf
# 生成反汇编代码
arm-none-eabi-objdump -d file.elf > disassembly.txt
# 查看文件大小
arm-none-eabi-size file.elf
8.2 常用内存地址
| 地址范围 | 用途 | 说明 |
|---|---|---|
| 0x00000000-0x0003FFFF | FSBL区域 | 引导程序使用 |
| 0x00100000-0x001FFFFF | 应用程序区 | 主程序放置位置 |
| 0xE0000000-0xE00FFFFF | 外设寄存器 | UART、GPIO等 |
8.3 开发板启动模式设置
| 开发板 | SD卡模式 | QSPI模式 | JTAG模式 |
|---|---|---|---|
| ZedBoard | MIO5-0: 111010 | MIO5-0: 000010 | MIO5-0: 111111 |
| ZCU102 | SW6: 000010 | SW6: 001010 | SW6: 111111 |
| PYNQ-Z2 | J12: SD模式 | J12: QSPI模式 | J12: JTAG模式 |
文档版本 : 1.0
最后更新 : 2026年01月
适用平台 : Xilinx Zynq-7000系列
注意事项: 本流程基于Vitis 2019.2版本,其他版本可能略有差异