时间:2018.4.5 作者:Tom 工作:HWE 说明:如需转载,请注明出处。
/*
*************************************************************************
* Startup Code (reset vector)
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
TEXT_BASE = 0xc3e00000
1.汇编是"绝对引用",即引用的就是地址
在start.s中
_TEXT_BASE:
.word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我设置为0xc3e00000 */
1)第100行这个TEXT_BASE就是上个课程中分析Makefile时讲到的那个配置阶段的TEXT_BASE,其实就是我们链接时指定的uboot的链接地址。(值就是0xc3e00000)
2)源代码中和配置Makefile中很多变量是可以互相运送的。简单来说有些符号的值可以从Makefile中传递到源代码中。
3) 此处和上面的类似,_TEXT_BASE是一个标号地址,此地址中是一个word类型的变量,变量名是TEXT_BASE,此值见名知意,是text的base,即代码的基地址,可以在Makefile中找到其定义:TEXT_BASE = 0x33D00000
/*
* Below variable is very important because we use MMU in U-Boot.
* Without it, we cannot run code correctly before MMU is ON.
* by scsuh.
*/
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE
X210_sd.h 配置文件中有定义:
结果就是CFG_PHY_UBOOT_BASE 0x33e00000 uboot在DDR中的物理地址,起始地址。
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
__bss_start定义在和开发板相关的u-boot.lds中,_bss_start保存的是__bss_start标号所在的地址。
.globl _bss_end
_bss_end:
.word _end
同上,这样赋值是因为代码所在地址非编译时的地址,直接取得该标号对应地址。
关于_bss_start和_bss_end都只是两个标号,对应着此处的地址。
而两个地址里面分别存放的值是__bss_start和_end,返两个的值,根据注释所说,是定义在开发板相关的链接脚本里面的,我们此处的开发板相关的链接脚本是:
C:\board\samsung\x210
其中可以找到__bss_start和_end的定义:
__bss_start = .;
.bss : { *(.bss) }
_end = .;
而关于_bss_start和_bss_end定义为.glogl全局变量,是因为uboot的其他源码中要用到返两个变量,详情请自己去搜索源码。
#if defined(CONFIG_USE_IRQ)
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
堆栈指针开始的地方,但目前我们初始化,还不知道,随便放了一个badcode
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
同上,IRQ_STACK_START和FIQ_STACK_START,也是在cpu_init中用到了。不过此处,是另有当定义了宏CONFIG_USE_IRQ的时候,才用到返两个变量,其含义也很明显,另有用到了中断IRQ,才会用到中断的堆栈,才有中断堆栈的起始地址。快速中断FIQ同理。在cpu_init中,根据我们的一些定义,比如堆栈大小等等,去修改了IRQ_STACK_START ,FIQ_STACK_START ,FREE_RAM_END和FREE_RAM_SIZE的值。至于为何这么修改,后面遇到的时候会具体再解释。
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
1)bl disable_l2cache // 禁止L2 cache
2)bl set_l2cache_auxctrl_cycle // l2 cache相关初始化
3)bl enable_l2cache // 使能l2 cache
4)刷新L1 cache的icache和dcache。
5)关闭MMU
总结:上面这5步都是和CPU的cache和mmu有关的,不用去细看,大概知道即可。
cpu_init_crit:
#ifndef CONFIG_EVT1
#if 0
bl v7_flush_dcache_all
不用看
#else
bl disable_l2cache
ARM 微处理器可支持多达 16 个协处理器,用亍各种协处理操作,在程序执行的过程中,每个协处理器叧执行针对自身的协处理指令,忽略 ARM 处理器和其他协处理器的指令。 ARM 的协处理器指令主要用亍 ARM 处理器初始化 ARM 协处理器的数据处理操作,以及在ARM 处理器的寄存器和协处理器的寄存器之间传送数据,和在 ARM 协处理器的寄存器和存储器之间传送数据。 ARM 协处理器指令包括以下 5 条:
— CDP 协处理器数操作指令
— LDC 协处理器数据加载指令
— STC 协处理器数据存储指令
— MCR ARM 处理器寄存器到协处理器寄存器的数据传送指令
— MRC 协处理器寄存器到ARM 处理器寄存器的数据传送指令
CP15系统控制协处理器
CP15 —系统控制协处理器 (the system control coprocessor)他通过协处理器指令MCR和MRC提供具体的寄存器来配置和控制caches、 MMU、保护系统、配置时钟模式(在bootloader时钟初始化用到)CP15的寄存器叧能被MRC和MCR(Move to Coprocessor from ARM Register )指令访问"。
CP15有很多个寄存器,分别叨做寄存器0(Register 0),到寄存器15(Register 15),每个寄存器分别控制丌同的功能,而且有的是只读,有的是只写,有的是可读写。而且这些寄存器的含义,随着版本ARM内核版本变化而不断扩展,详情请参考返个网址:
http://www.heyrick.co.uk/assembler/coprocmnd.html,https://blog.youkuaiyun.com/xie0812/article/details/52628006,
其中,根据我们此处关心的内容,摘录部分内容如下:
用于系统存储管理的协处理器CP15
MCR{cond} coproc,opcode1,Rd,CRn,CRm,opcode2
MRC {cond} coproc,opcode1,Rd,CRn,CRm,opcode2
coproc 指令操作的协处理器名.标准名为pn,n,为0~15
opcode1 协处理器的特定操作码. 对于CP15寄存器来说,opcode1永远为0,不为0时,操作结果不可预知
<Rd>作为源寄存器的ARM寄存器,其值将被传送到协处理器寄存器中。
<CRn>作为目标寄存器的协处理器寄存器,其编号可能是C0,C1,…,C15。
<CRm>和<opcode_2>两者组合决定对协处理器寄存器迚行所需要的操作,如果没有指定,则将为<CRm>为C0,opcode_2为0"
在基于ARM的嵌入式系统中,存储系统通常是通过系统控制协处理器CP15完成的。CP15可以包含16个32位的寄存器,其编号为0-15。实际上对于某些编号的寄存器可能对应有多个物理寄存器。在指令中指定特定的标志位来区分这些物理寄存器。有些类似于ARM寄存器中,处于不同的处理器模式时,ARM某些寄存器可能不同。
.global disable_l2cache
1)bl disable_l2cache // 禁止L2 cache
disable_l2cache:
mrc p15, 0, r0, c1, c0, 1
rd为r0=0
CRn为C1
CRm为C0
opcode_2为1,即#将主标示符寄存器c1中的内容读到AMR寄存器R0中
bic r0, r0, #(1<<1)
将r0中第二位置0;
mcr p15, 0, r0, c1, c0, 1
将r0写入C1
mov pc, lr
mov r0, #0x0 @
mov r1, #0x0 @ i
mov r3, #0x0
mov r4, #0x0
对寄存器进行清零。
lp1:
mov r2, #0x0 @ j
lp2:
mov r3, r1, LSL #29 @ r3 = r1(i) <<29
mov r4, r2, LSL #6 @ r4 = r2(j) <<6
orr r4, r4, #0x2 @ r3 = (i<<29)|(j<<6)|(1<<1)
orr r3, r3, r4
mov r0, r3 @ r0 = r3
bl CoInvalidateDCacheIndex
add r2, #0x1 @ r2(j)++
cmp r2, #1024 @ r2 < 1024
bne lp2 @ jump to lp2
add r1, #0x1 @ r1(i)++
cmp r1, #8 @ r1(i) < 8
bne lp1 @ jump to lp1
bl set_l2cache_auxctrl
2)bl set_l2cache_auxctrl_cycle // l2 cache相关初始化
bl enable_l2cache
3)bl enable_l2cache // 使能l2 cache
#endif
#endif
bl disable_l2cache
bl set_l2cache_auxctrl_cycle
bl enable_l2cache
4)刷新L1 cache的icache和dcache。
/*
* Invalidate L1 I/D
因为此时CPU还不能区分来的数据是指令还是数据,代码段内容会被修改然后加载到数据缓存,此时代码缓存中的数据和RAM中的不一致。所以清掉重新来过。
*/
mov r0, #0 @ set up for MCR r0清零
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs r0放在C8 C7中,这个TLB是虚拟内存管理,后面再说、
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache 使之无效
/*
* disable MMU stuff and caches
5)关闭MMU
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
/* Read booting information */
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
/* Read booting information */
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
读取启动信息
#ifdef CONFIG_VOGUES
/* PS_HOLD(GPH0_0) set to output high */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x00000001
str r1, [r0, #GPH0CON_OFFSET]
ldr r1, =0x5500
str r1, [r0, #GPH0PUD_OFFSET]
ldr r1, =0x01
str r1, [r0, #GPH0DAT_OFFSET]
#endif
这个是上电锁存。
/* NAND BOOT */
cmp r2, #0x0 @ 512B 4-cycle
moveq r3, #BOOT_NAND
cmp r2, #0x2 @ 2KB 5-cycle
moveq r3, #BOOT_NAND
cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
moveq r3, #BOOT_NAND
cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC
moveq r3, #BOOT_NAND
cmp r2, #0x8 @ OneNAND Mux
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0xc
moveq r3, #BOOT_MMCSD
/* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR
/* Uart BOOTONG failed */
cmp r2, #(0x1<<4)
moveq r3, #BOOT_SEC_DEV
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET] 将启动标识码写入INF_REG3中
1)从哪里启动是由SoC的OM5:OM0这6个引脚的高低电平决定的。
2)实际上在210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。
3)我们代码中可以通过读取这个寄存器的值然后判断其值来确定当前选中的启动介质是Nand还是SD还是其他的。
4)start.S的225-227行执行完后,在r2寄存器中存储了一个数字,这个数字等于某个特定值时就表示SD启动,等于另一个特定值时表示从Nand启动••••
5)260行中给r3中赋值#BOOT_MMCSD(0x03),这个在SD启动时实际会被执行,因此执行完这一段代码后r3中存储了0x03,以后备用。
/*
* Go setup Memory and board specific bits prior to relocation. 15.重定位前初始化存储器和板特殊位
*/
ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
sub sp, sp, #12 /* set stack */
mov fp, #0
1)284-286行第一次设置栈。这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。
2)在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。
bl lowlevel_init /* go setup pll,mux,memory */ 17.调用lowlevel_init函数初始化pll memory等与板子相关的内容 函数位于board目录下