文章目录
0 写在前面
这是一个系列博客,详细介绍如何在 ZYNQ 与 ZYNQ MP 平台上如何移植 Linux 系统。目前网络上的大部分教程都是全程基于 Petalinux 的开发,虽然这样简化了开发流程,但对于初学者深入理解掌握 Linux 是不利的,所以,有了这个系列的博客,从几乎为 0 开始教大家怎么移植 Linux 系统。
本人的软件与环境版本:
Windows 的 Vivado 与 Vitis 版本:2020.2(前期学习 ZYNQ7020 跟随正点原子安装);
Ubuntu版本:18.04.2;
虚拟机上的 Vivado、Vitis 与 Petalinux 版本:2020.1(前期学习 ZYNQ MP 跟随 Alinx 安装)。
所有相关的文件存放在虚拟机的以下路径:
~/Linux_tp
如果后面涉及到一些命令行操作的文件路径,大家可以参考着修改为自己的路径。
此外,我后续的操作与实测都是黑金(Alinx)基于 ZYNQ MP 系列的 zu3eg 操作,但基本也会对 ZYNQ-7000系列的一些不同做一些说明,如果你使用的是 ZYNQ-7000 系列的板卡,可以自己灵活变通一下。
1 Bootloader 与 u-boot
对于计算机系统而言,从开机上电到操作系统启动需要一个引导过程,这个引导过程由引导程序指定。引导程序是系统上电启动运行的第一段软件代码。 这个引导程序一般我们叫作启动加载程序(Bootloader)。
Bootloader 的主要功能:
1,硬件初始化
2,加载操作系统内核
通过 Bootloader 还可以向内核传递命令行参数,如后续大家会看到root=/dev/mmcblk1p2 console=ttyS0,115200
,就是制定根文件系统路径,以及控制台。同时,也可以通过 Bootloader 通过 USB、网络更新系统,升级固件。通过 Bootloader 也可以完成简单的调试功能,如进行内存的检测。
而u-boot(Universal Bootloader)是嵌入式 Linux 使用最为广泛的开源 Bootloader。
但是,Bootloader 是基于特定硬件平台实现的。因此,几乎不可能为所有的嵌入式系统建立一个通用的 Bootloader,不同的处理器架构都有不同的 Bootloader。u-boot官方提供了一个大致的框架,uboot 官方的 uboot 源码是给半导体厂商准备的,半导体厂商会下载 uboot 官方的 uboot 源码,然后将自家相应的芯片移植进去,这个移植工作对于我们开发者而言,复杂且没有必要。在这个系列的第一篇:【ZYNQ Linux移植】1-前期准备 介绍了如何获取 Xilinx 维护的适配自家硬件的 u-boot,后续的操作都将基于这份下载来的 u-boot。
2 u-boot的文件结构
解压出来的 u-boot 包内的文件结构如下:
这里用表对后续我们涉及的目录,进行一个大致的介绍:
文件名称 | 描述 |
---|---|
arch | 与芯片架构体系相关的代码 |
board | 各种板卡的定制代码 |
configs | 各种板卡的配置文件 |
include | 各种头文件 |
scripts | 脚本文件 |
知道了大致的结构,接下来,我们开始移植!
3 ZYNQ 的 u-boot 移植
u-boot 的移植并不是说我们完完全全的从零开始将 uboot 移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的,这个工作一般是半导体厂商做的,半导体厂商负责将 uboot移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板。半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot发布出去,这就是大家常说的原厂 BSP 包。实际的产品开发,就会参考原厂的开发板做硬件,然后在原厂提供的 BSP 包上做修改,将 uboot 或者 linux kernel 移植到我们的硬件上。
因此,移植的整体思路为:在 Xilinx 的 u-boot 中找到原厂开发板的配置文件,进行一定的修改后用到我们自己的板卡上。
3.1 创建板卡配置文件
在 u-boot 包根目录打开终端,进入 configs 文件夹,先查看一下都有什么配置文件可供参考:
cd configs
ls
在众多文件中,找 xilinx 开头的,我们注意到以下两个文件:
这就是我们可以参考的文件,如果是 ZYNQ-7000 系列的板卡,就是使用 xilinx_zynq_virt_defconifg
,如果是 ZYNQ MP 系列的板卡,就使用 xilinx_zynqmp_virt_defconifg
。我使用的是 ZYNQ MP 系列的 zu3eg ,就先把对应的文件复制一份(使用 ZYNQ-7000 系列的板卡的需要修改以下命令的文件名称):
cp xilinx_zynqmp_virt_defconfig zynqmp_alinx3eg_defconfig
然后对这个复制出来的文件进行编辑,最后修改后的文件内容如下(ZYNQ-7000 系列内容大不一样,后续有机会再补上):
CONFIG_ARM=y
CONFIG_SYS_CONFIG_NAME="zynqmp_alinx3eg"
CONFIG_POSITION_INDEPENDENT=y
CONFIG_ARCH_ZYNQMP=y
CONFIG_SYS_TEXT_BASE=0x8000000
CONFIG_SYS_MALLOC_F_LEN=0x8000
CONFIG_SPL=y
CONFIG_SPL_SPI_FLASH_SUPPORT=y
CONFIG_SPL_SPI_SUPPORT=y
CONFIG_ZYNQMP_USB=y
CONFIG_AHCI=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_FIT=y
CONFIG_FIT_VERBOSE=y
CONFIG_SPL_LOAD_FIT=y
CONFIG_USE_PREBOOT=y
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_BOARD_EARLY_INIT_R=y
CONFIG_SPL_OS_BOOT=y
CONFIG_SPL_RAM_SUPPORT=y
CONFIG_SPL_RAM_DEVICE=y
CONFIG_SPL_SPI_LOAD=y
CONFIG_SPL_ATF=y
CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y
CONFIG_CMD_BOOTMENU=y
CONFIG_CMD_THOR_DOWNLOAD=y
CONFIG_CMD_MEMTEST=y
CONFIG_SYS_ALT_MEMTEST=y
CONFIG_CMD_SHA1SUM=y
CONFIG_CMD_BIND=y
CONFIG_CMD_CLK=y
CONFIG_CMD_DFU=y
CONFIG_CMD_FPGA_LOADBP=y
CONFIG_CMD_FPGA_LOADP=y
CONFIG_CMD_FPGA_LOAD_SECURE=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_GPT=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_MTD=y
CONFIG_CMD_NAND_LOCK_UNLOCK=y
CONFIG_CMD_POWEROFF=y
CONFIG_CMD_SDRAM=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_FRU=y
CONFIG_CMD_TPM=y
CONFIG_CMD_EXT4_WRITE=y
CONFIG_CMD_MTDPARTS=y
CONFIG_CMD_UBI=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_DEFAULT_DEVICE_TREE="zynqmp-alinx3eg"
CONFIG_OF_LIST="zynqmp-zcu102-rev1.0 zynqmp-zcu102-revA zynqmp-zcu102-revB"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_SPL_DM=y
CONFIG_SPL_DM_SEQ_ALIAS=y
CONFIG_SCSI_AHCI=y
CONFIG_SATA_CEVA=y
CONFIG_CLK_ZYNQMP=y
CONFIG_DFU_RAM=y
CONFIG_ENV_OFFSET=0x1E00000
CONFIG_ENV_SECT_SIZE=0x40000
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_ADDR=0x0
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_FASTBOOT_CMD_OEM_FORMAT=y
CONFIG_FPGA_XILINX=y
CONFIG_FPGA_ZYNQMPPL=y
CONFIG_DM_GPIO=y
CONFIG_GPIO_HOG=y
CONFIG_XILINX_GPIO=y
CONFIG_DM_PCA953X=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_CADENCE=y
CONFIG_I2C_MUX=y
CONFIG_I2C_MUX_PCA954x=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
CONFIG_MISC=y
CONFIG_I2C_EEPROM=y
CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET=0x20
CONFIG_SYS_I2C_EEPROM_ADDR=0x0
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW=0x0
CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_MMC_IO_VOLTAGE=y
CONFIG_MMC_UHS_SUPPORT=y
CONFIG_MMC_HS200_SUPPORT=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ZYNQ=y
CONFIG_MTD=y
CONFIG_MTD_RAW_NAND=y
CONFIG_NAND_ARASAN=y
CONFIG_SYS_NAND_MAX_CHIPS=2
CONFIG_SF_DUAL_FLASH=y
CONFIG_SPI_FLASH_ISSI=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_SST=y
CONFIG_SPI_FLASH_WINBOND=y
# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
CONFIG_SPI_FLASH_MTD=y
CONFIG_PHY_MARVELL=y
CONFIG_PHY_MICREL=y
CONFIG_PHY_MICREL_KSZ90X1=y
CONFIG_PHY_NATSEMI=y
CONFIG_PHY_REALTEK=y
CONFIG_PHY_TI=y
CONFIG_PHY_VITESSE=y
CONFIG_PHY_XILINX_GMII2RGMII=y
CONFIG_PHY_FIXED=y
CONFIG_MII=y
CONFIG_ZYNQ_GEM=y
CONFIG_SCSI=y
CONFIG_DM_SCSI=y
CONFIG_ARM_DCC=y
CONFIG_ZYNQ_SERIAL=y
CONFIG_SPI=y
CONFIG_ZYNQ_SPI=y
CONFIG_ZYNQMP_GQSPI=y
CONFIG_TPM2_TIS_SPI=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_DWC3=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_GENERIC=y
CONFIG_USB_ULPI_VIEWPORT=y
CONFIG_USB_ULPI=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_MANUFACTURER="Xilinx"
CONFIG_USB_GADGET_VENDOR_NUM=0x03FD
CONFIG_USB_GADGET_PRODUCT_NUM=0x0300
CONFIG_USB_FUNCTION_THOR=y
CONFIG_USB_ETHER=y
CONFIG_USB_ETH_CDC=y
CONFIG_USB_HOST_ETHER=y
CONFIG_USB_ETHER_ASIX=y
CONFIG_PANIC_HANG=y
CONFIG_TPM=y
CONFIG_SPL_GZIP=y
CONFIG_OF_LIBFDT_OVERLAY=y
CONFIG_EFI_LOADER_BOUNCE_BUFFER=y
CONFIG_BOOTCOMMAND="run default_bootcmd"
主要修改如下:
1,指定头文件名称(与后续头文件修改名称要对应):
2,指定设备树(后续自己的设备树要改为这个名字):
3,对环境变量进行一定的配置(若不添加这几行, uboot 命令行模式没办法保存环境变量 ):
从上至下依次表示:
(1)设置环境变量在 Flash 中的偏移地址为 0x1E00000
;
(2)设置环境变量占用的扇区大小为 0x40000
(256KB);
(3)使用 SPI Flash 存储环境变量;
(4)设置环境变量在内存中的加载地址为 0x0
;
3.2 创建板卡头文件
在 u-boot 包根目录打开终端,进入 configs 文件夹,查看一下都有什么头文件可供参考:
cd include/configs
ls
在众多文件中,找 xilinx 开头的,我们注意到以下两个文件:
如果是 ZYNQ-7000 系列的板卡,就是使用 zynq-common.h
,如果是 ZYNQ MP 系列的板卡,就使用 xilinx_zynqmp.h
。我使用的是 ZYNQ MP 系列的 zu3eg ,就先把对应的文件复制一份:
cp xilinx_zynqmp.h zynqmp_alinx3eg.h
这里文件复制出来的文件名需要与3.1中的配置文件内容一致
需要对这个文件进行一定的修改,修改后的整个头文件内容(ZYNQ-7000 系列内容大不一样,后续有机会再补上)为:
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Configuration for Xilinx ZynqMP
* (C) Copyright 2014 - 2015 Xilinx, Inc.
* Michal Simek <michal.simek@xilinx.com>
*
* Based on Configuration for Versatile Express
*/
#ifndef __XILINX_ZYNQMP_H
#define __XILINX_ZYNQMP_H
#define CONFIG_REMAKE_ELF
/* #define CONFIG_ARMV8_SWITCH_TO_EL1 */
/* Generic Interrupt Controller Definitions */
#define CONFIG_GICV2
#define GICD_BASE 0xF9010000
#define GICC_BASE 0xF9020000
#define CONFIG_SYS_MEMTEST_START 0
#define CONFIG_SYS_MEMTEST_END 1000
#define CONFIG_SYS_INIT_SP_ADDR CONFIG_SYS_TEXT_BASE
/* Generic Timer Definitions - setup in EL3. Setup by ATF for other cases */
#if !defined(COUNTER_FREQUENCY)
# define COUNTER_FREQUENCY 100000000
#endif
/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 0x2000000)
/* Serial setup */
#define CONFIG_CPU_ARMV8
#define CONFIG_SYS_BAUDRATE_TABLE \
{ 4800, 9600, 19200, 38400, 57600, 115200 }
/* BOOTP options */
#define CONFIG_BOOTP_BOOTFILESIZE
#define CONFIG_BOOTP_MAY_FAIL
#ifdef CONFIG_NAND_ARASAN
# define CONFIG_SYS_MAX_NAND_DEVICE 1
# define CONFIG_SYS_NAND_ONFI_DETECTION
#endif
#if defined(CONFIG_SPL_BUILD)
#define CONFIG_ZYNQMP_PSU_INIT_ENABLED
#endif
/* Miscellaneous configurable options */
#define CONFIG_SYS_LOAD_ADDR 0x8000000
#if defined(CONFIG_ZYNQMP_USB)
#define CONFIG_SYS_DFU_DATA_BUF_SIZE 0x1800000
#define DFU_DEFAULT_POLL_TIMEOUT 300
#define CONFIG_THOR_RESET_OFF
#define DFU_ALT_INFO_RAM \
"dfu_ram_info=" \
"setenv dfu_alt_info " \
"Image ram 80000 $kernel_size_r\\\\;" \
"system.dtb ram $fdt_addr_r $fdt_size_r\0" \
"dfu_ram=run dfu_ram_info && dfu 0 ram 0\0" \
"thor_ram=run dfu_ram_info && thordown 0 ram 0\0"
#define DFU_ALT_INFO \
DFU_ALT_INFO_RAM
#ifndef CONFIG_SPL_BUILD
# define PARTS_DEFAULT \
"partitions=uuid_disk=${uuid_gpt_disk};" \
"name=""boot"",size=16M,uuid=${uuid_gpt_boot};" \
"name=""Linux"",size=-M,uuid=${uuid_gpt_Linux}\0"
#endif
#endif
#if !defined(DFU_ALT_INFO)
# define DFU_ALT_INFO
#endif
#if !defined(PARTS_DEFAULT)
# define PARTS_DEFAULT
#endif
/* Monitor Command Prompt */
/* Console I/O Buffer Size */
#define CONFIG_SYS_CBSIZE 2048
#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
#define CONFIG_SYS_MAXARGS 64
/* Ethernet driver */
#if defined(CONFIG_ZYNQ_GEM)
# define CONFIG_SYS_FAULT_ECHO_LINK_DOWN
# define PHY_ANEG_TIMEOUT 20000
#endif
#define CONFIG_SYS_BOOTM_LEN (60 * 1024 * 1024)
#define CONFIG_CLOCKS
#define ENV_MEM_LAYOUT_SETTINGS \
"fdt_high=10000000\0" \
"fdt_addr_r=0x40000000\0" \
"fdt_size_r=0x400000\0" \
"pxefile_addr_r=0x10000000\0" \
"kernel_addr_r=0x18000000\0" \
"kernel_size_r=0x10000000\0" \
"scriptaddr=0x20000000\0" \
"ramdisk_addr_r=0x02100000\0" \
"script_size_f=0x80000\0" \
#if defined(CONFIG_MMC_SDHCI_ZYNQ)
# define BOOT_TARGET_DEVICES_MMC(func) func(MMC, mmc, 0) func(MMC, mmc, 1)
#else
# define BOOT_TARGET_DEVICES_MMC(func)
#endif
#if defined(CONFIG_SATA_CEVA)
# define BOOT_TARGET_DEVICES_SCSI(func) func(SCSI, scsi, 0)
#else
# define BOOT_TARGET_DEVICES_SCSI(func)
#endif
#if defined(CONFIG_ZYNQMP_USB)
# define BOOT_TARGET_DEVICES_USB(func) func(USB, usb, 0) func(USB, usb, 1)
#else
# define BOOT_TARGET_DEVICES_USB(func)
#endif
#if defined(CONFIG_CMD_PXE) && defined(CONFIG_CMD_DHCP)
# define BOOT_TARGET_DEVICES_PXE(func) func(PXE, pxe, na)
#else
# define BOOT_TARGET_DEVICES_PXE(func)
#endif
#if defined(CONFIG_CMD_DHCP)
# define BOOT_TARGET_DEVICES_DHCP(func) func(DHCP, dhcp, na)
#else
# define BOOT_TARGET_DEVICES_DHCP(func)
#endif
#if defined(CONFIG_ZYNQMP_GQSPI)
# define BOOT_TARGET_DEVICES_QSPI(func) func(QSPI, qspi, 0)
#else
# define BOOT_TARGET_DEVICES_QSPI(func)
#endif
#if defined(CONFIG_NAND_ARASAN)
# define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand, 0)
#else
# define BOOT_TARGET_DEVICES_NAND(func)
#endif
#define BOOTENV_DEV_QSPI(devtypeu, devtypel, instance) \
"bootcmd_" #devtypel #instance "=sf probe " #instance " 0 0 && " \
"sf read $scriptaddr $script_offset_f $script_size_f && " \
"echo QSPI: Trying to boot script at ${scriptaddr} && " \
"source ${scriptaddr}; echo QSPI: SCRIPT FAILED: continuing...;\0"
#define BOOTENV_DEV_NAME_QSPI(devtypeu, devtypel, instance) \
#devtypel #instance " "
#define BOOTENV_DEV_NAND(devtypeu, devtypel, instance) \
"bootcmd_" #devtypel #instance "= nand info && " \
"nand read $scriptaddr $script_offset_f $script_size_f && " \
"echo NAND: Trying to boot script at ${scriptaddr} && " \
"source ${scriptaddr}; echo NAND: SCRIPT FAILED: continuing...;\0"
#define BOOTENV_DEV_NAME_NAND(devtypeu, devtypel, instance) \
#devtypel #instance " "
#define BOOT_TARGET_DEVICES_JTAG(func) func(JTAG, jtag, na)
#define BOOTENV_DEV_JTAG(devtypeu, devtypel, instance) \
"bootcmd_jtag=echo JTAG: Trying to boot script at ${scriptaddr} && " \
"source ${scriptaddr}; echo JTAG: SCRIPT FAILED: continuing...;\0"
#define BOOTENV_DEV_NAME_JTAG(devtypeu, devtypel, instance) \
"jtag "
#define BOOT_TARGET_DEVICES(func) \
BOOT_TARGET_DEVICES_JTAG(func) \
BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_QSPI(func) \
BOOT_TARGET_DEVICES_NAND(func) \
BOOT_TARGET_DEVICES_USB(func) \
BOOT_TARGET_DEVICES_SCSI(func) \
BOOT_TARGET_DEVICES_PXE(func) \
BOOT_TARGET_DEVICES_DHCP(func)
#include <config_distro_bootcmd.h>
/* Initial environment variables */
#ifndef CONFIG_EXTRA_ENV_SETTINGS
#define CONFIG_EXTRA_ENV_SETTINGS \
ENV_MEM_LAYOUT_SETTINGS \
BOOTENV \
DFU_ALT_INFO \
"kernel_addr_r=0x2000000\0" \
"ethaddr=00:0a:35:00:22:01\0" \
"kernel_image=image.ub\0" \
"kernel_load_address=0x2008000\0" \
"devicetree_image=devicetree.dtb\0" \
"devicetree_load_address=0x2000000\0" \
"bitstream_image=system.bit.bin\0" \
"boot_image=BOOT.bin\0" \
"loadbit_addr=0x100000\0" \
"loadbootenv_addr=0x2000000\0" \
"kernel_size=0x500000\0" \
"devicetree_size=0x20000\0" \
"bootenv=uEnv.txt\0" \
"loadbootenv=load mmc 0 ${loadbootenv_addr} ${bootenv}\0" \
"uenvboot=" \
"if run loadbootenv; then " \
"echo Loaded environment from ${bootenv}; " \
"run importbootenv; " \
"fi; " \
"if test -n $uenvcmd; then " \
"echo Running uenvcmd ...; " \
"run uenvcmd; " \
"fi\0" \
"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"bootm ${kernel_load_address}; " \
"fi\0" \
"netboot=tftpboot ${kernel_load_address} ${kernel_image} && bootm\0" \
"default_bootcmd=run sdboot;\0" \
"nandboot=echo Copying Linux from NAND flash to RAM... && " \
"nand read ${kernel_load_address} 0x100000 ${kernel_size} && " \
"nand read ${devicetree_load_address} 0x600000 ${devicetree_size} && " \
"echo Copying ramdisk... && " \
"nand read ${ramdisk_load_address} 0x620000 ${ramdisk_size} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0" \
"jtagboot=echo TFTPing Linux to RAM... && " \
"tftpboot ${kernel_load_address} ${kernel_image} && " \
"tftpboot ${devicetree_load_address} ${devicetree_image} && " \
"tftpboot ${ramdisk_load_address} ${ramdisk_image} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0"
#endif
/* SPL can't handle all huge variables - define just DFU */
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_DFU)
#undef CONFIG_EXTRA_ENV_SETTINGS
# define CONFIG_EXTRA_ENV_SETTINGS \
"dfu_alt_info_ram=uboot.bin ram 0x8000000 0x1000000;" \
"atf-uboot.ub ram 0x10000000 0x1000000;" \
"Image ram 0x80000 0x3f80000;" \
"system.dtb ram 0x4000000 0x100000\0" \
"dfu_bufsiz=0x1000\0"
#endif
#define CONFIG_SPL_STACK 0xfffffffc
#define CONFIG_SPL_MAX_SIZE 0x40000
/* Just random location in OCM */
#define CONFIG_SPL_BSS_START_ADDR 0x0
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#if defined(CONFIG_SPL_SPI_FLASH_SUPPORT)
# define CONFIG_SYS_SPI_KERNEL_OFFS 0x80000
# define CONFIG_SYS_SPI_ARGS_OFFS 0xa0000
# define CONFIG_SYS_SPI_ARGS_SIZE 0xa0000
#endif
/* u-boot is like dtb */
#define CONFIG_SPL_FS_LOAD_ARGS_NAME "u-boot.bin"
#define CONFIG_SYS_SPL_ARGS_ADDR 0x8000000
/* ATF is my kernel image */
#define CONFIG_SPL_FS_LOAD_KERNEL_NAME "atf-uboot.ub"
/* FIT load address for RAM boot */
#define CONFIG_SPL_LOAD_FIT_ADDRESS 0x10000000
/* MMC support */
#ifdef CONFIG_MMC_SDHCI_ZYNQ
# define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION 1
# define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTOR 0 /* unused */
# define CONFIG_SYS_MMCSD_RAW_MODE_ARGS_SECTORS 0 /* unused */
# define CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR 0 /* unused */
# if defined(CONFIG_SPL_LOAD_FIT)
# define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.itb"
# else
# define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.img"
# endif
#endif
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_DFU)
# undef CONFIG_CMD_BOOTD
# define CONFIG_SPL_ENV_SUPPORT
# define CONFIG_SPL_HASH_SUPPORT
# define CONFIG_ENV_MAX_ENTRIES 10
#endif
#define CONFIG_SYS_SPL_MALLOC_START 0x20000000
#define CONFIG_SYS_SPL_MALLOC_SIZE 0x100000
#ifdef CONFIG_SPL_SYS_MALLOC_SIMPLE
# error "Disable CONFIG_SPL_SYS_MALLOC_SIMPLE. Full malloc needs to be used"
#endif
#define CONFIG_BOARD_EARLY_INIT_F
#endif /* __XILINX_ZYNQMP_H */
实际的修改是在约 196 行添加了部分内容,增加对于一些初始环境变量的定义:
/* Initial environment variables */
#ifndef CONFIG_EXTRA_ENV_SETTINGS
#define CONFIG_EXTRA_ENV_SETTINGS \
ENV_MEM_LAYOUT_SETTINGS \
BOOTENV \
DFU_ALT_INFO \
"kernel_addr_r=0x2000000\0" \
"ethaddr=00:0a:35:00:22:01\0" \
"kernel_image=image.ub\0" \
"kernel_load_address=0x2008000\0" \
"devicetree_image=devicetree.dtb\0" \
"devicetree_load_address=0x2000000\0" \
"bitstream_image=system.bit.bin\0" \
"boot_image=BOOT.bin\0" \
"loadbit_addr=0x100000\0" \
"loadbootenv_addr=0x2000000\0" \
"kernel_size=0x500000\0" \
"devicetree_size=0x20000\0" \
"bootenv=uEnv.txt\0" \
"loadbootenv=load mmc 0 ${loadbootenv_addr} ${bootenv}\0" \
"uenvboot=" \
"if run loadbootenv; then " \
"echo Loaded environment from ${bootenv}; " \
"run importbootenv; " \
"fi; " \
"if test -n $uenvcmd; then " \
"echo Running uenvcmd ...; " \
"run uenvcmd; " \
"fi\0" \
"sdboot=if mmcinfo; then " \
"run uenvboot; " \
"echo Copying Linux from SD to RAM... && " \
"load mmc 0 ${kernel_load_address} ${kernel_image} && " \
"bootm ${kernel_load_address}; " \
"fi\0" \
"netboot=tftpboot ${kernel_load_address} ${kernel_image} && bootm\0" \
"default_bootcmd=run sdboot;\0" \
"nandboot=echo Copying Linux from NAND flash to RAM... && " \
"nand read ${kernel_load_address} 0x100000 ${kernel_size} && " \
"nand read ${devicetree_load_address} 0x600000 ${devicetree_size} && " \
"echo Copying ramdisk... && " \
"nand read ${ramdisk_load_address} 0x620000 ${ramdisk_size} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0" \
"jtagboot=echo TFTPing Linux to RAM... && " \
"tftpboot ${kernel_load_address} ${kernel_image} && " \
"tftpboot ${devicetree_load_address} ${devicetree_image} && " \
"tftpboot ${ramdisk_load_address} ${ramdisk_image} && " \
"bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}\0"
#endif
一些关键的变量及其含义如下:
首先是内存和加载地址:
变量 | 值 | 说明 |
---|---|---|
kernel_addr_r | 0x2000000 | Linux 内核在 RAM 中的加载地址 |
kernel_load_address | 0x2008000 | 内核从存储设备加载到 RAM 的目标地址 |
devicetree_load_address | 0x2000000 | 设备树(.dtb)的加载地址 |
loadbit_addr | 0x100000 | FPGA 比特流(.bit.bin)的加载地址 |
loadbootenv_addr | 0x2000000 | 环境变量文件(uEnv.txt)的加载地址 |
kernel_size | 0x500000 | 内核镜像的最大大小(5MB) |
devicetree_size | 0x20000 | 设备树文件的最大大小(128KB) |
然后是相关文件的文件名(后续可以根据生成的默认名字对这里进行修改,就不用频繁改名了):
变量 | 值 | 说明 |
---|---|---|
kernel_image` | image.ub | Linux 内核镜像文件名 |
devicetree_image | devicetree.dtb | 设备树文件名 |
bitstream_image | system.bit.bin | FPGA 比特流文件名 |
boot_image | BOOT.bin | Zynq 启动镜像 |
bootenv | uEnv.txt | U-Boot 环境变量文件 |
最后是网络配置:
变量 | 值 | 说明 |
---|---|---|
ethaddr | 00:0a:35:00:22:01 | 以太网 MAC 地址(需确保唯一) |
netboot | tftpboot … && bootm | 从 TFTP 服务器加载内核并启动 |
3.3 创建板级文件夹
uboot 中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等。Xilinx 的 ZYNQ MP 系列芯片的所有板级文件夹都存放在 board/xilinx/zynqmp
目录下,ZYNQ-7000 系列芯片的所有板级文件夹都存放在 board/xilinx/zynq
目录下。
对于 ZYNQ MP 系列芯片,可以参考 zynqmp-zcu102-rev1.0
:
如果是 ZYNQ-7000 系列芯片,可以参考 zynq-zed
:
我这里就把 zynqmp-zcu102-rev1.0
复制一份出来:
cd board/xilinx/zynqmp/
cp -r zynqmp-zcu102-rev1.0 zynqmp-alinx3eg
我们可以进去看一下,其实里面也就只有一个文件:
该文件是 Xilinx 提供的官方初始化代码,用于在 U-Boot 启动阶段配置 ZynqMP 的 PS(ARM Cortex-A53)部分,包括时钟初始化(PLL、时钟分频)、DDR 控制器配置(时序、电压、大小)、MIO 引脚复用、PS 电源域的配置以及外设初始化(QSPI、SDIO、USB、以太网等)。
如果是 ZYNQ-7000 的芯片,名字为 ps7_init_gpl.c
,功能类似,不过 ZYNQ-7000系列配置的就是 ARM Cortex-A9,而不是 A53 了。
3.4 添加板卡的设备树
首先,我们需要获取自己的某个具体的工程的设备树文件,上一篇:【ZYNQ Linux移植】2-获取设备树 中已做了介绍,这里不再赘述。
uboot 支持设备树,每个开发板都有一个对应的设备树文件。Xilinx 的 ZYNQ MP 系列芯片的所有设备树文件夹都存放在 arch/arm/dts/
目录下,我们先看下该目录下相关的设备树文件,命令如下:
cd arch/arm/dts
ls zynq*
这里注意到两个设备树头文件:zynqmp-clk-ccf.dtsi
与 zynqmp.dtsi
。这就是在上一篇:【ZYNQ Linux移植】2-获取设备树中提到的通用的设备树文件,它们不需要进行备份替换。
把上一篇备份好的所有相关的设备树文件拷贝到 arch/arm/dts/
目录下,然后把将 system-top.dts 文件重命名为 zynqmp-ainx3eg.dts,与 3.1 中的板卡配置文件保持一致。
然后,对这个目录( arch/arm/dts/
)下的 Makefile 进行修改,使得整体编译时把我们前面加进来的 zynqmp-ainx3eg.dts 编译为 zynqmp-ainx3eg.dtb。使用 vim 或 gedit 编辑:
gedit Makefile
vi Makefile
3.5 创建脚本进行编译
以上所有准备工作都完毕后,就可以进行编译了。编译都是需要在 u-boot 的根目录下进行的。可以直接用命令编译,但更推荐的,是把命令写成一个 shell 脚本。
使用 vim 或者 touch 命令 加 gedit 命令(不熟悉vim的使用)在根目录创建一个名为 zynqmp.sh 的 shell 脚本,并进行编辑:
touch zynqmp.sh
vi zynqmp.sh
将其内容修改为:
#!/bin/bash
make distclean
make zynqmp_alinx3eg_defconfig
make -j16
其中 zynqmp_alinx3eg_defconfig
是我们在 3.1 创建的配置文件,make -j16
是使用 16 线程进行编译,这个数字根据自己的配置进行修改。
然后需要给予我们的脚本可执行权限,我这里是直接权限拉满了:
chmod 777 zynqmp.sh
之后就可以运行编译了:
./zynqmp.sh
哪怕是第一次编译,这个编译的时间也不太长,我这边不到 10s 就完成了编译。
我们可以通过以下命令看看我们添加的头文件有没有被引用,看看我们的新板卡是否添加成功:
grep -nR "zynqmp_alinx3eg.h"
如果能看到很多输出结果,那就表示已经没有问题。
与此同时,我们也注意到,在u-boot的根目录下,已经生成了许多原来没有的文件,原来的结构是下图所示:
编译后为:
3.6 备份相关文件
编译完之后,我们需要把 3 个生成的文件备份一下,分别是 boot.bin 、u-boot.elf (有的版本生成结果可能只有u-boot,那就把它添加后缀 .elf
,但注意如果生成了u-boot.elf就直接用这个文件,再采用改后缀的方法启动不起来)、与 u-boot.img。它们生成的路径如下:
boot.bin :u-boot根目录/spl
u-boot.elf :u-boot根目录
u-boot.img :u-boot根目录
其中 u-boot.elf 将会参与后面的 u-boot 测试。
4 测试
4.1 构建 boot.bin
首先需要知道,boot.bin 需要包含哪些内容。如果是 ZYNQ MP 系列的话,主要是以下 4 个文件:
1,fsbl.elf:来自于上一篇 Vitis 生成
2,pmufw.elf:来自于上一篇 Vitis 生成,注意!是ZYNQ MP特有!
3,XXX.bit(如果有 PL 端程序的话):来自于上一篇 Vitis 生成
4,bl31.elf:ZYNQ MP特有,且必须包含!来源下文有交代
5,u-boot.elf:来自于这一篇中的 u-boot 编译
而如果是ZYNQ-7000系列的话,就没有 pmufw.elf ,只有 3个文件了。
构建 boot.bin 的话,主要有两种方式,一是使用 Petalinux,二是 使用 Vivado 或 Vitis 带的工具 bootgen 生成。这两种方式相比较而言,前者是比较简单的。
4.1.1 使用 bootgen 生成
我们可以把它们单独放在 Ubuntu虚拟机 的一个文件夹下,并创建一个 .bif 文件:
如果是 ZYNQ MP 系列的话,在 .bif 文件中加入以下内容:
//arch = zynqmp; split = false; format = BIN
the_ROM_image:
{
[bootloader]./zynqmp_fsbl.elf
[pmufw_image]./pmufw.elf
./system.bit
[destination_cpu=a53-0,exception_level=el-3]./bl31.elf"
[destination_cpu=a53-0,exception_level=el-2]./u-boot.elf
}
这里文件的先后顺序是不能够随意编排的,我这里顺序参考了Petalinux的顺序:
这里也可以根据自己的需求决定是否把设备树添加进去(.dtb)
如果大家完全从零开始,这里应该是没有 bl31.elf 的,而 ZYNQ MP必须 提供 bl31.elf
,因为 ARMv8-A 要求通过 ATF 实现安全监控模式(EL3)。如果有 Petalinux 的话,可以直接去 petalinux工程目录/images/linux
自己复制。我这里分享一下,有需要的自取。
而如果是 ZYNQ-7000系列的话,需要去掉 pmufw.elf 与 bl31.elf 两行,并对一些名称进行修改:
//arch = zynq; split = false; format = BIN
the_ROM_image:
{
[bootloader]./zynq_fsbl.elf
./system.bit
[destination_cpu=a53-0,exception_level=el-2]./u-boot.elf
}
编辑好后,就可以在当前文件夹,打开终端,首先配置 Vivado 或 Vitis 的环境变量:
source /tools/Xilinx/Vivado/2020.1/settings64.sh
source /tools/Xilinx/Vitis/2020.1/settings64.sh
只执行其中之一即可,如果安装路径不一样,可能需要修改路径。
再使用 bootgen 命令生成 BOOT.bin:
bootgen -arch zynqmp -image sd_image.bif -o /boot/BOOT.bin -w on(zynqmp系列)
bootgen -arch zynq -image sd_image.bif -o /boot/BOOT.bin -w on(zynq-7000系列)
这里特别注意!bootgen这个工具,默认的 arch 是 zynq,所以如果是 zynqmp 器件的话,一定要多加上这个参数,指定其为 zynqmp,否则将报以下错误:
如果没有问题,结果如下图所示:
当然,在Windows 下,也可以使用 Vitis 生成 BOOT.bin,下面是操作的步骤:
同样是放到一个文件夹:
打开 Vitis,按下图进入启动镜像生成页面:
Windows 下的 架构是默认跟打开工程挂钩的,如果是直接打开的,注意下选择自己器件的系列,然后选择 .bif
文件目录就好,按照我们上面 .bif
的内容,Vitis可以自动填充需要加入启动镜像的部分,不需要再自己选择了。
然后去生成目录找 BOOT.bin 即可:
4.1.2 使用 Petalinux 生成
首先把 Petalinux 工程编译一下:
source /opt/pkg/petalinux/settings.sh
petalinux-build
编译完后,会在以下路径生成镜像相关文件:
petalinux工程目录/images/linux
然后我们把前面 3.6 中的 u-boot.elf
复制过来,覆盖掉这里 Petalinux 的 u-boot.elf
。
再使用以下命令生成 BOOT.bin:
petalinux-package --boot --u-boot --fsbl --force
petalinux-package --boot --u-boot --fpga --fsbl --force(有pl端)
4.2 向SD放入相关文件
首先需要对 SD 卡进行分区,在这里,可以只有 FAT 一个分区,也可以按照经典的结构,一个 FAT 分区(放启动文件相关),一个 EXT4 分区(放根文件系统),至于为什么要进行分区,可以参考我以前的一篇博客:嵌入式Linux开发为什么要设置FAT与EXT两个分区。
完成 4.1.2 中的 BOOT.bin 的生成后,就可以把其中的至多 3 个文件拷贝进 SD 卡的 FAT 分区了:
注意这里的 boot.src
和 image.ub
都不是 u-boot 启动所必需的,只有 BOOT.bin 也可以使用只是后面用来测试加载镜像使用,不用 Petalinux 的话,也只有这个文件。
4.3 上板测试
把 SD 卡插入板卡,把板卡设置为 SD 卡启动,接上串口,上电启动!
然后我们可以输入一些 u-boot 的命令查看是否正常,比如先看看有哪些文件:
ls mmc 1:1
如果前面使用 Petalinux 把镜像也加进来了,这里可以试试能否加载镜像:
fatload mmc 1:1 8000000 image.ub
bootm 8000000
能运行到这说明我们的 u-boot 就算已经可以用了!
5 总结
在移植过程中,涉及的目录:
1,配置文件目录:u-boot根目录/configs
2,头文件目录:u-boot根目录/include/configs/
3,板级文件夹目录:
ZYNQ 7000系列:u-boot根目录/board/xilinx/zynq
ZYNQ MP系列:u-boot根目录/board/xilinx/zynqmp
4,设备树目录:u-boot根目录/arch/arm/dts
最后获取的目标文件:
boot.bin :u-boot根目录/spl
u-boot.elf :u-boot根目录
u-boot.img :u-boot根目录
6 参考资料
1,DeepSeeker相关搜索结果;
2,《正点原子 MPSoC-P5B之嵌入式Linux开发指南_V1.0》