根据Board/EmbedSky/u-boot.lds这个链接脚本文件,可知:
———————————————————————————————————
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/EmbedSky/boot_init.o (.text)
board/EmbedSky/lowlevel_init.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
————————————————————————————————————
可知U-Boot首先编译cpu/arm920t/start.S这个汇编源文件,被放在程序的最前面。
这个汇编源文件依次完成了如下硬件初始化设置:(下面开始分析start.S源代码)
————————————————————————————————————
.globl _start_start: b reset
1、将CPU的工作模式设置为管理模式(SVC);
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
2、关闭看门狗(WATCHDG);
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
3、屏蔽所有中断
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# elif defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
4、设置FCLK、HCLK、PCLK的比例(这里实际上是没有设置的,后面会有C函数来完成);
#if 0
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 || CONFIG_S3C2440 */
接着一个宏定义判断
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
在根目录下的 include/configs/EmbedSky.h文件里确实没有宏定义CONFIG_SKIP_LOWLEVEL_INIT,那么就跳入到cpu_init_crit这个函数里面,这个函数在start.S的第265行开始到292行结束。
5、cpu_init_crit这个函数关闭CACHES、MMU
MCR MRC C7 C8这些属于ARM架构的协指令,在《ARM体系架构与编程》,杜春雷著,这本书写的很详细,具体语法我就不多说了,有兴趣的去看可看。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
我们注意到,bl lowlevel_init这句,这个函数在board/EmbedSky/lowlevel_init.S这个汇编源代码里面
6、为加载bootloader的第二阶段代码准备RAM空间, 下面这段代码就是初始化内存芯片,使SDRAM可以使用。。
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE //TEXT_BASE = 0x33D00000
sub r0, r0, r1
/*这三句,完成了地址变换,因为这个时候内存中还没有数据,不能使用.lds文件里面确定的地址来读取数据。*/
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
/* SMRDATA表示这13个寄存器的值存放的开始地址(连续地址),处于内存中。 */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word .........
————————————————————————————————————
7、设置栈,为第二阶段的C函数做好准备。
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
8、时钟初始化
bl clock_init
其实上面的时钟初始化,在汇编源文件里是无效的,因为使用了#if 0 ,此时这段区域里的代码都无效,所以这里要用C函数初始化时钟,正好响应了第七步初始化栈。clock_init这个函数究竟在哪里呢,在start.S里面是找不到它的函数的,我们回头看看u-boot.lds看到, board/EmbedSky/boot_init.o (.text),正好,就是在boot_init.c 文件里面。
void clock_init(void)
{
S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000;
/* FCLK:HCLK:PCLK = ?:?:? */
#if CONFIG_133MHZ_SDRAM
clk_power->CLKDIVN = S3C2440_CLKDIV136; //HJ 1:3:6
#else
clk_power->CLKDIVN = S3C2440_CLKDIV; //HJ 1:4:8
#endif
/* change to asynchronous bus mod */
__asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* read ctrl register */
"orr r1, r1, #0xc0000000\n" /* Asynchronous */
"mcr p15, 0, r1, c1, c0, 0\n" /* write ctrl register */
:::"r1"
);
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure UPLL */
clk_power->UPLLCON = S3C2440_UPLL_48MHZ; //fin=12.000MHz
// clk_power->UPLLCON = S3C2440_UPLL_48MHZ_Fin16MHz; //fin=16.934MHz
/* some delay between MPLL and UPLL */
delay (4000);
/* configure MPLL */
clk_power->MPLLCON = S3C2440_MPLL_400MHZ; //fin=12.000MHz
// clk_power->MPLLCON = S3C2440_MPLL_405MHZ; //HJ 405MHz
// clk_power->MPLLCON = S3C2440_MPLL_440MHZ; //HJ 440MHz
// clk_power->MPLLCON = S3C2440_MPLL_480MHZ; //HJ 480MHz
// clk_power->MPLLCON = S3C2440_MPLL_399MHz; //fin=16.934MHz
/* some delay between MPLL and UPLL */
delay (8000);
}
9、复制bootloader的第二阶段代码到RAM空间去。
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0:当前代码的位置 */
ldr r1, _TEXT_BASE /* r1:代码段的链接地址 */
cmp r0, r1 /* 测试现在是在Flash中还是在RAM 中 */
beq clear_bss /*如果已经在RAM中就执行清除bss函数*/
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */ //r1=_TEXT_BASE 是目标地址
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
———————————————————————————————————
bl CopyCode2Ram 在board/EmbedSky/boot_init.c里面,这个函数实现将启动代码拷贝到内存中。
———————————————————————————————————
int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
unsigned int *pdwDest;
unsigned int *pdwSrc;
int i;
if (bBootFrmNORFlash())
{
pdwDest = (unsigned int *)buf;
pdwSrc = (unsigned int *)start_addr;
/* 从 NOR Flash启动 */
for (i = 0; i < size / 4; i++)
{
pdwDest[i] = pdwSrc[i];
}
return 0;
}
else
{
/* 初始化NAND Flash */
nand_init_ll();
/* 从 NAND Flash启动 */
if (NF_ReadID() == 0x76 )
nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
else
nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
return 0;
}
}
—————————————————————————————————————
10、清除bss段(初始值为0、无初始化值的全局变量,静态变量放在BSS段)
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
第一阶段的最后一步:C函数的运行环境一切准备好了,它将跳到第二阶段的入口点:
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
/* start_armboot是第二阶段的第一个C函数,在/lib_arm/board.c中定义 */
好了,U-Boot第一阶段代码就分析到这里,里面的语法大家慢慢体会,有不懂的可以互相交流一下。
祝愿大家学习愉快。^_^