如何在Zynq UltraScale+ MPSoC平台上通过JTAG启动嵌入式Linux镜像
以下文章来自OpenFPGA,作者为隋隋思
在之前的文章中,我们介绍了如何使用XSCT 工具通过JTAG 启动Zynq SoC 上的嵌入式Linux 镜像(Booting Zynq-7000 Embedded Linux from JTAG:使用XSCT 的全流程教程)。本文进一步讲解了如何在Zynq UltraScale+ MPSoC 平台上通过JTAG 逐步启动Linux,并提供了完整的流程和关键命令。只要按照步骤操作,即使是复杂的Linux 映像也可以通过JTAG 成功启动。
所需条件和工具
为了完成JTAG启动,需要准备以下内容:
具有JTAG 调试支持的Zynq UltraScale+ MPSoC 板
连接到目标板的JTAG 电缆(例如SmartLynq/平台电缆)
安装了Xilinx 工具的开发主机(例如Vitis/SDK)
可用的Linux 映像、设备树、ATF、U-Boot 和其他组件
操作概要(步骤不可更改)
整个流程分为8个步骤,每个步骤对于最终的上线都至关重要:
启动XSCT 命令行
配置FPGA(下载比特流)
下载并运行PMU 固件
配置处理系统
下载并执行第一阶段引导加载程序(FSBL)
下载第二阶段引导加载程序(U-Boot)
下载ARM 可信固件
下载并启动Linux 映像
1. 如何启动XSCT
XSCT(Xilinx 软件命令行工具)用于通过JTAG 与器件通信:
/Xilinx/Vitis/2021.1/bin/xsct
或者在开始菜单中搜索它。
启动后会进入交互式命令行,等待你输入各种启动命令。
1124 ~ /opt/Xilinx/Vitis/2021.1/bin/xsct****** Xilinx 软件命令行工具(XSCT) v2021.1 **** 软件版本3246112,于2021-06-09-1456 ** 版权所有1986-2021 Xilinx, Inc. 保留所有权利。xsct%helpAvailable 帮助类别断点- 目标Breakpoints/Watchpoints.connections - 目标连接管理.device - 设备配置System.download - 目标下载FPGA/BINARY.hsi - HSI 命令.ipi - 对Versal PMC 的IPI 命令.jtag - JTAG Access.memory - 目标内存.miscellaneous - Miscellaneous.petalinux - Petalinux 命令.projects - Vitis Projects.registers - 目标寄存器.reset - 目标Reset.running - 程序执行.streams - Jtag UART.svf - SVF Operations.tfile - 目标文件系统。键入“help”,后跟上面的“category”以获取更多详细信息,或者键入“help”,后跟关键字“commands”以列出所有命令xsct%
2. 连接目标设备
使用XSCT中的connect命令建立JTAG连接,例如:
连接-url tcp:3121
不同的JTAG 电缆具有不同的IP 地址:
SmartLynq:通过板上显示的IP
SmartLynq(USB 直连):通常为10.0.0.2
Platform Cable(国内常用JTAG):通常为127.0.0.1
默认端口为3121。
3. 列出连接到JTAG链的设备
连接成功后,可以查看JTAG链上的所有目标:
xsct% 目标1 PS TAP 2 PMU 3 PL 4 PSU 5 RPU(复位) 6 Cortex-R5#0(无电源) 7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(运行) 10 Cortex-A53#1(运行) 11 Cortex-A53#2(运行) 12 Cortex-A53#3(运行)
执行命令后看到的目标列表代表连接到JTAG 链的每个设备。设备可以是单元、CPU核、FPGA等,每个设备都可以管理。有时可能无法在目标设备和JTAG 电缆之间建立可靠的连接。在这种情况下,您应该检查错误消息并根据消息采取适当的操作。例如,当没有JTAG 连接或板断电时,将显示如下错误消息。
xsct% 目标1 整个扫描链(板电源关闭)
最常见的问题与所使用的JTAG 频率有关。 JTAG 链路的硬件结构对其最佳频率有重大影响。某些物理特性(例如电缆长度、目标数量等)可能会影响适当的JTAG 频率。在这种情况下,XSCT 工具会生成错误消息,指示与目标的连接不稳定。下面是一些例子。
xsct% 目标1 个整个扫描链(未知设备配置)xsct% 目标1 个整个扫描链(未知IR 长度)xsct% 目标1 个整个扫描链(设备太多)
当出现此类输出时,应重新调整JTAG频率以建立可靠稳定的连接。您可以通过调用jtagFrequency -list 来查找JTAG 电缆支持的频率。然后您可以从列表中选择适当的频率。您还可以选择任何频率,实用程序将自动处理舍入误差。
xsct% jtag 目标1xsct% jtag 频率-list125000 250000 500000 1000000 2000000 3000000 4000000 6000000 7500000 10000000 12000000 1300000015000000 20000000 30000000 40000000 50000000 60000000 70000000 80000000 90000000 100000000xsct% jtag 频率75000007500000xsct% jtag 频率70000007017543
4. 中断系统
在开始操作之前,CPU必须被中断并复位到其初始状态。如果没有这一步,治疗系统就无法管理。此步骤在Zynq SoC 中有所不同。重置系统:
xsct% 目标-set-nocase -filter {name=~'*PSU*'}xsct% rstxsct% Info: Cortex-A53#0(目标9) 停止于0xffff0000(重置捕获)xsct% 目标1 PS TAP 2 PMU 3 PL 4* PSU 5 RPU(重置)6 Cortex-R5#0(无电源)7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(复位捕获,EL3(S)/A64) 10 Cortex-A53#1(复位) 11 Cortex-A53#2(复位) 12 Cortex-A53#3(复位)
5.配置FPGA
由于Zynq UltraScale+ SoC 实际上是一个FPGA 芯片,因此需要硬件映像来访问其底层组件,如DDR、以太网、串行接口等。通过JTAG 下载完整的FPGA 映像非常简单,只需使用以下fpga 命令即可。
xsct% 目标1 PS TAP 2 PMU 3 PL 4* PSU 5 RPU(复位) 6 Cortex-R5#0(无电源) 7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(复位捕获,EL3(S)/A64) 10 Cortex-A53#1(复位) 11 Cortex-A53#2(重置) 12 Cortex-A53#3(重置)xsct% 目标-set-nocase -filter {name=~'*PS TAP*'}xsct% fpga'system.bit' 100% 25MB 1.2MB/s 00:21
下载FPGA 映像会清除现有PL 映像并用新映像替换。还可以通过为同一命令提供选项来管理可编程逻辑。按回车键help fpga可查看更多信息。目前,将FPGA镜像下载到硬件就足够了;其余部分超出了本教程的范围。
6.PMU固件
Zynq UltraScale+ SoC 包括一个用于平台管理的专用MicroBlaze 处理器。 PMU 代表平台管理单元。它的主要职责是在启动前初始化一些系统组件、管理分配给各个电源岛的电源以及处理系统错误。固件与处理系统单元上运行的用户应用程序进行通信。用户可以根据需要创建请求来管理平台。此外,可以使用Vitis IDE 修改PMU 固件。您可以参考《Zynq UltraScale+ 技术参考手册》(即UG1085)了解PMU 固件的更多详细信息和使用场景。
如果不禁用处理系统单元(PSU) 上的某些安全门,则无法访问位于平台管理单元(PMU) 内的MicroBlaze 处理器。所以我们的第一步是禁用这些安全门。然后,我们将PMU 固件下载到目标处理器并运行它。为了确保安全,我们将重新启动这些安全门。
xsct% 目标-set-nocase -filter {name=~'*PSU*'} xsct% 目标1 PS TAP 2 PMU 3 PL 4* PSU 5 RPU(复位) 6 Cortex-R5#0(无电源) 7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(复位捕获,EL3(S)/A64) 10 Cortex-A53#1(复位) 11 Cortex-A53#2(复位) 12 Cortex-A53#3(复位)xsct% mask_write0xFFCA00380x1C00x1C0xsct% 目标1 PS TAP 2 PMU 13 MicroBlaze PMU(睡眠。无时钟) 3 PL 4* PSU 5 RPU(复位) 6 Cortex-R5#0(无电源) 7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(复位捕捉器, EL3(S)/A64) 10 Cortex-A53#1(复位) 11 Cortex-A53#2(复位) 12 Cortex-A53#3(复位)
禁用安全门后,MicroBlaze 内核立即出现在JTAG 目标链上。通过访问处理器,我们现在可以下载并运行PMU 固件。
xsct% 目标-set-nocase -filter {name=~'*MicroBlaze PMU*'}xsct% dow'pmufw.elf' 下载程序-- pmufw.elfsection,vectors.reset:0xffdc0000 -0xffdc0007section,vectors.sw_exception:0xffdc0008 -0xffdc000fsection,vectors.interrupt:0xffdc0010 -0xffdc0017section,vectors.hw_exception:0xffdc0020 -0xffdc0027section,text:0xffdc0050 -0xffdd1057section,rodata:0xffdd1058 -0xffdd2287section,data:0xffdd2288 -0xffdd639bsection,sdata2:0xffdd639c -0xffdd639fsection,sdata:0xffdd63a0 -0xffdd639fsection,sbss:0xffdd63a0 -0xffdd639fsection,bss:0xffdd63a0 -0xffdda0dbsection,srdata:0xffdda0dc -0xffdda9f7section,stack:0xffdda9f8 -0xffddb9f7section,xpbr_serv_ext_tbl:0xffddf6e0 -0xffddfadf100% 0MB 0.2MB/s 00:00 将PC 设置为编程起始地址0xffdd0a04 成功下载pmufw.elfxsct% Info: MicroBlaze PMU(目标13)已停止于0xffdc8534(停止)xsct% con Info: MicroBlaze PMU(目标13)正在运行
如果一切顺利,我们可以重新启用安全门以防止访问MicroBlaze PMU 并继续进行PSU 初始化。
xsct% 目标-set-nocase -filter {name=~'*PSU*'}xsct% mask_write0xFFCA00380x1C00x0xsct% 目标1 PS TAP 2 PMU 3 PL 4* PSU 5 RPU(复位) 6 Cortex-R5#0(无电源) 7 Cortex-R5#1(无电源) 8 APU 9 Cortex-A53#0(复位锁扣,EL3(S)/A64) 10 Cortex-A53#1(复位) 11 Cortex-A53#2(复位) 12 Cortex-A53#3(复位)
7.APU和PSU初始化
在将第一阶段引导加载程序下载到应用处理器单元(APU) 之前,必须移除复位。此外,处理系统单元必须根据硬件映像(HDF/XSA 文件)进行初始化。从Vivado 设计工具导出的硬件映像包含名为“psu_init.tcl”的TCL 脚本。以下是执行这些步骤的命令。
xsct% 目标-set-nocase -filter {name=~'*APU*'}xsct% 目标1 PS TAP 2 PMU 3 PL 5 PSU 6 RPU(复位) 7 Cortex-R5#0(无电源) 8 Cortex-R5#1(无电源) 9* APU 10 Cortex-A53#0(复位捕获,EL3(S)/A64) 11 Cortex-A53#1(复位) 12 Cortex-A53#2(复位) 13 Cortex-A53#3(复位)xsct% mwr0xffff00000x14000000xsct% mask_write0xFD1A01040x5010x0xsct%sourcepsu_init.tclxsct% psu_init
如果电源单元初始化成功,屏幕上将不会显示任何输出。完成这一步后,我们就获得了电源单元资源的访问权。
8. 第一阶段引导加载程序(FSBL)
FSBL 是Zynq UltraScale+ 启动时在应用处理器上运行的第一个程序(实际上是第二个程序,BootROM 首先运行)。它负责在调用SSBL(第二阶段引导加载程序)或所需的裸机应用程序之前配置硬件并初始化一些组件。您可以使用自动生成的FSBL 或编写自己的FSBL。要下载并运行FSBL,请调用以下命令。请注意,FSBL 必须在应用程序处理单元的第一个Cortex-A53 内核上运行。因此,我们需要相应地选择目标核心。
xsct% 目标-set-nocase -filter {name=~'*A53#0*'}xsct% 目标1 PS TAP 2 PMU 3 PL 5 PSU 6 RPU(复位) 7 Cortex-R5#0(无电源) 8 Cortex-R5#1(无电源) 9 APU 10* Cortex-A53#0(复位Catch,EL3(S)/A64) 11 Cortex-A53#1(复位) 12 Cortex-A53#2(复位) 13 Cortex-A53#3(复位)xsct% dow'zynqmp_fsbl.elf'下载程序-- zynqmp_fsbl.elf 部分,text:0xfffc0000 -0xffffcf88b 部分,note.gnu.build-id:0xfffcf88c -0xfffcf8af 部分,init:0xfffcf8c0 -0xfffcf8f3 部分,fini:0xffffcf900 -0xffffcf933 部分,rodata:0xfffcf940 -0xfffcfe6f 部分,sys_cfg_data:0xfffcfe80 -0xfffd0657 部分,mmu_tbl0:0xfffd1000 -0xfffd100f 部分,mmu_tbl1:0xfffd2000 -0xfffd3fff 部分,mmu_tbl2:0xfffd4000 -0xfffd7fff 部分,data:0xfffd8000 -0xfffd932f 部分,sbss:0xfffd9330 -0xfffd933f 部分,bss:0xfffd9340 -0xfffdb87f 部分,heap:0xfffdb880 -0xfffdbc7f 部分,stack:0xfffdbc80 -0xfffddc7f 部分,dup_data:0xfffddc80 -0xfffdefaf 部分,handoff_params:0xfffe9e00 -0xfffe9e87部分,bitstream_buffer:0xffff0040 -0xfffffc3f100% 0MB 0.3MB/s 00:00将PC设置为程序起始地址0xfffc0000已成功下载zynqmp_fsbl.elfxsct% conInfo: Cortex-A53#0(目标10) 4000 后Runningxsct%; stopInfo: Cortex-A53#0(目标10) 停止于0xfffce2f0(外部调试请求)
启动FSBL后,我们让它运行一段时间以完成其操作。然后我们在切换点手动停止它。由于我使用的是定制板,其启动引脚已硬配置为QSPI 启动模式,因此我修改了FSBL 代码以强制其使用JTAG 启动模式。这是我的UART 输出:
Xilinx Zynq MP 第一阶段引导加载程序(已修改)发布2021.1 2022 年6 月20 日- 1015此FSBL 已修改,以便仅从JTAG 引导!强制引导模式为JTAG!检查xfsbl_initialization.c 文件。
9. 第二阶段引导加载程序(U-Boot)
与常规启动过程类似,SSBL 遵循FSBL。由于我们使用PetaLinux 构建系统来生成映像,因此我们将使用常用的U-Boot 作为SSBL 程序。也可以使用不同的引导加载程序,但过程应该类似。
U-Boot 需要一个设备树二进制文件,因此我们需要在启动U-Boot 之前将其加载到内存中。必须将设备下载到内存中的预设地址。在PetaLinux 项目中,此配置位于名为CONFIG_SUBSYSTEM_UBOOT_DEVICETREE_OFFSET 的参数下。可以在我的上一篇文章中找到一个简单的单行Bash 命令,可以更快地找到所需的配置。在我的示例中,地址是0x100000。请注意,由于该二进制文件不是可执行文件,因此我们使用-data 选项来下载它。
xsct% Targets -set-nocase -filter {name=~'*A53#0*'}xsct% dow -data'system.dtb'0x100000100% 0MB 0.2MB/s 00:00成功下载system.dtb
现在可以将U-Boot 下载到内存中。
xsct% Targets -set-nocase -filter {name=~'*A53#0*'}xsct% dow 'u-boot.elf'下载程序--
u-boot.elfsection, .text: 0x08000000 - 0x080001afsection, .efi_runtime: 0x080001b0 - 0x080011bfsection, .text_rest: 0x08001800 - 0x080c8c23section, .rodata: 0x080c8c28 - 0x080fa111section, .hash: 0x080fa118 - 0x080fa12fsection, .data: 0x080fa130 - 0x0810863fsection, .got: 0x08108640 - 0x08108647section, .got.plt: 0x08108648 - 0x0810865fsection, .u_boot_list: 0x08108660 - 0x0810d3f7section, .efi_runtime_rel: 0x0810d3f8 - 0x0810d5a7section, .rela.dyn: 0x0810d5a8 - 0x08125877section, .bss_start: 0x08125878 - 0x08125877section, .bss: 0x08125880 - 0x0813e3c7section, .bss_end: 0x0813e3c8 - 0x0813e3c7100% 1MB 0.3MB/s 00:04Setting PC to Program Start Address 0x08000000Successfully downloaded u-boot.elf PS1: 注意到了吗?这一步其实有点棘手,而且一旦出错就很难找出原因。与 FSBL 不同,我们并没有在将 U-Boot 下载到内存后立即启动它。这是因为 Zynq UltraScale+ 的架构与 Zynq SoC 相比,情况要复杂一些。Zynq UltraScale+ 的应用处理器基于 ARMv8 架构,而 Zynq 的应用处理器则基于 ARMv7 架构。 PS2: 为了符合 ARVv8 拓扑结构,Linux 内核启动时运行在 ARM 异常级别 1/0。在该异常级别下,Linux 内核对系统或安全关键寄存器的访问受到硬件限制。Linux 与这些受限设备的所有交互都通过运行在异常级别 3 的 ARM 可信固件 (ATF) 进行路由。如果没有 ATF, Linux 镜像可能根本无法启动。 10、ARM 可信固件 (ATF) 如前所述,访问特权区域需要 ARM 可信固件 (ATF)。ATF 作为代理,代表操作系统修改系统关键设置。为了使操作系统(在本例中为 Linux)能够访问这些资源,必须对其进行修改,以支持 ATF 导出到操作系统自身的安全监控调用。这些步骤超出了本教程的范围。可以参考Zynq UltraScale+ 技术参考手册 UG1085 的第 16 章“16-System Protection Units”。接下来,我们将 ATF 下载到内存中。 xsct% targets -set-nocase -filter {name =~"*A53#0*"}xsct% targets 1 PS TAP 2 PMU 3 PL 5 PSU 6 RPU 7 Cortex-R5#0(No Power) 8 Cortex-R5#1(No Power) 9 APU 10* Cortex-A53#0(External Debug Request, EL3(S)/A64) 11 Cortex-A53#1(Reset) 12 Cortex-A53#2(Reset) 13 Cortex-A53#3(Reset)xsct% dow "bl31.elf"Downloading Program -- bl31.elfsection, .text: 0xfffea000 - 0xffff1fffsection, .rodata: 0xffff2000 - 0xffff2fffsection, .data: 0xffff3000 - 0xffff679dsection, stacks: 0xffff67c0 - 0xffff78bfsection, .bss: 0xffff78c0 - 0xffff863fsection, xlat_table: 0xffff9000 - 0xffffdfffsection, coherent_ram: 0xffffe000 - 0xffffefff100% 0MB 0.3MB/s 00:00Setting PC to Program Start Address 0xfffea000Successfully downloaded bl31.elf 我们没有重新启动处理器,因为还缺少一样东西,那就是 Linux 内核本身。 11、完整的 Linux 镜像 本教程的最后一步是将 Linux 内核镜像下载到 DDR 内存中,并通过 U-Boot 启动它。用于下载镜像的地址有限制。必须将镜像放置在 DDR 内存的空段中,并且不能损坏内存中已有的任何其他镜像。如果你已经创建了一个 PetaLinux 项目,可以在配置文件中通过带有 CONFIG_SUBSYSTEM_UBOOT_FIT_IMAGE_OFFSET 标记的配置项找到对应的地址 请注意,Linux 内核需要设备树二进制文件和可挂载的根文件系统才能完成启动过程。为了快速演示,我准备了一个 INITRAMFS 镜像,并使用 PetaLinux 工具将内核、根文件系统和设备树二进制文件打包到一个镜像文件中。也可以根据需要选择其他方法。 xsct% targets -set-nocase -filter {name =~"*A53#0*"}xsct% dow -data "image.ub"0x10000000100% 24MB 0.3MB/s 01:37Successfully downloaded image.ub 下载 Linux 内核需要一些时间,因为它是整个过程中最大的镜像文件。接下来,我们释放 APU,然后通过检查串口通道来观察设备上的情况。 首先,运行 ARM 可信固件。其输出如下所示。 NOTICE: ATF running on XCZU9EG/silicon v4/RTL5.1 at 0xfffea000NOTICE: BL31: v2.4(release):v1.1-7609-g851523ea2NOTICE: BL31: Built : 0807, Apr 28 2021 当 ATF 完成将安全监视器调用导出到操作系统后,就会发生 U-Boot 交接。 U-Boot 2021.01 (Jun 01 2021 - 1106 +0000)Board: Xilinx ZynqMPDRAM: 4 GiBPMUFW: v1.1EL Level: EL2Chip ID: zu9egNAND: 0 MiBMMC: mmc@ff160000: 0, mmc@ff170000: 1In: serialOut: serialErr: serialBootmode: QSPI_MODEReset reason: DEBUGNet:ZYNQ GEM: ff0b0000, mdio bus ff0b0000, phyaddr -1, interface rgmii-ideth0: ethernet@ff0b0000Hit any key to stop autoboot: 0ZynqMP> 当终端出现 U-Boot 提示符后,可以输入bootm 0x10000000来启动下载的 Linux 内核镜像。由于该镜像完整(包含内核、根文件系统和设备树),它将自动启动。 ## Loading kernel from FIT Image at 10000000 ... Using'conf-system-top.dtb'configuration Trying'kernel-1'kernel subimage Description: Linux kernel Created: 2021-06-04 1516 UTC Type: Kernel Image Compression: gzip compressed Data Start: 0x100000f8 Data Size: 9356113 Bytes = 8.9 MiB Architecture: AArch64 OS: Linux Load Address: 0x00200000 Entry Point: 0x00200000 Hash algo: sha256 Hash value: b4f7073afddc350f3c14f3e9ac1bf6ef8604c602951d13bbe58548bd0d415241 Verifying Hash Integrity ... sha256+ OK## Loading ramdisk from FIT Image at 10000000 ... Using'conf-system-top.dtb'configuration Trying'ramdisk-1'ramdisk subimage Description: petalinux-image-minimal Created: 2021-06-04 1516 UTC Type: RAMDisk Image Compression: uncompressed Data Start: 0x108fa12c Data Size: 16778258 Bytes = 16 MiB Architecture: AArch64 OS: Linux Load Address: unavailable Entry Point: unavailable Hash algo: sha256 Hash value: 6fcf87930415873cac0aa2232786dace5b65379e68e42b0a6c30f77b870807bc Verifying Hash Integrity ... sha256+ OK## Loading fdt from FIT Image at 10000000 ... Using'conf-system-top.dtb'configuration Trying'fdt-system-top.dtb'fdt subimage Description: Flattened Device Tree blob Created: 2021-06-04 1516 UTC Type: Flat Device Tree Compression: uncompressed Data Start: 0x108ec55c Data Size: 56061 Bytes = 54.7 KiB Architecture: AArch64 Hash algo: sha256 Hash value: 9bf95db6c5106f1f1945af67bbb43f2269539e514acadf88c4615f4ae7d6b595 Verifying Hash Integrity ... sha256+ OK Booting using the fdt blob at 0x108ec55c Uncompressing Kernel Image Loading Ramdisk to 7cd11000, end 7dd11412 ... OK Loading Device Tree to 000000007cd00000, end 000000007cd10afc ... OKStarting kernel ...[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034][ 0.000000] Linux version 5.10.0-xilinx-v2021.1 (oe-user@oe-host) (aarch64-xilinx-linux-gcc (GCC) 10.2.0, GNU ld (GNU Binutils) 2.35.1)#1SMP Fri Jun 4 15:57:16 UTC 2021[ 0.000000] Machine model: xlnx,zynqmp..............................PetaLinux 2021.1 ZynqUS_Linux ttyPS0root@ZynqUS_Linux:~ > uname -r5.10.0-xilinx-v2021.1 现在,硬件上已经安装了完整的 Linux 镜像 :) 提示与经验总结 流程顺序不可随意更改 — 每一步都有依赖 JTAG 启动非常强大 — 即使没有 SD/USB/网络也能引导系统 要注意 JTAG 链路稳定性 — 线缆长度、频率设置会影响连接稳定性 ATF 必须支持 UltraScale+ 安全模式 — 否则内核可能不能正常访问安全级别寄存器