ZYNQ_FSBL学习

1 FSBL介绍

1.1 fsbl涉及的启动流程

  • zynq内部的BootROM存储有一段在CPU复位后固定执行的代码。称为stage-0启动代码。
  • 这段代码用来配置一个ARM CPU和一些必要外设,从而能从一个启动设备中获取FSBL(first stage boot loader)执行。BootROM是一个ROM,不可写,PL的配置不是通过BootROM实现的。BootROM不能使用DDR和SCU,因为它们还没有初始化。
  • FSBL通常存储在FLASH中,BootROM从选定的FLASH中拷贝FSBL到片上存储器OCM中执行。FSBL也支持QSPI-EMMC模式,在这种情况下,我们可以把FSBL放在QSPI-FLASH中,把镜像文件其它较大的部分放在EMMC FLASH中。
  • 下图描述了BootROM读取FSBL到OCM中的流程:ARM上电后执行BootROM,发现有合法镜像(BOOT.BIN)后判断是否要在FLASH中执行(利用XIP技术就不用把代码读到RAM中了,NOR FLASH支持),若没有合法镜像则到下一个32K的偏移处寻找镜像。如果不XIP,则将FSBL载入到OCM中执行,因为此时OCM是不能使用DDR的,之后开始执行FSBL。
    ZYNQ_FSBL学习_第1张图片

1.2 fsbl的功能

  • 初始化PS的配置,具体的配置方法由Xilinx硬件配置工具提供。
  • 如果镜像中有bitstream文件部分,则用它配置PL部分。
  • 从非易失性存储器(NAND/NOR FLASH)中加载SSBL(second stage boot loader,u-boot)或裸机程序到RAM(DDR)中,并将执行权限移交给它。
  • 注意,在将权限移交给SSBL或裸机程序之前,FSBL会关闭cache和MMU,因为u-boot在执行开始时假设它们都是关闭的。
  • 下图是FSBL的执行流程:
    ZYNQ_FSBL学习_第2张图片

1.2 fsbl EMMC启动模式的支持

  • 一般情况下fsbl,bitstream和u-boot都在FLASH中,从单一FLASH中启动,此时我们不使能MMC_SUPPORT标志。但FSBL也支持EMMC FLASH的设备。
  • 当我们的QSPI FLASH容量较小,而镜像文件较大时,我们可以把fsbl放在QSPI FLASH中,把镜像文件的其他部分放在eMMC中。
  • 配置流程如下所示:
  1. Create a BSP with the library and set enable_mmc in the SDK options. For more details, see the library documentation.
  2. Enable the MMC_SUPPORT flag through SDK and build FSBL. The FSBL image build
    (fsbl.elf) now has eMMC support.
  3. Stitch the boot image with FSBL as the only partition (using Bootgen).
  4. Place the boot image in the QSPI flash.
  5. Stitch an image (using Bootgen) with all the other required partitions (like the bitstream
    or the U-Boot) and place it in the eMMC flash.
  6. Set the boot mode to QSPI.
  7. Power cycle the board.

2.FSBL分析

2.1 工程结构

  • 在Vivado中定制好硬件后,包括bitstream导出到SDK。如下图所示:
    ZYNQ_FSBL学习_第3张图片
  • xillydemo_hw_platform_0是我导出的硬件工程,其中包含了在Vivado对PS部分的定制情况,主要包含一些外设寄存器的地址和相应的配置函数。
  • fsbl_bsp是fsbl工程的板级支持包,在建立fsbl工程时根据xillydemo_hw_platform_0而产生,主要给fsbl工程提供底层的硬件访问的支持。
  • fsbl工程调用了fsbl_bsp中的函数,编译后生成fsbl.elf文件。

