• 正文
  • 相关推荐
申请入驻 产业图谱

ZYNQ入门很容易出bug的一段知识

11/09 08:48
22
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

板子上电,串口一片沉默。这时候你最需要的不是玄学重启,而是搞清楚这三段代码各自负责什么、谁把谁拉起来的。

01为什么第三期讲启动

因为"板子起不来"是新手期最高频、也最消耗耐心的故障。

灯不亮、串口没输出、串口有输出但卡在某一行——三种现象对应的故障点是完全不同的位置。绝大多数人这一步靠猜:换张 SD 卡、重新烧一次、拔插电源、换个串口助手,运气好就过了。

但如果一开始就知道 Zynq 的启动是三段接力,你可以用很确定的方式定位到哪一段断的。

02它到底是什么

https://github.com/Xilinx/u-boot-xlnx

Zynq-7000 上电后的完整链条是这样的:

上电 / 复位
↓
BootROM(芯片内部固化的代码,你改不了)
↓  读启动介质,找 BOOT.BIN,验头
第一段:FSBL(或 U-Boot SPL)
↓  配 DDR、配时钟、下比特流(可选)、加载下一段
第二段:U-Boot(含 SPL 之后的 proper 阶段)
↓  初始化外设、读环境变量、加载内核和设备树
第三段:Linux kernel
↓
用户空间

关键在于:BootROM 是第一段之前的东西,它只认几种固定格式(SD、NAND、QSPI、JTAG)。它做的事情很有限:把介质上第一个分区里的 BOOT.BIN 读进片上 RAM,校验头部,然后跳过去。

所以"串口一点输出都没有",问题一定在 BootROM 到第一段之间:

•BOOT.BIN 没在正确位置(FAT 分区根目录,或裸分区起始扇区)

• 启动模式跳线选错(SD 卡启动 vs QSPI 启动)

• 串口线接的是 UART1,而 FSBL 用的是 UART0

这三条能覆盖 80% 的"完全没输出"。

03值得读的三处

▍ps7_init.c —— 那几千行"魔法数字"

BOOT.BIN 里的 FSBL 有一份 ps7_init.c(开源版本叫 ps7_init_gpl.c),它是 Vivado 导出硬件时生成的,内容是几千行 Xil_Out32(0xF8000000, 0x00000000) 这样的寄存器写。

这些数字一次性把 PS 的时钟、PLL、DDR 控制器、MIO 复用全部配好。它才是"DDR 能不能起来"的真正决定者。

所以有个经典故障:你在 Vivado 里改了 DDR 型号或者时钟频率,但 BOOT.BIN 里还是旧的 ps7_init。现象是 FSBL 跑到 DDR 初始化就挂,或者能跑但一到 Linux 就随机崩。改硬件配置后,一定要重新生成 FSBL 并重新打 BOOT.BIN。

▍board/xilinx/zynq/board.c —— U-Boot 眼里的"这块板子"

这里定义了板级初始化和环境变量的默认值:bootcmd、bootargs、fdt_addr、kernel_addr。

Zynq 上 U-Boot 的默认流程通常是 distro boot:扫描 mmc 上的 boot.scr 或者 extlinux/extlinux.conf,按里面的脚本加载内核。

想知道"为什么我的内核没被加载",先看 bootcmd 到底在扫哪个设备、哪个分区。

▍drivers/mmc/zynq_sdhci.c —— SD 卡为什么读不出来

SD 卡相关的故障特别多,而且报错含糊。这个驱动里能看到 SD 控制器的时钟分频、电压切换、以及 Zynq 特有的一个坑:MIO 上 SD 的 CD(卡检测)引脚如果没配,U-Boot 可能认为没插卡。

04上手:手工做一个能干活的 BOOT.BIN

不用 Vitis 的图形界面,直接写 .bif 文件,用 bootgen 打:

// boot.bif
the_ROM_image:
{
[bootloader] fsbl.elf
system.bit
u-boot.elf
}

bootgen -image boot.bif -arch zynq -o BOOT.BIN -w on

三个组件的顺序是有讲究的:[bootloader] 标记的那一项必须排第一,它是 BootROM 直接跳过去的东西。system.bit 放中间,FSBL 会把它写进 PL。u-boot.elf 放最后。

烧进 SD 卡后,串口应该能看到这样的输出(版本号会不同):

Xilinx First Stage Boot Loader
Release 2023.2  ...
...
U-Boot 2023.01 (Jan 01 2024 - 00:00:00 +0000)
Model: Zynq Zybo Board
DRAM:  512 MiB
MMC:   mmc@e0100000: 0

看到 U-Boot 那一行,就说明前两段都通了。

TIP

如果你只想快速验证 PL 逻辑,不想每次等 Linux 启动,可以在 BOOT.BIN 里只放 FSBL + bitstream,然后让 FSBL 直接把一个裸机 ELF 加载起来。这样从上电到逻辑跑起来只要一两秒,调试硬件非常爽。

05三个坑

坑

一、环境变量存在 QSPI 里,改错了会一直启动失败。saveenv 之前先 printenv 看一眼。真改坏了,可以在 U-Boot 启动的瞬间狂按回车进命令行,然后 env default -a; saveenv 复位。

二、串口波特率。U-Boot 默认 115200,但 FSBL 打印的波特率由 Vivado 里 PS 配置的 UART 时钟决定。两边不一致,你看到的就是一堆乱码——很容易被误判成"启动失败"。

三、bootargs 里的 console= 必须和实际用的 UART 一致。console=ttyPS0,115200 对应 UART0,用 UART1 就得写 ttyPS1。写错了内核照样启动,但你看不到任何输出。

06它不适合谁

不适合只想跑 PYNQ 镜像的人。

官方镜像已经把这三段全部打包好了,你插卡就能用,完全不需要理解 U-Boot。只有在你要定制启动流程、要加快启动速度、要做双系统或者 A/B 升级的时候,这一层才变成必需品。

但反过来说,只要你打算把板子做成"产品"而不是"实验台",你迟早要碰它——因为产品的启动时间、故障恢复、固件升级,全都长在这一层。

07写在最后

启动链是唯一一条"全链路都必须正确"的路径:硬件配置、引导程序、内核、设备树,任何一环错一个字节,结果都是黑屏。

但也正因为它是串联的,定位反而简单——串口打到哪一行,问题就在那一行之后。

 

关注微信公众号『ZYNQ』,回复“加群”加入 fpga/zynq 技术交流群。后台回复“m”,查看全部文章。

相关推荐