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。
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的执行流程:
1.2 fsbl EMMC启动模式的支持
- 一般情况下fsbl,bitstream和u-boot都在FLASH中,从单一FLASH中启动,此时我们不使能MMC_SUPPORT标志。但FSBL也支持EMMC FLASH的设备。
- 当我们的QSPI FLASH容量较小,而镜像文件较大时,我们可以把fsbl放在QSPI FLASH中,把镜像文件的其他部分放在eMMC中。
- 配置流程如下所示:
- Create a BSP with the library and set enable_mmc in the SDK options. For more details, see the library documentation.
- Enable the MMC_SUPPORT flag through SDK and build FSBL. The FSBL image build
(fsbl.elf) now has eMMC support. - Stitch the boot image with FSBL as the only partition (using Bootgen).
- Place the boot image in the QSPI flash.
- 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. - Set the boot mode to QSPI.
- Power cycle the board.
2.FSBL分析
2.1 工程结构
- 在Vivado中定制好硬件后,包括bitstream导出到SDK。如下图所示:
- xillydemo_hw_platform_0是我导出的硬件工程,其中包含了在Vivado对PS部分的定制情况,主要包含一些外设寄存器的地址和相应的配置函数。
- fsbl_bsp是fsbl工程的板级支持包,在建立fsbl工程时根据xillydemo_hw_platform_0而产生,主要给fsbl工程提供底层的硬件访问的支持。
- fsbl工程调用了fsbl_bsp中的函数,编译后生成fsbl.elf文件。
2.2 源码分析
- 在fsbl_bsp的asm_vectors.S文件中,给Cortex A9初始化了一个向量表。上电后,BootROM将fsbl读到OCM中执行,fsbl跳转到boot.S中标号为_boot的代码处执行。
- 在boot.S的注释中,给出了boot.S的作用。在boot.S中,进行了一些最简化的配置从而使处理器从初始化状态运行,下面是一系列在控制进入main函数之前要完成的配置顺序。最后跳转到xil-ctrl0.S文件中的_start标号中执行。
_boot标号在这里:
进行上面描述的一系列操作后,在这里跳转到xil-ctrl0.S文件的_start标号去。 - 在xil-ctrl0.S文件中的_start标号如下,一开始就跳到_cpu_init标号中
- _cpu_init标号在cpu_init.S文件中,用于CPU的特定初始化。
完成了一系列初始化后,在boot.S文件中跳转至main函数开始执行。 - 在main.c中有关于FSBL功能的描述。FSBL在OCM中运行,根据启动模式的选择,FSBL从FLASH中复制启动镜像的其他部分。如果有bitstream,那么就加载到FPGA中。如果有应用程序存在,那么就加载到DDR中并将执行权限移交给它。
- 在main函数中定义了一些常量后,调用ps7_init()函数进行板级的初始化,MIO、PLL、CLK、DDR。
在ps7_init()函数中,先调用ps7GetSiliconVersion()函数获取PS版本号,根据不同的版本号选择不同的初始化数据。
ps7GetSiliconVersion()函数如下所示,通过访问一个寄存器特定的位值获取版本号。在地址 0xF8007080处的高4bit是要获取的值。详情可见ug585,p1162。
在下面调用ps7_config()函数初始化相应的外设。 - 然后main函数调用SlcrUnlock()解锁SLCR寄存器。在ug585 p114有关于SLCR寄存器的描述。
- 然后在main函数中可以定义宏FSBL_PERF,进行FSBL的性能测试,计算FSBL的运行时间。
- 调用Xil_DCacheFlush()函数清除D-Cache,调用Xil_DCacheDisable()函数关闭D-Cache。
- 调用RegisterHandlers()函数注册异常错误代码。在该函数中初始化向量表。在ARM遇到这些异常情况时,就执行相应的异常处理程序。异常处理程序主要打印了遇到的异常状态。
- 打印FSBL的标志,SDK的版本以及编译fsbl的时间等等。
- 然后进行DDR读写测试,验证DDR是否可用。向DDR在CPU系统总线的物理地址范围内写两个数,并读出判断是否相同,若相同则DDR初始化完成。
- 进行PCAP初始化。PCAP用来与PL进行通信加载bitstream。
在初始化PCAP的函数中初始化了devcfg接口驱动。 - 获取PCAP控制器的设置。判断是否使用看门口。
- 存储FSBL的运行状态到Reboot Status Register。
- 读取启动模式寄存器。见ug585 p1623。
- 对启动模式进行判断处理。并对相应模式下的控制器进行初始化。
QSPI模式:
NOR模式:
SD模式:
MMC模式:
JTAG模式: - 下面对SD模式进行分析。在InitSD()函数中,传进了启动镜像的文件名BOOT.BIN。InitSD函数挂载并打开SD设备,MoveImage是一个函数指针,函数SDAccess()用于对SD类型的设备进行访问,可用于对SD及EMMC设备进行统一访问。
SDAccess()函数用于读取SD FLASH地址中的数据到用户的地址空间当中。f_open、f_read、f_leek、f_close都是操作FatFs文件系统的函数。 - 然后检查FLASH类型的设备地址是否有效。
20.调用LoadBootImage()函数加载启动镜像文件,并返回移交到的执行地址。 - 下面对LoadBootImage函数进行分析。
首先定义了很多标志和变量:
如果芯片版本SILICON_VERSION_1,那么进行下面的操作。获取重启状态寄存器的值,如果上一次启动失败,那么更新该值。
然后从一个给定的寄存器中读取镜像开始的地址。
如果芯片版本较高,那么读取multiboot寄存器值,并据此计算镜像开始的地址。
得到了镜像的开始地址ImageStartAddress后,读取镜像头BootHeader的一些信息到全局变量中。
FSBL忽略第一Partition的头,因为它描述的是FSBL本身的信息。MMC启动时启动镜像内不含有FSBL,所以此时不用略过。
接下来遍历所有Partition的头,读取头中信息并保存在局部变量中。
当Partition的所有权是FSBL时,FSBL
当Partition是bitstream或ps程序时,将相应的标志位置位。
进行一系列的RSA校验和加载地址校验后,获取ps部分程序的加载地址。
从启动设备中将partitions移动到指定的地址,并完成bitstream加载。 - 最后在FsblHandoff函数中将执行权限移交给u-boot或裸机程序。FsblHandoff中调用了FsblHandoffExit函数。
21.在fsbl_handoff.S文件中,目标地址被放入链接寄存器LR中,最后BX LR将执行流跳转至LR中存储的地址处,ARM开始从此处执行DDR中的elf文件。至此FSBL的使命结束,bitstream完成加载,裸机程序或u-boot程序被启动。