2.2 源码分析

  1. 在fsbl_bsp的asm_vectors.S文件中,给Cortex A9初始化了一个向量表。上电后,BootROM将fsbl读到OCM中执行,fsbl跳转到boot.S中标号为_boot的代码处执行。
    ZYNQ_FSBL学习_第4张图片
  2. 在boot.S的注释中,给出了boot.S的作用。在boot.S中,进行了一些最简化的配置从而使处理器从初始化状态运行,下面是一系列在控制进入main函数之前要完成的配置顺序。最后跳转到xil-ctrl0.S文件中的_start标号中执行。
    ZYNQ_FSBL学习_第5张图片
    _boot标号在这里:
    在这里插入图片描述
    进行上面描述的一系列操作后,在这里跳转到xil-ctrl0.S文件的_start标号去。
    在这里插入图片描述
  3. xil-ctrl0.S文件中的_start标号如下,一开始就跳到_cpu_init标号中
    ZYNQ_FSBL学习_第6张图片
  4. _cpu_init标号在cpu_init.S文件中,用于CPU的特定初始化。
    ZYNQ_FSBL学习_第7张图片
    完成了一系列初始化后,在boot.S文件中跳转至main函数开始执行。
    ZYNQ_FSBL学习_第8张图片
  5. main.c中有关于FSBL功能的描述。FSBL在OCM中运行,根据启动模式的选择,FSBL从FLASH中复制启动镜像的其他部分。如果有bitstream,那么就加载到FPGA中。如果有应用程序存在,那么就加载到DDR中并将执行权限移交给它。
    ZYNQ_FSBL学习_第9张图片
  6. 在main函数中定义了一些常量后,调用ps7_init()函数进行板级的初始化,MIO、PLL、CLK、DDR。
    ZYNQ_FSBL学习_第10张图片
    在ps7_init()函数中,先调用ps7GetSiliconVersion()函数获取PS版本号,根据不同的版本号选择不同的初始化数据。
    ZYNQ_FSBL学习_第11张图片
    ps7GetSiliconVersion()函数如下所示,通过访问一个寄存器特定的位值获取版本号。在地址 0xF8007080处的高4bit是要获取的值。详情可见ug585,p1162。
    ZYNQ_FSBL学习_第12张图片
    ZYNQ_FSBL学习_第13张图片
    ZYNQ_FSBL学习_第14张图片
    在下面调用ps7_config()函数初始化相应的外设。
    ZYNQ_FSBL学习_第15张图片
  7. 然后main函数调用SlcrUnlock()解锁SLCR寄存器。在ug585 p114有关于SLCR寄存器的描述。
    在这里插入图片描述
    ZYNQ_FSBL学习_第16张图片
    ZYNQ_FSBL学习_第17张图片
  8. 然后在main函数中可以定义宏FSBL_PERF,进行FSBL的性能测试,计算FSBL的运行时间。
    ZYNQ_FSBL学习_第18张图片
    ZYNQ_FSBL学习_第19张图片
  9. 调用Xil_DCacheFlush()函数清除D-Cache,调用Xil_DCacheDisable()函数关闭D-Cache。
    ZYNQ_FSBL学习_第20张图片
  10. 调用RegisterHandlers()函数注册异常错误代码。在该函数中初始化向量表。在ARM遇到这些异常情况时,就执行相应的异常处理程序。异常处理程序主要打印了遇到的异常状态。
    在这里插入图片描述
    ZYNQ_FSBL学习_第21张图片
  11. 打印FSBL的标志,SDK的版本以及编译fsbl的时间等等。ZYNQ_FSBL学习_第22张图片
  12. 然后进行DDR读写测试,验证DDR是否可用。向DDR在CPU系统总线的物理地址范围内写两个数,并读出判断是否相同,若相同则DDR初始化完成。
    ZYNQ_FSBL学习_第23张图片
    ZYNQ_FSBL学习_第24张图片
  13. 进行PCAP初始化。PCAP用来与PL进行通信加载bitstream。
    ZYNQ_FSBL学习_第25张图片
    在初始化PCAP的函数中初始化了devcfg接口驱动。
    ZYNQ_FSBL学习_第26张图片
  14. 获取PCAP控制器的设置。判断是否使用看门口。
    ZYNQ_FSBL学习_第27张图片
  15. 存储FSBL的运行状态到Reboot Status Register。
    ZYNQ_FSBL学习_第28张图片
    在这里插入图片描述
  16. 读取启动模式寄存器。见ug585 p1623。
    ZYNQ_FSBL学习_第29张图片
    ZYNQ_FSBL学习_第30张图片
    ZYNQ_FSBL学习_第31张图片
  17. 对启动模式进行判断处理。并对相应模式下的控制器进行初始化。
    ZYNQ_FSBL学习_第32张图片
    QSPI模式:
    ZYNQ_FSBL学习_第33张图片
    NOR模式:
    ZYNQ_FSBL学习_第34张图片
    SD模式:
    ZYNQ_FSBL学习_第35张图片
    MMC模式:
    ZYNQ_FSBL学习_第36张图片
    JTAG模式:
    ZYNQ_FSBL学习_第37张图片
  18. 下面对SD模式进行分析。在InitSD()函数中,传进了启动镜像的文件名BOOT.BIN。InitSD函数挂载并打开SD设备,MoveImage是一个函数指针,函数SDAccess()用于对SD类型的设备进行访问,可用于对SD及EMMC设备进行统一访问。
    ZYNQ_FSBL学习_第38张图片
    ZYNQ_FSBL学习_第39张图片
    SDAccess()函数用于读取SD FLASH地址中的数据到用户的地址空间当中。f_open、f_read、f_leek、f_close都是操作FatFs文件系统的函数。
    ZYNQ_FSBL学习_第40张图片
  19. 然后检查FLASH类型的设备地址是否有效。
    ZYNQ_FSBL学习_第41张图片
    20.调用LoadBootImage()函数加载启动镜像文件,并返回移交到的执行地址。
    ZYNQ_FSBL学习_第42张图片
  20. 下面对LoadBootImage函数进行分析。
    首先定义了很多标志和变量:
    ZYNQ_FSBL学习_第43张图片
    如果芯片版本SILICON_VERSION_1,那么进行下面的操作。获取重启状态寄存器的值,如果上一次启动失败,那么更新该值。
    ZYNQ_FSBL学习_第44张图片
    ZYNQ_FSBL学习_第45张图片
    ZYNQ_FSBL学习_第46张图片
    然后从一个给定的寄存器中读取镜像开始的地址。
    在这里插入图片描述
    如果芯片版本较高,那么读取multiboot寄存器值,并据此计算镜像开始的地址。
    ZYNQ_FSBL学习_第47张图片
    得到了镜像的开始地址ImageStartAddress后,读取镜像头BootHeader的一些信息到全局变量中。
    ZYNQ_FSBL学习_第48张图片
    FSBL忽略第一Partition的头,因为它描述的是FSBL本身的信息。MMC启动时启动镜像内不含有FSBL,所以此时不用略过。
    ZYNQ_FSBL学习_第49张图片
    接下来遍历所有Partition的头,读取头中信息并保存在局部变量中。
    ZYNQ_FSBL学习_第50张图片
    当Partition的所有权是FSBL时,FSBL
    ZYNQ_FSBL学习_第51张图片
    当Partition是bitstream或ps程序时,将相应的标志位置位。
    ZYNQ_FSBL学习_第52张图片
    进行一系列的RSA校验和加载地址校验后,获取ps部分程序的加载地址。
    ZYNQ_FSBL学习_第53张图片
    从启动设备中将partitions移动到指定的地址,并完成bitstream加载。
    ZYNQ_FSBL学习_第54张图片
  21. 最后在FsblHandoff函数中将执行权限移交给u-boot或裸机程序。FsblHandoff中调用了FsblHandoffExit函数。
    ZYNQ_FSBL学习_第55张图片
    21.在fsbl_handoff.S文件中,目标地址被放入链接寄存器LR中,最后BX LR将执行流跳转至LR中存储的地址处,ARM开始从此处执行DDR中的elf文件。至此FSBL的使命结束,bitstream完成加载,裸机程序或u-boot程序被启动。
    ZYNQ_FSBL学习_第56张图片

你可能感兴趣的:(Xilinx,嵌入式设计相关)