第一階段 start.S 首先我們可以在u boot.lds中看到 ,即指定了入口 ,`_start start.S`的最開始; 1. reset 在 中的 normal_start_flow: / set stack for C code / ldr x0, =(CONFIG_SYS_INIT_S ...
第一階段 start.S
首先我們可以在u-boot.lds中看到ENTRY(_start)
,即指定了入口_start
,_start
也就是整個start.S
的最開始;
1. reset
在arch\arm\cpu\armv8\hi3559av100
中的start.S
註意x30在ARMV8中代表lr寄存器
reset:
/*
* Could be EL3/EL2/EL1, Initial State:
* Little Endian, MMU Disabled, i/dCache Disabled
*/
adr x0, vectors
switch_el x1, 3f, 2f, 1f
3: msr vbar_el3, x0
mrs x0, scr_el3
orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */
msr scr_el3, x0
msr cptr_el3, xzr /* Enable FP/SIMD */
#ifdef COUNTER_FREQUENCY
ldr x0, =COUNTER_FREQUENCY
msr cntfrq_el0, x0 /* Initialize CNTFRQ */
#endif
b 0f
2: msr vbar_el2, x0
mov x0, #0x33ff
msr cptr_el2, x0 /* Enable FP/SIMD */
b 0f
1: msr vbar_el1, x0
mov x0, #3 << 20
msr cpacr_el1, x0 /* Enable FP/SIMD */
0:
/*
* Cache/BPB/TLB Invalidate
* i-cache is invalidated before enabled in icache_enable()
* tlb is invalidated before mmu is enabled in dcache_enable()
* d-cache is invalidated before enabled in dcache_enable()
*/
/*
* read system register REG_SC_GEN2
* check if ziju flag
*/
ldr x0, =SYS_CTRL_REG_BASE
ldr w1, [x0, #REG_SC_GEN2]
ldr w2, =0x7a696a75 /* magic for "ziju" */
cmp w1, w2
bne normal_start_flow
mov x1, sp /* save sp */
str w1, [x0, #REG_SC_GEN2] /* clear ziju flag */
adr x0, vectors
,其中的vectors代表了異常向量表
主要做瞭如下事情:
1)reset SCTRL寄存器
具體可參考reset_sctrl函數,由CONFIG_SYS_RESET_SCTRL控制,一般不需要打開。該配置項的解釋如下:
Reset the SCTRL register at the very beginning of execution to avoid interference from stale mappings set up by early firmware/loaders/etc.
http://lists.denx.de/pipermail/u-boot/2015-April/211147.html
2)根據當前的EL級別,配置中斷向量、MMU、Endian、i/d Cache等。
3)配置ARM的勘誤表
具體可參考apply_core_errata函數,由CONFIG_ARM_ERRATA_XXX控制,在項目的初期,可以不打開,後續根據實際情況打開)。
2. normal_start_flow流程
這裡是正常啟動流程
normal_start_flow:
/* set stack for C code */
ldr x0, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */
bl uart_early_init
adr x0, Str_SystemSartup
bl uart_early_puts
ldr x0, =0x1202008c
ldr w0, [x0]
bl uart_early_put_hex
/* enable I-Cache */
bl icache_enable
1)設置代碼的堆棧
2.)跳轉到uart_early_init
因為uart_early_init是全局的偽彙編指令(在uart.S中定義),所以在start.S中也可以使用到
3)聲明一個字元串Str_SystemSartup
4)使能icache
因為bne normal_start_flow
是不跳轉回來的,所以會繼續向下執行
3. running_addr_check流程
判斷是否進入not_ddr_init中,不需要DDR初始化,直接copy到DDR中
check_boot_mode:
ldr x0, =SYS_CTRL_REG_BASE
ldr w0, [x0, #REG_SYSSTAT]
lsr w6, w0, #4
and w6, w6, #0x3
cmp w6, #BOOT_FROM_EMMC //判斷是不是EMMC啟動
bne ufs_boot //如果不是,則進入ufs_boot
4. ziju_flow流程
自舉模式從這裡我可以推斷出,晶元的啟動分為兩種,一種是自舉模式也就是本地的spiflash或nand或emmc等啟動,另一種就是pcie啟動模式。不同啟動模式對應不同的啟動流程。但不同啟動模式代碼是相互交織的,需要分清楚!
1) 初始化PLL和DDRC控制器和管腳復用情況。
/* init PLL/DDRC/pin mux/... */
ldr r0, _blank_zone_start
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =RAM_START_ADRS
add r0, r0, r1
mov r1, #0x0 /* flags: 0->normal 1->pm */
bl init_registers /* init PLL/DDRC/... */
bl init_registers
這個函數是初始化一些寄存器,這些寄存器分了很多,包括中斷、網路、哈希功能形式的寄存器,初始化的意思就是給一個值,但這值一般沒什麼意義,具體的寄存器,後面會再進行配置!
2) start_ddr_training
/* DDR training:DR佈線,完全按等長約束就沒有ddr training的說法。
當佈線去掉等長約束或放寬約束條件,就要做ddr training,以保證時序的完整性,使信號的建立&保持時間視窗一致。ddr training是調整Addr/Cmd信號對CLK,DQ信號對DQS的延時。由於沒做等長約束,信號有長,有短,就會導致信號有快,慢之差(信號在1000mil走線耗時約160~180ps,相對FR-4的板材),ddr training就是找到一套參數,使信號的建立與保持時間充足。並保存且寫到配置中。*/
3) pcie_slave_boot
5. jump_to_ddr
自舉模式省略了一些PCIE判斷的情況的解釋,我也沒怎麼看懂
jump_to_ddr:
adr x0, _start_armboot
ldr x30,[x0]
ret
開始進入跳轉到C語言階段
總結
- 關cache,關mmu,SVC模式
- 檢測是不是自舉模式還是pcie啟動,也包括是冷啟動還是熱啟動
- 串口初始化
- DDR初始化和DDR training
- 正常啟動時,會檢測啟動方式,對代碼進行相應的拷貝,重定位
- 設置堆棧
- 清bss段
- 跳轉到第二階段,即C語言階段