六盘水市网站建设_网站建设公司_虚拟主机_seo优化
2025/12/24 4:52:46 网站建设 项目流程

从零构建 Zynq-7000 Linux 启动系统:基于 Vivado 2018.3 的实战手记

最近接手了一个老项目——在一块基于Zynq-7020的定制板上移植嵌入式 Linux。客户明确要求使用Vivado 2018.3工具链,不许用 PetaLinux,必须手动搭建整个启动流程。

说实话,现在都 2025 年了,还用这么“复古”的方式干活,一开始我也有点抵触。但真动手做完一遍才发现:这不仅是技术复盘,更是一次对嵌入式底层机制的深度理解之旅。今天就把我踩过的坑、总结的经验,毫无保留地分享出来。


为什么选择 Vivado + 手动构建?而不是直接上 PetaLinux?

PetaLinux 确实方便,三行命令就能生成一个完整的镜像。但它像一个黑箱——你知其然,不知其所以然。

而当我们面对的是非标准硬件、需要极致裁剪、或要搞软硬协同加速时,只有亲手走完 FSBL → U-Boot → Kernel → Rootfs 这条完整路径,才能真正掌控系统每一环的行为

更何况,很多工业现场的老项目还在维护,你躲不开这些“古早”工具链。掌握它,不是怀旧,是生存技能。


第一步:用 Vivado 搭建 PS 系统 —— 别小看这个“配置向导”

打开 Vivado 2018.3,新建工程,添加ZYNQ7 Processing SystemIP,进入配置界面。别急着点“OK”,这里有太多细节决定成败。

关键设置清单(以常见 Zynq-7020 板卡为例)

配置项推荐值说明
PS ClocksCPU_6OR4X_CLK = 666.66MHzARM 核运行在 667MHz
FCLK_CLK0 = 100MHz给 PL 提供时钟,常用于 QSPI 或 GPIO 中断
DDR ConfigurationDDR3, 800MHz (400MHz x2)必须和你的物理内存颗粒匹配
MIO Selection启用 UART0, SD0, GigE, QSPI常用外设
禁用未使用的 MIO 引脚减少干扰
USB Reset PolarityActive Low很多开发板 USB 芯片复位是低有效,手册没写清楚!
SDIO 0 CD/PDConnected to MIO[8]/MIO[9]千万别忘了插卡检测脚,否则 SD 卡热插拔会出问题

🛠️血泪教训:有一次我忽略了FCLK_CLK0的使能,结果 QSPI Flash 死活读不出来。查了一整天才发现 PL 没有时钟驱动 AXI_QSPI 控制器!

输出什么?.hdf还是.xsa

在 Vivado 2018.3 中,默认导出的是.hdf文件(Hardware Description File),这是 SDK 能识别的格式。到了后来版本才主推.xsa

记住一句话:

.hdf是给 Xilinx SDK 用的“硬件说明书”,没有它,FSBL 和 U-Boot 就不知道你的 DDR 地址在哪、串口连了哪个 MIO。

最后记得生成Bitstream并导出到 SDK,哪怕你 PL 部分暂时为空,也得走完这一步。


第二步:编译 FSBL —— 第一阶段引导程序的本质

FSBL(First Stage Boot Loader)听起来高大上,其实它的任务非常简单:

  1. 被 ROM Code 从 Flash 加载进 OCM(0xFFFF0000)
  2. 初始化 DDR
  3. 把 U-Boot 从 Flash 搬到 DDR
  4. 跳过去执行

就这么四步。但它必须跑赢时间——因为 OCM 只有 256KB,代码不能膨胀。

如何生成 FSBL 工程?

在 Xilinx SDK 中:
- 新建 Application Project
- 选择模板:Zynq FSBL
- 导入前面生成的.hdf

SDK 会自动从embeddedsw目录复制源码并创建工程。

调试技巧:让 FSBL “说话”

默认情况下 FSBL 是静默的。一旦卡住,你就只能“盲调”。解决办法很简单:

// 在 fsbl_debug.h 中取消注释 #define FSBL_DEBUG_INFO

然后重新编译。你会在串口看到类似输出:

Starting FSBL... Initializing DDR... Copying Linux Image to DDR... Jumping to U-Boot at 0x3000000...

如果只打印到“Initializing DDR”就停了?那基本可以锁定是DDR 参数配错了,回去检查 MIG 设置。


第三步:交叉编译 U-Boot —— 自己动手,丰衣足食

U-Boot 是整个系统的“指挥官”。它负责加载内核、解析设备树、提供命令行接口,甚至可以通过 TFTP 实现远程更新。

获取正确的源码版本

别随便 git clone 一个 u-boot master 分支!你要找的是:

git clone https://github.com/Xilinx/u-boot-xlnx.git git checkout xilinx-v2018.1

为什么是 v2018.1?因为Vivado 2018.3 工具链配套的就是这个版本。混用新旧版本可能导致设备树兼容性问题。

编译流程

make ARCH=arm distclean make ARCH=arm xilinx_zynq_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

最终你会得到几个关键文件:
-u-boot: ELF 格式,可用于 JTAG 调试
-u-boot.bin: 二进制镜像,烧写进 Flash 使用

让 U-Boot 自动启动 Linux

我们当然不想每次上电都手动敲命令。编辑include/configs/zynq-common.h或通过环境变量设置自动启动脚本:

setenv bootcmd 'fatload mmc 0:1 0x3000000 uImage; fatload mmc 0:1 0x2A00000 system.dtb; bootm 0x3000000 - 0x2A00000' setenv bootargs 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait' saveenv

