【ZYNQ Linux移植】3-u-boot移植

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_r0x2000000Linux 内核在 RAM 中的加载地址
kernel_load_address0x2008000内核从存储设备加载到 RAM 的目标地址
devicetree_load_address0x2000000设备树(.dtb)的加载地址
loadbit_addr0x100000FPGA 比特流(.bit.bin)的加载地址
loadbootenv_addr0x2000000环境变量文件(uEnv.txt)的加载地址
kernel_size0x500000内核镜像的最大大小(5MB)
devicetree_size0x20000设备树文件的最大大小(128KB)

       然后是相关文件的文件名(后续可以根据生成的默认名字对这里进行修改,就不用频繁改名了):

变量说明
kernel_image`image.ubLinux 内核镜像文件名
devicetree_imagedevicetree.dtb设备树文件名
bitstream_imagesystem.bit.binFPGA 比特流文件名
boot_imageBOOT.binZynq 启动镜像
bootenvuEnv.txtU-Boot 环境变量文件

       最后是网络配置

变量说明
ethaddr00:0a:35:00:22:01以太网 MAC 地址(需确保唯一)
netboottftpboot … && 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.dtsizynqmp.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.binu-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.srcimage.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》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辣个蓝人QEX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值