0. 芯片启动概述

  • ZYNQ-7000系列芯片运行Linux操作系统需要BOOT.BIN文件、image.ub文件和rootfs。
  • BOOT.BIN文件由fsbl.elf、bitstream和u-boot.elf(裸机elf程序)文件组成。fsbl.elf是由xilinx设计的,由OCM加载执行,有两个主要功能。第一是用于加载bitstream到PL,第二是根据BOOT.BIN文件组成,执行u-boot或是裸机elf程序。bitstream文件是FPGA的配置文件。裸机elf程序用于在不启动操作系统的情况下运行一些软件。u-boot.elf是一种bootloader程序,可以根据不同的硬件情况,在不同的场景下引导操作系统镜像的执行。
  • image.ub文件是由操作系统的镜像文件uImage和设备树文件dtb组成。uImage由压缩过的操作系统镜像zImage和一段由u-boot在引导时会读取的头image_header_t(64B)组成,这个头用于储存u-boot在引导系统时所要获取的一些信息。zImage由未压缩的内核镜像vmlinux和解压代码组成。在u-boot引导内核时,会解压zImage到内存中执行。
  • rootfs是linux的根文件系统,在linux系统启动过程中,必须要挂载这样一个根文件系统。我们的可执行程序以及所用到的库文件、linux系统的配置文件等,都会存储在这样一个根文件系统中。

1. SD卡启动

  • SD卡启动的最大优点就是方便可携带,SD卡被分为两个分区。一个FAT32分区存放BOOT.BIN和image.ub文件,一个ext4分区存放rootfs。
  • zynq-7000有两个SD控制器,在blockdesign里配置zynq核时,根据硬件原理图显示(SD连到了PS端,电路接口与zynq的MIO40-45相连),选择SD0并选择正确的MIO口。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 输入 petalinux-config --get-hw-description -p ./配置硬件。
  • 选择SD0作为首选SDIO。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 选择SD作为BOOT.BIN的存储介质。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 默认FLASH作为u-boot环境变量的存储介质,u-boot会根据下面的FLASH分区表到指定的位置读取环境变量。忽略除bootenv外的其它部分,因为FLASH中根本就没有这些部分,它们都在SD卡上。在这里只会用到FLASH中的bootenv。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 根据前面所分析,内核镜像当然在SD卡上喽。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 下面还有能设置文件系统的存储介质,这里我们不管它,因为我们不从FALSH加载文件系统,不对它进行设置。那我们在哪设置呢?
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 在如下目录,设置文件系统的类型和位置,我们的文件系统在SD卡的第二分区,所以选择文件系统类型为SD,设备节点设置为/dev/mmcblk0p2
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 可以看到,有这么多种文件系统类型。从上到下依次为,内存文件系统、ramdisk文件系统、闪存文件系统、网络文件系统、SD文件系统。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 我们的设备树和系统镜像是绑定的,所以如下设置设备树。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 输入petalinux-config -c u-boot配置u-boot支持的启动内核方式,内核在SD卡中,所以这里是从SD启动。
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 如上所示,从SD卡启动已配置完成,输入petalinux-build编译内核即可。
  • 我们用petalinux配置完后,petalinux会根据我们的配置生成u-boot的环境变量,进入u-boot后输入printenv查看环境变量。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
  • 下面我简要分析一下u-boot的环境变量。
    bootcmd=run default_bootcmd bootcmd变量用于启动内核,在u-boot中输入run bootcmd即可启动内核。
    default_bootcmd=run cp_kernel2ram && bootm ${netstart}
    cp_kernel2ram=mmc dev 0 && mmcinfo && fatload mmc 0 ${netstart} ${kernel_img} cp_kernel2ram变量用于将内核加载到内存中,mmc dev 0用于切换到设备mmc0,mmcinfo用于打印mmc的信息,fatload用于从FAT32文件系统中加载文件到内存的给定位置。netstart=0x10000000给定了加载到的内存位置,kernel_img=image.ub指定了文件的名称。bootm用于从指定的内存地址启动系统镜像,在这里即从加载到的位置启动镜像。
  • 在系统启动过程中我们看到,挂载了SD卡上的rootfs。
    ZYNQ-7000芯片用u-boot启动linux系统方法

