第一阶段start.S
首先我们可以在u-boot.lds中看到ENTRY(_start),即指定了入口_start,_start也就是整个start.S的最开始;
1.reset
在arch\arm\cpu\armv8\hi3559av100中的start.S
reset:/**CouldbeEL3/EL2/EL1,InitialState:*LittleEndian,MMUDisabled,i/dCacheDisabled*/adrx0,vectorsswitch_elx1,3f,2f,1f3:msrvbar_el3,x0mrsx0,scr_el3orrx0,x0,#0xf/*SCR_EL3.NS|IRQ|FIQ|EA*/msrscr_el3,x0msrcptr_el3,xzr/*EnableFP/SIMD*/#ifdefCOUNTER_FREQUENCYldrx0,=COUNTER_FREQUENCYmsrcntfrq_el0,x0/*InitializeCNTFRQ*/#endifb0f2:msrvbar_el2,x0movx0,#0x33ffmsrcptr_el2,x0/*EnableFP/SIMD*/b0f1:msrvbar_el1,x0movx0,#3<<20msrcpacr_el1,x0/*EnableFP/SIMD*/0:/**Cache/BPB/TLBInvalidate*i-cacheisinvalidatedbeforeenabledinicache_enable()*tlbisinvalidatedbeforemmuisenabledindcache_enable()*d-cacheisinvalidatedbeforeenabledindcache_enable()*//**readsystemregisterREG_SC_GEN2*checkifzijuflag*/ldrx0,=SYS_CTRL_REG_BASEldrw1,[x0,#REG_SC_GEN2]ldrw2,=0x7a696a75/*magicfor"ziju"*/cmpw1,w2bnenormal_start_flowmovx1,sp/*savesp*/strw1,[x0,#REG_SC_GEN2]/*clearzijuflag*/
adrx0,vectors,其中的vectors代表了异常向量表
主要做了如下事情:
1)resetSCTRL寄存器
具体可参考reset_sctrl函数,由CONFIG_SYS_RESET_SCTRL控制,一般不需要打开。该配置项的解释如下:
ResettheSCTRLregisterattheverybeginningofexecutiontoavoidinterferencefromstalemappingssetupbyearlyfirmware/loaders/etc.
http://lists.denx.de/pipermail/u-boot/2015-April/211147.html
2)根据当前的EL级别,配置中断向量、MMU、Endian、i/dCache等。
3)配置ARM的勘误表
具体可参考apply_core_errata函数,由CONFIG_ARM_ERRATA_XXX控制,在项目的初期,可以不打开,后续根据实际情况打开)。
2.normal_start_flow流程
这里是正常启动流程
normal_start_flow:/*setstackforCcode*/ldrx0,=(CONFIG_SYS_INIT_SP_ADDR)bicsp,x0,#0xf/*16-bytealignmentforABIcompliance*/bluart_early_initadrx0,Str_SystemSartupbluart_early_putsldrx0,=0x1202008cldrw0,[x0]bluart_early_put_hex/*enableI-Cache*/blicache_enable
- 设置代码的堆栈
- 跳转到uart_early_init
因为uart_early_init是全局的伪汇编指令(在uart.S中定义),所以在start.S中也可以使用到
- 声明一个字符串Str_SystemSartup
- 使能icache
因为bnenormal_start_flow是不跳转回来的,所以会继续向下执行
3.running_addr_check流程
判断是否进入not_ddr_init中,不需要DDR初始化,直接copy到DDR中
check_boot_mode:ldrx0,=SYS_CTRL_REG_BASEldrw0,[x0,#REG_SYSSTAT]lsrw6,w0,#4andw6,w6,#0x3cmpw6,#BOOT_FROM_EMMC//判断是不是EMMC启动bneufs_boot//如果不是,则进入ufs_boot
4.ziju_flow流程
自举模式从这里我可以推断出,芯片的启动分为两种,一种是自举模式也就是本地的spiflash或nand或emmc等启动,另一种就是pcie启动模式。不同启动模式对应不同的启动流程。但不同启动模式代码是相互交织的,需要分清楚!
- 初始化PLL和DDRC控制器和管脚复用情况。
/*initPLL/DDRC/pinmux/...*/ldrr0,_blank_zone_startldrr1,_TEXT_BASEsubr0,r0,r1ldrr1,=RAM_START_ADRSaddr0,r0,r1movr1,#0x0/*flags:0->normal1->pm*/blinit_registers/*initPLL/DDRC/...*/
blinit_registers这个函数是初始化一些寄存器,这些寄存器分了很多,包括中断、网络、哈希功能形式的寄存器,初始化的意思就是给一个值,但这值一般没什么意义,具体的寄存器,后面会再进行配置!
- start_ddr_training
/*DDRtraining:DR布线,完全按等长约束就没有ddrtraining的说法。
当布线去掉等长约束或放宽约束条件,就要做ddrtraining,以保证时序的完整性,使信号的建立&保持时间窗口一致。ddrtraining是调整Addr/Cmd信号对CLK,DQ信号对DQS的延时。由于没做等长约束,信号有长,有短,就会导致信号有快,慢之差(信号在1000mil走线耗时约160~180ps,相对FR-4的板材),ddrtraining就是找到一套参数,使信号的建立与保持时间充足。并保存且写到配置中。*/
在arch\arm\cpu\armv8\hi3559av100\lowlevel_init_v300.c中的start_ddr_training()函数中
- pcie_slave_boot
5.jump_to_ddr
自举模式省略了一些PCIE判断的情况的解释,我也没怎么看懂
jump_to_ddr:adrx0,_start_armbootldrx30,[x0]ret
开始进入跳转到C语言阶段
总结
- 关cache,关mmu,SVC模式
- 检测是不是自举模式还是pcie启动,也包括是冷启动还是热启动
- 串口初始化
- DDR初始化和DDRtraining
- 正常启动时,会检测启动方式,对代码进行相应的拷贝,重定位
- 设置堆栈
- 清bss段
- 跳转到第二阶段,即C语言阶段