文章目录
-
- 前言
- [一、ZYNQ 烧录文件的组成](#一、ZYNQ 烧录文件的组成)
-
- [1.1 裸机/裸跑程序](#1.1 裸机/裸跑程序)
- [1.2 Linux 系统](#1.2 Linux 系统)
- [二、核心概念:FSBL 是什么?](#二、核心概念:FSBL 是什么?)
-
- [2.1 FSBL 的定义](#2.1 FSBL 的定义)
- [2.2 FSBL 的核心职责](#2.2 FSBL 的核心职责)
- [2.3 FSBL 的生成时机](#2.3 FSBL 的生成时机)
- 三、更底层的起点:BootROM
-
- [3.1 BootROM 是什么?](#3.1 BootROM 是什么?)
- [3.2 BootROM 的核心职责](#3.2 BootROM 的核心职责)
- [3.3 为什么需要 BootROM?](#3.3 为什么需要 BootROM?)
- [3.4 BootROM 与 FSBL 的分工](#3.4 BootROM 与 FSBL 的分工)
- 四、完整的程序加载流程
-
- [4.1 启动链路全景图](#4.1 启动链路全景图)
- [4.2 简化版流程图](#4.2 简化版流程图)
- 五、常见问题与解答
-
- [Q1:什么情况需要烧录 .elf?](#Q1:什么情况需要烧录 .elf?)
- [Q2:既然 .elf 被打包在 BOOT.bin,为什么还要每次固化 .elf?](#Q2:既然 .elf 被打包在 BOOT.bin,为什么还要每次固化 .elf?)
- [Q3:XSA文件不变,是不是就不需要更换 .elf?](#Q3:XSA文件不变,是不是就不需要更换 .elf?)
- Q4:升级过程会自动校验吗?
- 六、升级文件的产生流程
-
- [6.1 完整的文件生成链路](#6.1 完整的文件生成链路)
- [6.2 不同场景下的升级文件](#6.2 不同场景下的升级文件)
- 七、实践:一键烧录脚本
-
- [7.1 使用说明](#7.1 使用说明)
- [7.2 运行日志](#7.2 运行日志)
- [7.3 U-Boot 命令详解](#7.3 U-Boot 命令详解)
- 八、总结
- [附录:常用 Flash 类型参数](#附录:常用 Flash 类型参数)
前言
在 ZYNQ 开发过程中,程序如何固化、如何升级是每个开发者都会遇到的问题。本文将以问答形式,系统梳理 ZYNQ 程序烧录相关的核心知识点,帮助你从原理到实践全面掌握。
一、ZYNQ 烧录文件的组成
1.1 裸机/裸跑程序
ZYNQ 的烧录文件通常打包成一个 BOOT.bin 文件,包含三个核心部分:
| 组成部分 | 文件格式 | 作用 |
|---|---|---|
| FSBL | .elf |
第一阶段引导加载程序,初始化PS端硬件 |
| 硬件逻辑 | .bit |
配置FPGA逻辑电路 |
| 用户程序 | .elf |
在ARM上运行的应用程序 |
这三部分按照 FSBL → BIT → ELF 的顺序打包成 BOOT.bin,顺序错误会导致启动失败。

1.2 Linux 系统
Linux 系统的启动文件更为复杂,通常包含:
- BOOT.bin:FSBL + .bit + U-Boot
- uImage:Linux内核镜像
- devicetree.dtb:硬件描述文件
- uramdisk.image.gz:根文件系统
二、核心概念:FSBL 是什么?
2.1 FSBL 的定义
FSBL 是 First Stage Boot Loader (第一阶段引导加载程序)的缩写。简单理解:ZYNQ 上电后,ARM 核心执行的第一段用户程序就是 FSBL。
2.2 FSBL 的核心职责
| 职责 | 具体内容 |
|---|---|
| 初始化PS端 | 配置ARM核心、DDR内存、时钟、中断等 |
| 加载PL端 | 把 .bit 文件配置到FPGA逻辑区域 |
| 加载用户程序 | 从Flash读取程序到DDR,然后跳转执行 |
| 支持安全启动 | 可选:验证镜像签名、解密等 |
2.3 FSBL 的生成时机
FSBL 是在 Vitis(旧称 SDK) 中生成的,流程如下:
text
Vivado硬件设计 → 导出XSA文件 → Vitis创建FSBL工程 → 编译生成fsbl.elf
关键点 :只要 Vivado 硬件设计变了(PS配置、DDR参数、引脚分配等),就必须重新生成 FSBL。
三、更底层的起点:BootROM
3.1 BootROM 是什么?
BootROM 是 ZYNQ 芯片内部固化、不可修改 的一段只读代码,位于芯片的 ROM 中。它是 ZYNQ 上电后执行的第一段代码。
可以把它理解为芯片的"硬件出厂设置"------类似电脑主板的 BIOS,但更底层。
3.2 BootROM 的核心职责
| 职责 | 说明 |
|---|---|
| 初始化基本硬件 | 配置 CPU、时钟等最基础的功能 |
| 读取启动模式 | 检测拨码开关,确定从 QSPI/SD卡/JTAG 等介质启动 |
| 加载 FSBL | 从启动介质读取 BOOT.bin 头部,加载到芯片内部 RAM(OCM) |
| 验证并跳转 | 检查 CRC 校验和,通过后跳转到 FSBL 执行 |
3.3 为什么需要 BootROM?
没有 BootROM,ZYNQ 上电后不知道该怎么启动。
核心原因:要从外部 Flash 读取代码,必须先有代码来初始化 Flash 控制器------这是一个"先有鸡还是先有蛋"的问题。BootROM 作为芯片内部预置的代码,解决了这个悖论。
3.4 BootROM 与 FSBL 的分工
| BootROM | FSBL | |
|---|---|---|
| 位置 | 芯片内部 ROM(固化) | 外部 Flash(可更新) |
| 是否可修改 | ❌ 不可修改 | ✅ 可以修改 |
| 主要任务 | 加载 FSBL | 初始化 DDR、配置 PL、加载用户程序 |
| 对开发者的影响 | 只需遵守其格式要求 | 需要根据硬件生成 |
一句话总结:BootROM 负责找到并加载 FSBL,然后就把控制权交出去了。开发者无需修改它,只需要按照它的要求打包 BOOT.bin 即可。
四、完整的程序加载流程
4.1 启动链路全景图
┌─────────────────────────────────────────────────────────────┐
│ 1. 上电复位 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 2. BootROM(芯片内部固定,不可修改) │
│ - 初始化最基本硬件(CPU、时钟等) │
│ - 读取启动模式拨码开关(QSPI/SD卡/JTAG) │
│ - 从启动介质读取 BOOT.bin 头部 │
│ - 验证 Header 的 CRC 校验和 │
│ - 将 FSBL 加载到 OCM(芯片内部 RAM) │
│ - 跳转到 FSBL │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 3. FSBL(存储在 Flash 中,用户可以修改) │
│ - 初始化 DDR 内存 │
│ - 配置 PL(加载 .bit 文件到 FPGA) │
│ - 从 Flash 读取用户程序到 DDR │
│ - 验证分区校验和 │
│ - 跳转到用户程序 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 4. 用户应用程序 │
│ - 裸机程序:直接运行 │
│ - Linux:运行 U-Boot → 加载内核 → 挂载文件系统 │
└─────────────────────────────────────────────────────────────┘
4.2 简化版流程图
上电复位
↓
┌─────────────────────────────────────────┐
│ BootROM(芯片固化,不可修改) │
│ 读取启动模式 → 加载FSBL → 验证 → 跳转 │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ FSBL(可更新) │
│ 初始化DDR → 配置PL → 加载用户程序 → 跳转 │
└─────────────────────────────────────────┘
↓
用户应用程序
五、常见问题与解答
Q1:什么情况需要烧录 .elf?
答案:FSBL(.elf文件)变了,或者 Flash 里没有 FSBL 时才需要。
| 情况 | 是否需要烧录.elf |
|---|---|
| Flash是空白的(第一次烧录) | ✅ 必须 |
| Vivado硬件设计变了 | ✅ 必须 |
| 只改了C代码(app.elf) | ❌ 不需要 |
| 只改了PL逻辑(.bit) | ❌ 通常不需要 |
| 量产时升级固件 | ❌ 不需要 |
Q2:既然 .elf 被打包在 BOOT.bin,为什么还要每次固化 .elf?
这是一个很容易混淆的问题。关键在于:烧录时指定的 -fsbl 参数,和打包在 BOOT.bin 里的 FSBL,是两个不同用途的 FSBL。
| 烧录工具使用的FSBL | 打包在BOOT.bin里的FSBL | |
|---|---|---|
| 存放位置 | 电脑硬盘上的.elf文件 |
BOOT.bin镜像内部 |
| 运行时间 | 烧录过程中临时运行 | 正常启动时运行 |
| 核心任务 | 为烧录工具提供运行环境 | 初始化系统并启动应用 |
| 是否需要频繁更新 | 不需要 | 看硬件是否变化 |
通俗理解:
- 烧录时的FSBL = U盘启动盘(制作一次可反复使用)
- BOOT.bin里的FSBL = C盘里的系统引导程序(随系统更新而变化)
Q3:XSA文件不变,是不是就不需要更换 .elf?
是的,完全正确。
只要 .xsa 文件(硬件平台描述文件)没有变化,用于烧录的 fsbl.elf 就完全不需要更换。
日常固件升级时,你只需要替换 BOOT.bin,fsbl.elf 可以视为一个固定不变的烧录"驱动"。
Q4:升级过程会自动校验吗?
是的,升级过程会自动进行校验。
校验分为两个层面:
- 烧录过程校验 (
program_flash工具):- 使用
-verify选项,写入后自动读取Flash内容并比对
- 使用
- 启动过程校验 (BOOT.bin内部机制):
- BootROM检查Header的CRC校验和
- FSBL检查每个分区的校验和
建议 :烧录命令中始终添加 -verify 选项,确保写入数据的完整性。
六、升级文件的产生流程
6.1 完整的文件生成链路
text
Vivado硬件设计
↓ 导出
XSA文件(硬件平台描述)
↓ Vitis创建工程
FSBL工程 → 编译 → fsbl.elf
↓
应用工程 → 编译 → app.elf
↓
Boot Image打包工具(bootgen)
↓
BOOT.bin(最终烧录文件)
6.2 不同场景下的升级文件
| 升级场景 | 需要替换的文件 | 是否需要换fsbl.elf |
|---|---|---|
| 应用程序更新 | BOOT.bin | 否 |
| FPGA逻辑更新 | BOOT.bin(包含新.bit) | 否(除非接口变化) |
| 硬件配置变化 | BOOT.bin + fsbl.elf | 是 |
| 首次烧录空白板 | BOOT.bin + fsbl.elf | 是 |
七、实践:一键烧录脚本
以下是一个带详细打印信息的烧录脚本,将所需文件放在同一文件夹,双击即可完成升级:
bash
@echo off
chcp 65001 > nul
title Zynq QSPI Flash 烧录工具
color 0A
echo ========================================
echo Zynq QSPI Flash 一键烧录脚本
echo ========================================
echo.
:: 设置Xilinx工具路径(请根据实际安装路径修改)
set PATH_XILINX=C:\Softwares\Xilinx\Vitis\2021.1\bin
:: 检查工具路径是否存在
if not exist "%PATH_XILINX%\program_flash" (
echo [错误] 找不到 program_flash
echo 请检查路径: %PATH_XILINX%
pause
exit /b 1
)
echo [1/5] Xilinx 工具路径检测: 通过
echo.
:: 检查当前目录下的文件
echo [2/5] 检查烧录文件...
set BIN_FILE=
set ELF_FILE=
for %%f in (*.bin) do (
set BIN_FILE=%%f
echo 找到 BIN 文件: %%f
)
for %%f in (*.elf) do (
set ELF_FILE=%%f
echo 找到 ELF 文件: %%f
)
echo.
:: 检查 .bin 文件是否存在
if "%BIN_FILE%"=="" (
echo [错误] 当前文件夹下没有找到 .bin 文件
echo 请确保 BOOT.bin 或其它 .bin 文件在此目录下
pause
exit /b 1
)
:: ========== 新增:强制要求 .elf 文件 ==========
if "%ELF_FILE%"=="" (
echo [错误] 当前文件夹下没有找到 .elf 文件
echo 烧录空白 Flash 时必须提供 fsbl.elf 文件
echo 请将 fsbl.elf 复制到当前目录后重试
echo.
echo 提示:如果 Flash 中已有 FSBL 且无需更新,
echo 请手动移除本脚本中的 .elf 检查逻辑
pause
exit /b 1
)
:: ============================================
echo [3/5] 文件校验通过
echo 将烧录 BIN: %BIN_FILE%
echo 使用 FSBL: %ELF_FILE%
echo.
:: 用户确认
echo [4/5] 请确认以下事项:
echo 1. 板卡已设置为 JTAG 模式
echo 2. 下载器已连接并上电
echo 3. 即将烧录文件: %BIN_FILE%
echo.
set /p confirm="确认继续烧录?(Y/N): "
if /i not "%confirm%"=="Y" (
echo 用户取消烧录
pause
exit /b 0
)
:: 开始烧录
echo.
echo [5/5] 正在烧录 QSPI Flash...
echo 请勿断开电源或 USB 连接...
echo.
call "%PATH_XILINX%\program_flash" -f "%BIN_FILE%" -offset 0 -flash_type qspi-x4-single -fsbl "%ELF_FILE%" -verify
:: 检查结果
if %errorlevel% neq 0 (
echo.
echo [错误] 烧录失败!错误代码: %errorlevel%
echo 请检查:
echo - 下载器是否连接正常
echo - 板卡是否上电
echo - flash_type 参数是否正确
pause
exit /b %errorlevel%
)
:: 烧录成功
echo.
echo ========================================
echo 烧录成功完成!
echo ========================================
echo.
echo 下一步操作:
echo 1. 断开电源
echo 2. 将板卡启动模式切换到 QSPI
echo 3. 重新上电,程序应从 Flash 启动
echo.
pause
7.1 使用说明
- 将上述脚本保存为
program_qspi.bat - 修改脚本中的
PATH_XILINX为你的实际安装路径 - 将
BOOT.bin和fsbl.elf放在同一文件夹

- 板卡设置 JTAG 模式,连接下载器并上电
- 双击
program_qspi.bat即可完成烧录
7.2 运行日志




7.3 U-Boot 命令详解
以下是嵌入式开发中最核心、最常用的 U-Boot 命令详解:
-
sf probe
-
格式:sf probe <总线编号> [<时钟频率>] [<SPI模式>]
-
总线编号:SPI 控制器的编号(通常是 0)
-
时钟频率:设置 SPI 时钟频率(Hz),
0表示使用 Flash 芯片的默认时钟频率(通常是 40-50 MHz) -
SPI 模式:SPI 工作模式(0-3)
模式 说明 0CPOL=0, CPHA=0(最常用) 1CPOL=0, CPHA=1 2CPOL=1, CPHA=0 3CPOL=1, CPHA=1
-
-
说明:U-Boot 中用于初始化和探测 SPI Flash 设备 的命令。这是执行任何 Flash 操作(读、写、擦除)前必须首先运行的命令。
-
-
sf erase
- 格式:sf erase <偏移地址> <长度>
- 偏移地址:Flash 中的起始位置(十六进制)
- 长度:要擦除的字节数(十六进制)
- 说明:U-Boot 中用于擦除 SPI Flash 数据的命令。由于 Flash 的物理特性,必须先擦除才能写入数据。
- 格式:sf erase <偏移地址> <长度>
-
sf write
- 格式:sf write <内存地址> <Flash偏移地址> <长度>
- 内存地址:数据在 DDR 内存中的源地址
- Flash偏移地址:写入 Flash 的目标起始地址
- 长度:要写入的字节数(十六进制)
- 说明:U-Boot 中用于将数据写入 SPI Flash 的命令。在执行此命令前,必须先执行
sf probe初始化 Flash,并且目标区域必须已经被擦除。
- 格式:sf write <内存地址> <Flash偏移地址> <长度>
-
sf read
- 格式:sf read <内存地址> <Flash偏移地址> <长度>
- 内存地址:数据要写入的 DDR 内存目标地址
- Flash偏移地址:Flash 中读取的起始位置
- 长度:要读取的字节数(十六进制)
- 说明:U-Boot 中用于从 SPI Flash 读取数据到内存的命令。这是验证烧录结果、备份数据、调试系统时最常用的命令之一。
- 格式:sf read <内存地址> <Flash偏移地址> <长度>
八、总结
| 知识点 | 核心要点 |
|---|---|
| BOOT.bin组成 | FSBL + .bit + app.elf(顺序固定) |
| BootROM的作用 | 芯片内部固化的引导代码,负责加载FSBL |
| FSBL的作用 | 上电后第一段用户程序,初始化硬件、加载FPGA、启动应用 |
| FSBL生成时机 | 在Vitis中从XSA文件编译生成 |
| 何时需要烧录.elf | XSA变化时,或首次烧录空白板 |
| 升级校验机制 | 烧录用-verify + BootROM/FSBL的CRC校验 |
| 日常升级操作 | 只需替换BOOT.bin,fsbl.elf固定不变 |
附录:常用 Flash 类型参数
| Flash类型 | program_flash参数 |
|---|---|
| 单路QSPI(4线) | qspi-x4-single(最常用) |
| 双路QSPI(2线) | qspi-x2-single |
| 双片并联(8线) | qspi-x8-dual_parallel |
本文基于 ZYNQ 7000 系列编写,UltraScale+ 系列原理相同,具体参数请参考对应文档。