下面是自己理解和查资料得出的一些解释。作为自己的笔记,里面还有些知识块不是理解非常透彻。希望指教。还有个这不是单纯的uboot ,而是一个移植的uboot,但是没看清楚,分析到后面发现有移植部分,想了下还是将错就错吧。
设置中断向量表
用globl来定义全局变量,因为这个中断变量在其他模块中也要用到。
把中断标号赋值给pc,其实真正的中断服务子程序地址在下面再次定义。
中断向量表中只有复位键不带返回的跳转,且该指令地址为0x00。中断向量表中的其他中断启动时是不会调用的,因为复位中断会跳过这些中断向量直接跳转到真正的运行代码,只有当程序运行出现中断时,才会回来查看这个中断向量表,然后查找中断服务子程序。
.globl _start
_start: b start_code
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
定义了真正的中断子程序的入口地址,word分配一个字长的地址空间。因为在启动时,全部切换到32为的ARM状态下操作,所以字长为4个字节。
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
对于这条指令很多博友都有争议,下面是我个人的理解:balignl是个伪指令,由balign指令变来的,表示的意思是在当前位置及以后的代码中以16个字节的方式对齐,不够则以0xdeadbeef填充。至于这个0xdeadbeef值,则是个单词:坏死的牛肉。就是个标志性的单词,为了方便程序员阅读代码,没具体意思。
这是下面看到移植那部分添加的:可以算下下面这条指令的地址,是0x3C。
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Startup Code (called from the ARM reset exception vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
定义代码基地址变量
在文件u-boot-2010.03\board\embedclub\smdk2440a中有定义:TEXT_BASE = 0x33F80000
_TEXT_BASE:
.word TEXT_BASE
定义代码起始地址,即是_start标号地址0x00
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
定义_bss的起始地址和结束地址,该地址在.../.../u-boot.lds文件中有定义
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
IRQ/FIQ的堆栈地址设置
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*
* the actual start code
*/
这是代码真正开始的地方,上电开始从复位中断0x00地址跳转到该标号地址执行。
start_code:
/*
* set the cpu to SVC32 mode
*/
uboot第一阶段的第二步:设置cpu工作模式为管理模式svc。具体指令不解释,步骤分析下:从cpsr状态寄存器中读取数据,然后更改工作模式位,再写入cpsr状态寄存器中,更改完毕。
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
下面两个跳转指令被注释了,查了下资料:是说在移植的时候,解开注释跳转到函数中执行,然后测试LED灯来表示移植情况的。
/*
bl coloured_LED_init
bl red_LED_on
*/
这是个状态显示,当上电启动时,LED灯会一直闪烁不停。
#ifdef CONFIG_SMDK2440_LED
bl LED_on
#endif
至于这个段代码,我自己的解释是:把_start映射到0x00地址上。不知道理解正不正确,查了些资料,没找到解释,不知道是太简单了还是这个不太重要。我想这个是要关系到硬件板子的设计。
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
首先是定义一些寄存器,用判断语句来兼容多个板子的寄存器地址不一样问题
#ifdef CONFIG_S3C24X0
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif
这是个为些特殊值得起的宏名,方便操作。我好奇的是后面的注释:Hanson ~_~ boy
#define CLK_CTL_BASE 0x4C000000 /* Hanson */
#define MDIV_405 0x7f << 12 /* Hanson */
#define PSDIV_405 0x21 /* Hanson */
#define MDIV_200 0xa1 << 12 /* Hanson */
#define PSDIV_200 0x31 /* Hanson */
uboot第一阶段第三步:关闭看门狗。
简单解释,看门狗是块定时调用复位中断的硬件模块,当程序运行一定时间后,要给看门狗设置个值,如果没有设置,则看门狗会自动调用复位中断来让系统进行复位。其作用是防止系统卡住,死循环之类的。这里关闭看门狗是为了省去喂狗操作,和启动方便。否则要不执行复杂的喂狗操作,要不就无限重启。
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
uboot第一阶段第四步:禁止所有中断
设置主中断屏蔽寄存器和子中断屏蔽寄存器全为1.屏蔽所有中断。
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
uboot第一阶段第五步:设置CPU时钟频率
按照硬件板子的规定FCLK:HCLK:PCLK的比例来分频。设置时钟频率的目的是让调整外围设备的时钟频率,使CPU能够操作外围硬件资源
#if defined(CONFIG_S3C2440)
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
uboot第一阶段第六步:设置cp15协处理器寄存器
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_405
add r2, r2, #PSDIV_405
str r2, [r1, #0x04] /* MPLLCON tekkaman */
#else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0 /*write ctrl register tekkaman*/
mov r1, #CLK_CTL_BASE /* tekkaman*/
mov r2, #MDIV_200
add r2, r2, #PSDIV_200
str r2, [r1, #0x04]
#endif
#endif /* CONFIG_S3C24X0 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
返回开发板设置些初始化值,bank的值之类的
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
uboot第一阶段第七步:拷贝代码到RAM中
检测代码所在的位置,如果是在flash(不相等)中则直接拷贝自身到RAM中,并且要判断是nor flash还是nand flash启动。如果在RAM中(相等)则直接跳转到栈设置中初始化。判断方法是代码的起始地址是否为0地址。
如果是在flash中运行则 _start的值就是0,
如果是在RAM中运行,则 _start=0x33F80000
/***************** CHECK_CODE_POSITION ******************************************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
/***************** CHECK_CODE_POSITION ******************************************/
/***************** CHECK_BOOT_FLASH ******************************************/
下面代码是涉及到移植的问题,判断是nand falsh还是nor flash启动的。前面已经提示过了,记住0x3C位置的指令是什么?就是往该内存中添加一个0Xbeadbeef值。现在是把0x4000003C地址内容置0,然后读取0x0000003C地址内容,比较是否为0.如果是0,则说明是nand flash 启动,因为nand flash把4k以后的地址映射到了0x00地址上。记得要恢复开始的0Xbeadbeef值,因为搬运代码时要检测是否相等。
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /* address of Internal SRAM 0x4000003C*/
mov r0, #0 /* r0 = 0 */
str r0, [r1]
mov r1, #0x3c /* address of men 0x0000003C*/
ldr r0, [r1]
cmp r0, #0
bne relocate
不相等则是nand flash启动,恢复被修改的值。
/* recovery */
ldr r0, =(0xdeadbeef)
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
str r0, [r1]
/***************** CHECK_BOOT_FLASH ******************************************/
/***************** NAND_BOOT *************************************************/
从nand flash启动把代码搬运到RAM中
#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000
#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable
str r2, [r1, #oNFCONT]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2:
b loop2 @ infinite loop
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
#ifdef CONFIG_S3C2410
/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq nand2
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2:
b loop2 @ infinite loop
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
/***************** NAND_BOOT *************************************************/
表示是从nor flash启动,把代码搬运到RAM中
/***************** NOR_BOOT *************************************************/
relocate: /* relocate U-Boot to RAM */
/*********** CHECK_FOR_MAGIC_NUMBER***************/
ldr r1, =(0xdeadbeef)
cmp r0, r1
bne loop3
/*********** CHECK_FOR_MAGIC_NUMBER***************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
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] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
/***************** NOR_BOOT *************************************************/
设置堆栈,为第二阶段的c程序设置运行做环境设置
/* Set up the stack
uboot第一阶段第八步:配置栈空间
*/
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_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 */
uboot第一阶段第九步:bss段清零
对bss段进行初始化:从起点循环置0到终点
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
跳转到第二阶段
ldr pc, _start_armboot