上一节讲了FSBL主要功能负责对PS端进行初始化、调用bitstream文件对PL端进行配置,加载应用或二级引导程序置DDR中并启动DDR,本节通过对FSBL代码进行分析深入理解代码。
参考资料
SDK版本:Release Version: 2018.3
.org 0
.text
.globl _vector_table
.section .vectors
_vector_table:
B _boot
B Undefined
B SVCHandler
B PrefetchAbortHandler
B DataAbortHandler
NOP /* Placeholder for address exception vector*/
B IRQHandler
B FIQHandler
于是就跳转到boot.S中执行_boot标号下的代码了,期间对包括DDR是否共享状态进行配置、使能了MMU和各级cache、中断初始化,当它执行完后,PS将具备执行C代码的能力,接着在_boot的代码中,再次执行了一个跳转:b _start
ps7_init()函数位于ps7_init.c文件中,这个C文件是由SDK根据用户的硬件hdf配置自动生成的。这个接口就是根据查看晶元版本对MIO、PLL、Clock、DDR、和外设进行初始化。
然后解锁SLCR系统级控制寄存器(使能系统软件复位功能),刷新D-cache,关闭D-cache,注册中断处理函数。
接下来就是对DDR区进行读写测试检验DDR状态是否正常;
初始化PACP(Processor Configuration Access Port即处理器配置接口,是PS与PL间通信的桥梁);配置FSBL正在执行状态;读取模式寄存器获取启动模式,如果是QSPI模式,则调用InitQspi()对flash进行初始化(flash可以启用);
接下来就是最关键的地方了,LoadBootImage()这个函数做两件事情①分析烧录到qspi中的数据的头的部分②根据分析结果拷贝数据到DDR中.
GetPartitionHeaderInfo()接口分析镜像文件BOOT.bin头部(又称bootrom头,实际上bootrom过程中,bootrom代码也会解析该头部并提取信息),并根据解析出的partition header数据解析出partition的数量,并把对应的信息头(fsbl、bit、应用程序3个)存放在PartitionHeader[MAX_PARTITION_NUMBER]数组中
注:fsbl、bit文件、应用elf通过sdk生成的BOOT.bin镜像文件头部是固定格式,其结构如下:
其中:
typedef struct StructPartHeader {
u32 ImageWordLen; /* 0x0 加密下分区长度(如果本身未加密则ImageWordLen和DataWordLen相等)/
u32 DataWordLen; / 0x4 未加密分区长度,也就是要搬到RAM中的数据长度*/
u32 PartitionWordLen; /* 0x8 整个(指的是单个)分区长度,,如果使能了RSA签名或者校验,则包括加密+填充+展开
+验证长度,大于等于ImageWordLen*/
u32 LoadAddr; /* 0xC 该分区要加载到RAM的地址*/
u32 ExecAddr; /* 0x10 加载后该分区的执行地址*/
u32 PartitionStart; /* 0x14 分区相对于起始镜像文件偏移*/
u32 PartitionAttr; /* 0x18 * 文件属性,用来判断文件类型如bitstream还是app,具体见下图/
u32 SectionCount; /* 0x1C /
u32 CheckSumOffset; / 0x20 /
u32 Pads1[1];
u32 ACOffset; / 0x28 /
u32 Pads2[4];
u32 CheckSum; / 0x3C */
}PartHeader;
拿到partition header后应该分别加载各个partition,但由于第0个partition其实就是FSBL,而我们当前其实已经在FSBL执行中了,所以不用加载直接跳过从partitionNum = 1开始加载。
接下来开始加载对各个partition是类似的,主要完成两部分工作:
①解析并检查各个partition header中内容的正确性
②从flash中加载各个partiton到指定的目标地址中。(这里对FPGA.bit和application.elf有所差别)
(实现的拷贝就是由PartitionStart参数获得拷贝的起始地址,由LoadAddr获得加载地址,)
LoadBootImage()搬移工作结束后将第一个PS段的程序(即一般情况下应用程序)的执行地址作为返回值返回。
注意:对于加密的程序会进行解密,加载到DDR中的程序时不加密的。
该接收调用FsblHandoffExit(FsblStartAddr);这个接口使用汇编代码实现的,代码在fsbl_handoff.S文件下:bx lr指令实现了跳转到application开始执行。(期间失能了I-cache和MMU)
FsblHandoffExit:
mov lr, r0 /* move the destination address into link register */
mcr 15,0,r0,cr7,cr5,0 /* Invalidate Instruction cache */
mcr 15,0,r0,cr7,cr5,6 /* Invalidate branch predictor array */
dsb
isb /* make sure it completes */
ldr r4, =0
mcr 15,0,r4,cr1,cr0,0 /* disable the ICache and MMU */
isb /* make sure it completes */
bx lr /* force the switch, destination should have been in r0 */
.Ldone: b .Ldone /* Paranoia: we should never get here */
.end