2. QSPI_FLASH 启动

  • QSPI_FLASH 启动时,FLASH被分为四个分区。partition0存放BOOT.BIN文件,partition1存放u-boot的环境变量,partition2存放image.ub,partition3存放文件系统。

  • 一般情况下,当应用程序需要使用一些库,如Qt库时,文件系统所占空间会比较大。当FPGA逻辑部分比较复杂时,bitstream文件也会比较大,故BOOT.BIN文件会很大。我的硬件FLASH只有32MB,在这种情况下是肯定放不下的。所以一般不使用这种方式。

  • 下面我简要介绍一下这种方式的实现。

  • QSPI_FLASH启动时,我们要用到FLASH芯片了,我们要在blockdesign里配置QSPI用于读写FLASH。我们使用的核心板上两块FLASH芯片并行连接成8bit宽,每个FLASH为4bit宽,即4线spi(QSPI)。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 我们要根据每个文件的大小对FLASH的分区进行设置,一般情况下,FLASH是放不下这些文件的。每个分区大小设置如下,其实已大于FLASH的总大小32MB。下面估计每个文件的大小:
    Boot.BIN 17.9MB 分配18MB 0x1200000
    bootenv 分配128KB 0x20000
    image.ub 3.7MB 分配4MB 0x400000
    jffs2 分配8MB 0x800000 实际14.1MB
    我们看到,32MB的FLASH根本放不下!!!!!
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • BOOT.BIN,linux内核及文件系统都在FLASH里。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 文件系统类型选择jfss2闪存文件系统。
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 同样,U-boot选择从FLASH启动linux内核。
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 镜像编译完成后,我们反编译设备树,可以看到FLASH的分区如下所示,已经超出了FLASH的32MB存储范围。
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 用JTAG将BOOT.BIN下载至FLASH后,在u-boot启动后打印环境变量。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 下面简要分析一下该环境变量。
    bootcmd=run default_bootcmddefault_bootcmd=run cp_kernel2ram && bootm ${netstart} 如前文所述,不再叙述。
    cp_kernel2ram=sf probe 0 && sf read ${netstart} ${kernelstart} ${kernelsize}
    cp_kernel2ram用于将内核镜像从存储介质复制到内存执行。 sf probe 0用于初始化与给定SPI总线相连接的FLASH设备。sf read用于从给定的内存地址读取数据并保存到FLASH上。netstart=0x10000000为镜像加载的内存地址,kernelstart=0x1220000为给内核分配的FLASH起始地址,kernelsize=0x400000为给内核分配的FLASH存储空间。

  • 如下图,设置板子和主机的ip地址,连接以太网并打开tftp服务器,通过通过tftp下载image.ub和jfss2到FLASH中,并启动内核。注意到,内核不一定能启动成功,因为FLASH放不下jffs2。
    set serverip 192.168.10.124
    set ipaddr 192.168.10.1
    tftp 0x10000000 image.ub
    sf probe 0
    sf erase 0x01220000 0x400000
    sf write 0x10000000 0x01220000 0x400000
    tftp 0x10000000 rootfs.jffs2
    sf erase 0x1620000 0xd6e770
    sf write 0x10000000 0x1620000 0xd6e770
    sf erase 0x1620000 0x800000
    sf write 0x10000000 0x1620000 0x800000
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 可以看到,完整的jffs2根本就无法下载到FLASH中,只能将不完整的jffs2文件系统下载到FLASH中。

  • 现在,FLASH中已经有BOOT.BIN,image.ub,jffs2了,重启后,u-boot会从FLASH中读取内核并挂载jffs2,可能启动失败,因为文件系统不完整!!

  • 我们看到,在系统启动过程中,打印了许多jffs2错误和空间不足等信息,这是我们预料之中的。最神奇的是,我们竟然能登录进入系统。不得不说,Linux真的牛逼!!虽然系统能够进去,但使用肯定是不正常的,因为外存空间不足了。
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

  • 大家最好还是别用这种方式了,如果有EMMC的话,它不香吗?

3. QSPI_FLASH + EMMC启动

  • 在项目中,我一般使用这种方式,QSPI_FLASH + EMMC启动最大的优点是EMMC芯片稳定、可靠性高、能适用于震动等环境相对恶劣的场合。
  • QSPI_FLASH + EMMC启动时,BOOT.BIN文件存放在FLASH中,大容量(8GB)EMMC芯片被分为两个分区,第一个FAT32分区存放image.ub,第二个ext4分区存放rootfs。
  • 我所使用的开发板EMMC接在了PL端,PS端通过EMIO接口路由到PL端,我们要在PL端进行约束,从而使得PS能与EMMC通信。(关于MIO与EMIO可见ZYNQ-7000 GPIO使用
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法
    ZYNQ-7000芯片用u-boot启动linux系统方法

4. QSPI_FLASH + RAMFS启动

5. QSPI_FLASH + NFS启动

相关文章:

  • 2021-04-10
  • 2021-12-18
  • 2022-12-23
  • 2021-07-16
  • 2021-07-11
  • 2022-12-23
  • 2021-12-23
  • 2022-01-07
猜你喜欢
  • 2021-07-15
  • 2021-11-24
  • 2021-08-13
  • 2022-12-23
  • 2022-03-08
  • 2021-11-29
  • 2021-06-19
相关资源
相似解决方案