以下是bootstrap的启动过程,主要分为两个步骤:
第一阶段:汇编程序,主要负责最低层的硬件初始化
第二阶段:c程序,主要负责SDRAM初始化,Dataflash的拷贝工作,完成程序在SDRAM中运行的准备工作
第一阶段主要是在crt0_gnu.S中,和前面的链接脚本文件有很大关系()
crt0_gnu.S的汇编程序是bootstrap的第一阶段执行过程:
主要实现的目的是:
1 建立中断向量表(_exception_vectors:在此标号处)
2 设置堆栈SP(代码在内部SRAM0中运行,堆栈在内部SRAM1中运行。启动过程是cpu启动后先检测外部的dataflash,有接,则拷贝dataflash从0开始的4K字节到内部SRAM1中,开始执行)
(_init_stack:在此处)
3 主晶振使能为主时钟(设置时钟_setup_clocks: 使能主晶振_enable_mosc: 打开主晶振_switch_to_mosc: )
4 数据段拷贝 (_init_data:)
5 BSS段数据初始化(_init_bss:)
6 底层基本初始化完成,跳转到C代码的MAIN函数中执行(_branch_main:)
至此bootstrap第一阶段汇编代码的任务执行完毕。接下来执行C代码。
以下是汇编代码的详细注释:
.sectionstart 定义域中包含的段
.text 代码段开始
#include"include/part.h" 包含头文件,供编译使用
/*----------------------------------------------------------------------------
Area DeFinition 区域定义
Must be defined as function to put first inthe code as it must be mapped
at offset 0 of the flash EBI_CSR0,ie. ataddress 0 before remap.必须先定义一个函数进入第一段代码必须映射到flash(EBI_CSR0)的偏移0地址处例如地址重映射后在0地址
--------------------------------------------------------------------------*/
/*Application startup entry point */应用程序开始进入点
.globl reset 定义全局符号
.align 4 以下以4字节对齐
reset:
//Exception vectors (should be a branch to be detected as a valid code by the rom异常向量表8个
_exception_vectors:
b reset_vector /* reset */
b undef_vector /* Undefined Instruction */
b swi_vector /*Software Interrupt */
b pabt_vector /*Prefetch Abort */
b dabt_vector /* DataAbort */
.word _edata /* Size of the image for SAM-BA */
b irq_vector /* IRQ : read the AIC */
b fiq_vector /* FIQ */
undef_vector:
b undef_vector
swi_vector:
b swi_vector
pabt_vector:
b pabt_vector
dabt_vector:
b dabt_vector
rsvd_vector:
b rsvd_vector
irq_vector:
b irq_vector
fiq_vector:
b fiq_vector
reset_vector:
/*Init the stack */堆栈初始化
_init_stack:
ldr sp,=TOP_OF_MEM //TOP_OF_MEMORY=0x301000
//在Makefile文件中的# Link Address andTop_of_Memory
//LINK_ADDR=0x20 0000 内部SRAM0 的起始位置 0x20 0000 共4k
//TOP_OF_MEMORY=0x30 1000 内部SRAM1的起始位置 0x30 1000 共4K
#ifdef CFG_norFLASH //norFLASH启动时要使用的代码段,本次移植使用的是DATAFLASH
/* When running from nor,we must relocate to SRAM prior toresetting the clocks and SMC timings. */
_relocate_to_sram:
#if 0
/*relocation is slow,disable the watchdog or it will trigger */
ldr r1,=0xFFFFFD44
mov r2,#0x00008000
str r2,[r1]
#endif
mov r1,#0
ldr r3,=_stext
ldr r4,=_edata
1:
cmp r3,r4
ldrcc r2,[r1],#4
strcc r2,[r3],#4
bcc 1b
ldr pc,=_setup_clocks
#endif /* CFG_norFLASH */
_setup_clocks: 设置时钟寄存器
/*Test if main oscillator is enabled */测试主晶振是否使能
ldr r0,=AT91C_PMC_SR // (PMC) Status Register 0xFFFFFC68
ldr r1,[r0] //将状态寄存器中的值读出到R1
ldr r2,=AT91C_PMC_MOSCS //R2= (0x1 << 0)
ands r1,r1,r2 //R1=R1与R2 判断主晶振是否起振了
// r1=1 则晶振已经启动 r1=0则晶振未启动
bne _switch_to_mosc //未起振,则继续执行函数_enable_mosc执行启动主晶振
bne不等于 则跳转
/*Enable the main oscillator */使能主晶振
_enable_mosc:
ldr r0,=AT91C_PMC_MOR // (PMC) Main Oscillator Register(0xFFFFFC20)
mov r1,#(0x40 << 8)
ldr r2,=AT91C_CKGR_MOSCEN // (CKGR) Main Oscillator Enable(0x1<< 0)
orr r1,r2 // r1,=r1或 r2
str r1,[r0] //将r1的值写入寄存器 AT91C_PMC_MOR
ldr r0,=AT91C_PMC_SR // (PMC) Status Register(0xFFFFFC68)
1:
ldr r1,[r0] //寄存器的值读出到R1中
ldr r2,=AT91C_PMC_MOSCS //0x1 << 0) (PMC) MOSCStatus/Enable/disable/Mask
ands r1,r2 //R1=R1与R2
beq 1b //如果等于0,就继续循环检测,为1为止说明晶振已启动跳到下面函数处理
/*Test if MCK == SLOW CLOCK */
_switch_to_mosc: // 到此函数说明主晶振已起振
ldr r0,=AT91C_PMC_MCKR // (PMC) Master Clock Register(0xFFFFFC30)
ldr r1,=AT91C_PMC_CSS //(PMC) Programmable Clock Selection(0x3 << 0)
ldr r2,[r0] //将Master Clock Register读到R2
and r2,r2,r1 //R2=R2与R1
mov r1,#0 //R1=0
cmp r1,r2 //判断R2是否位0
/* No=> Do nothing */是0,则主时钟选择是MCK ==SLOW CLOCK
bne _init_bss //R1=R2直接初始化bss段
/*Yes => Switch to the main oscillator */打开主晶振开关
ldr r1,=AT91C_PMC_CSS_MAIN_CLK // (PMC)Main Clock is selected (0x1) R1=1
ldr r2,=AT91C_PMC_PRES_CLK // (PMC) Selectedclock(0x0 << 2)R2=0
orr r1,r2 //R1=R1或R2 R1=1
str r1,[r0] //将R1写到Master Clock Register
ldr r0,=AT91C_PMC_SR // (PMC) Status Register (0xFFFFFC68)读到R0
1:
ldr r1,[r0] //将Master Clock Register读到R1
ldr r2,=AT91C_PMC_MCKRDY//MasterClockStatus/Enable/disable/Mask(0x1<<3)
ands r1,r2 //R2=1000 R1=R1与R2
beq 1b//如果不等于,就继续循环检测,直到为1为止,跳到下面函数处理
//copythe data section in RAM at .data link address从连接地址.data开始拷贝数据段到 RAM
_init_data:
ldr r2,=_lp_data
ldmia r2,{r1,r3,r4} //r1=[ r2] r3=[ r2] +4 r4=[ r2] +8
1:
cmp r3,#4 //将r2中的数据写入到[r3]的地址的存储单元中,并新地址[r3]=[ r3] +4
bcc 1b
/*Initialize the bss segment *///初始化bss段
_init_bss:
adr r2,_lp_bss //伪指令:小范围内读取数据
ldmia r2,{r3,r4}
mov r2,#0
1:
cmp r3,r4
strcc r2,#4
bcc 1b
/*Branch on C code Main function (with interworking) *///进入C代码MAIN函数
_branch_main:
ldr r4,= main//在main函数中,拷贝完主要的运行代码
mov lr,pc //将LR寄存器的值直接写入道PC中
/*Branch to the application at the end of the bootstrap init *//在bootstrap初始化最后分支进入应用程序中执行
_go: //main函数执行完毕返回,一个地址传递到r0中,此地址是SDRAM中的地址
ldr r1,=MACH_TYPE
mov lr,pc
bx r0 //跳转到main函数执行完毕的返回地址,也就是跳转到bootstrap加载的
//程序(uboot)在SDRAM中的位置
.align
_lp_data:
.word _etext
.word _sdata
.word _edata
_lp_bss:
.word _sbss
.word _ebss
问题:
1 _init_data的数据拷贝为什么从代码段的结束位置开始到数据段的结束位置结束?????
2 汇编中bne和beq的各自跳转使用方式???
3 汇编中 ldmia,ldrcc,strcc,bcc 具体的指令说明?其中的cc是什么意思???
问题解决办法:
1:
2:汇编中bne和beq的各自跳转使
3:
LDMIA
IA: 每次传送后地址加4;
IB: 每次传送前地址加4;
DA: 每次传送后地址减4;
DB: 每次传送前地址减4;
ldmia r2,r4} //r1=[ r2] r3=[ r2] +4 r4=[ r2] +8
ldrcc r2,#4 //将r1地址的数据传送到r2中,并且新的r1=r1+4,等待下个存储单元的使用
strcc r2,#4 //将r2中的数据写入到[r3]的地址的存储单元中,并新地址[r3]=[ r3] +4
bcc //一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行
条件助记符:
BEQ 相等则跳转,
B跳转,EQ相等(,前面有CMP)
LDRCC 小于则装载内存数据到寄存器,LDR装载,CC小于
STRCC BCC类似
<助记符>{<执行条件>}{S}<Rd>,<Rn>{,第2操作数}
有S则表示影响cpsR寄存器的值
条件码助记符:
EQ 相等
NE 不相等
CS/HS 无符号数大于或等于
CC/LO 无符号数小于