解释一下:
-fatload mmc 0:1 xxxxx: 从 SD 卡第一个 FAT32 分区读取文件
-uImage: 内核镜像(zImage 经 mkimage 封装)
-system.dtb: 设备树二进制
-bootm: 启动内核,-表示无 initrd

保存后,U-Boot 会在倒计时结束时自动执行bootcmd


第四步:定制设备树 —— 描述你的硬件真相

很多人觉得设备树神秘,其实它就是一份硬件说明书 JSON 化。Linux 内核靠它知道:“哦,原来 UART0 接在 MIO14/15 上,而且有个 AXI GPIO 在 0x41200000。”

设备树结构拆解

Zynq 的设备树通常由两部分组成:

  1. 基础描述文件zynq-7000.dtsi(由 Xilinx 提供,定义 PS 内部资源)
  2. 板级描述文件my_board.dts(你写的,补充 PL 外设和引脚映射)

编译命令:

dtc -I dts -O dtb -o system.dtb my_board.dts

或者集成进内核编译体系:

make ARCH=arm zynq_my_board.dtb

实战案例:把 PL 端的 AXI GPIO 写进设备树

假设你在 Vivado 里加了个 AXI GPIO IP,地址分配为0x41200000,想让它在 Linux 下可用。

/ { amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; axi_gpio_led: gpio@41200000 { compatible = "xlnx,axi-gpio-2.0"; reg = <0x41200000 0x10000>; xlnx,all-inputs = <0>; xlnx,all-outputs = <1>; xlnx,signal-width = <8>; gpio-controller; #gpio-cells = <2>; }; }; };

编译后放进 SD 卡,启动 Linux,就可以用 sysfs 控制 LED 了:

echo 8 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio8/direction echo 1 > /sys/class/gpio/gpio8/value # 点亮

⚠️ 注意事项:
- 地址必须与 Vivado Address Editor 完全一致
- 如果 PL 没有实现该 IP,却留在设备树中,内核可能卡死
- 修改设备树后一定要重新编译.dtb,否则无效!


最终系统是如何一步步启动起来的?

让我们把所有环节串起来,看看通电瞬间发生了什么:

  1. 上电复位
    - Zynq ROM Code 开始执行(固化在芯片内部)
    - 检测启动模式(QSPI / SD / JTAG)

  2. 加载 FSBL
    - 从 Flash(如 QSPI)读取 FSBL 到 OCM(0xFFFF0000)
    - 执行 FSBL

  3. FSBL 初始化 DDR
    - 配置 MIO、时钟、DDR 控制器
    - 将 U-Boot.bin 从 Flash 拷贝至 DDR(比如 0x3000000)

  4. 跳转到 U-Boot
    - FSBL 跳转至0x3000000
    - U-Boot 初始化外设、恢复环境变量

  5. U-Boot 加载内核
    - 从 SD 卡读取uImagesystem.dtb
    - 放入指定内存地址
    - 执行bootm启动内核

  6. Linux 内核接管
    - 解析设备树,初始化驱动
    - 挂载根文件系统(EXT4)
    - 启动init,进入用户空间

整个过程就像接力赛,每一棒都不能掉链子。


常见问题与调试秘籍

❌ 问题1:串口无输出

  • ✅ 检查 MIO 是否启用了 UART0?
  • ✅ 波特率是不是 115200?
  • ✅ 电源是否稳定?尤其是 VCCPINT
  • ✅ 使用 JTAG 调试,查看 PC 指针停在哪

❌ 问题2:卡在“DDR init”阶段

  • ✅ 回去检查 MIG 配置,频率、CL 值是否匹配实际颗粒
  • ✅ 供电电压是否达标(DDR3 通常是 1.5V ±0.075V)
  • ✅ PCB 走线等长控制如何?差太远会导致采样失败

❌ 问题3:U-Boot 能启动,但内核不起来

  • ✅ 检查bootargs中的root=参数是否正确(mmcblk0p2vsmmcblk1p2
  • .dtb是否包含正确的根文件系统分区信息?
  • ✅ uImage 是否真的存在?可以用ls mmc 0:1查看 SD 卡内容

🔧 调试利器推荐

工具用途
串口线 + minicom最基础也是最重要的日志来源
JTAG + Xilinx SDK Debugger查寄存器、看堆栈、设断点
TFTP + NFS免烧卡快速测试内核和根文件系统
逻辑分析仪抓 QSPI/SPI 时序,确认 Flash 通信正常

写在最后:这套方法还有价值吗?

有人可能会问:现在都有 Vitis、PetaLinux、Yocto,谁还这么原始地一个个编译?

我的回答是:正因为高级工具太智能,我们才更需要理解底层原理

当你遇到以下情况时,这种“手工派”技能就会救命:
- 客户只要最小系统,连根文件系统都不想要
- 需要在启动早期做安全验证(比如验签 U-Boot)
- PL 要和 PS 做超低延迟通信,必须精确控制启动顺序
- 老项目维护,文档缺失,只能逆向分析.bit.bin

掌握这套流程,意味着你不再是一个“点按钮工程师”,而是真正理解 Zynq 启动机制的开发者。

如果你正在学习嵌入式 Linux 移植,不妨放下 PetaLinux,亲手试一次从 Vivado 到 Shell 的全过程。相信我,那种“终于看到 login prompt”的成就感,无与伦比。

💬互动时间:你在移植 Zynq Linux 时遇到过哪些奇葩问题?欢迎留言分享,我们一起排坑!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询