最近一直在用赛灵思的zynq7000芯片,是一个ARM核+FPGA的soc,zynq在运行完芯片内固化的bootRom之后运行的是FSBL程序(first stage boot loader),uboot由FSBL调起,官方说法中将uboot叫做SSBL(second stage boot loader).
本文分析fsbl源码;fsbl源码由vivado工具生成,笔者的vivado版本是2019.1.
bootROM阶段
芯片上电之后,cpu从0x0000地址开始运行,此时芯片运行的是bootROM固化代码,这一段代码主要是初始化CPU和一些必要的外设->QSPI、SD卡、NORFlash.初始化这些外设是为了从这些外设中拿到BOOT.BIN(fsbl和uboot通过赛灵思的工具整合在一个文件中,这个文件叫BOOT.BIN)
假设我们使用SD卡作为启动设备,bootROM会初始化SD卡并从SD卡的第一个分区拿到BOOT.BIN,(第一个分区的文件系统只能是FAT格式)然后从BOOT.BIN的文件头中获取fsbl代码的长度,位置,将fsbl复制到256KB的OCM中运行(on chip memory),再跳转到FSBL,至此bootROM的生命周期就结束了。
fsbl阶段
fsbl的工作主要有:
1 初始化DDR,为下面的工作做准备
2 调起uboot,这是fsbl最重要且必要的任务
3 将bitstream配置到fpga,这是可有可无的任务
开始分析:
1 汇编阶段
找到asm_vectors.S文件,这是fsbl最开始运行的汇编.
可以看到如下代码:
_vector_table:
B _boot
跳转到_boot标号处,有如下代码:
cmp r1, #0
beq CheckFUSE
cmp r1, #0:
比较r1寄存器值和立即数0,如果r1等于0,那么ARM的CPSR寄存器的Z bit为1;
beq CheckFUSE:
查看CPSR寄存器的Z bit,如果为1,则跳转到CheckFUSE处;如果为0,不跳转。
现在跳转到CheckFUSE处,有如下代码:
ands r1,r1,#0x80
beq OKToRun
这一段跟上面那一段差不多,不再解释,直接跳到OKToRun:
OKToRun中有四五十行汇编,主要做了关闭MMU,关闭Dcache(CPU的数据缓存,是CPU L1 cache中的Dcache,还有一个Icache是指令缓存;Dcache与Icache合在一起构成了CPU的L1 cache,就是常说的一级缓存)
直到OKToRun的最后面有这样的一句:
b _start
跳转到_start标号处:
bl __cpu_init
跳转到__cpu_init标号处:
这里开始对CPU进行初始化,主要是为C语言运行环境做准备,SP指针指向OCM段的结尾。
直到运行这一句:
bl main
终于,我们可以打印hello world了.
2 C语言阶段
1
Status =ps7_init();
初始化 MIO PLL CLK DDR3/2
2
Xil_DCacheFlush();
Xil_DcacheDisable();
清空Dcaches关闭Dcaches;上面已经关过了这里为什么还要关?无他,稳健。
3
Status=InitPcap();
初始化Pcap,Pcap具体是什么我也不清楚,不过看后面的使用发现,应该可能大概是操作AXI总线使用的.
4
GetSiliconVersion();
获取silicon的版本
5
BootModeRegister = Xil_In32(BOOT_MODE_REG);
获取boot模式寄存器,这个寄存器的值取决于你的bootmode管脚是如何设置的。
6
根据BootModeRegister 选择从哪里拿BOOT.BIN
QSPI启动:
InitQspi();
MoveImage = QspiAccess;
Nand启动:
Status = InitNand();
MoveImage = NandAccess;
NOR启动:
InitNor();
MoveImage = NorAccess;
初始化Qspi/nand/nor, flash操作函数MoveImage 指向各自的xxxxAccess函数.
SD卡启动:
Status = InitSD("BOOT.BIN");
MoveImage = SDAccess;
挂载SD卡打开BOOT.BIN文件,flash操作函数MoveImage 指向SDAccess函数.
7
HandoffAddress = LoadBootImage();
获取application在DDR中的运行地址(链接地址),这里的application指的就是SSBL,也就是uboot
8
FsblHandoff(HandoffAddress);
fsbl交出控制权,从HandoffAddress地址运行application
到这里,uboot终于出生了,fsbl也宣告生命结束。
接下来就是uboot的事情,拿设备树到DDR,拿kernel到DDR,解压kernel,将kernel从加载地址移动到运行地址,给kernel传递参数并调起kernel....
OK我话终于讲完.