用的U-BOOT版本是1.1.2,CPU是S3C2410
U-BOOT源码在这里可以下到http://sourceforge.net/projects/u-boot/files/u-boot/
*/
#include <config.h>
#include <version.h>
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start ;声明一个全局变量,可以把它当然一个程序标号,不过在整系统中是都可以使用的,globl是一个伪指令,可以查看资料,它不进行数据操作
_start: b reset ;跳转到复位异常执行,优先级为1
ldr pc, _undefined_instruction ;该指令存放地址为0x00000004,是示定义指令异常入口,也就是说当产生未定义异常时,程序寄存器PC会自动给赋值0x00000004,学过单片机的应该明白,优先级为6
ldr pc, _software_interrupt ;软件中断入口,存放地址为0x00000008,优先级为6,因为它和未定义指令异常是互斥的,所有可以为同一优先级
ldr pc, _prefetch_abort ;指令预取终止异常,地址为0x0000000c,十六进制表示,这应该清楚吧
ldr pc, _data_abort ;数据访问终止异常,地址为0x00000010
ldr pc, _not_used ;未使用异常,地址为0x00000014,还不清楚是做什么的,清楚的请补充
ldr pc, _irq ;外中断异常,地址为0x00000018
ldr pc, _fiq ;快速中断异常
/*以上是ARM920T核的9个异常入口地址,其中 b reset不需要返回,好好想下为什么!呵呵。*/
_undefined_instruction: .word undefined_instruction ;这条指令相当用undefined_instruction对_undefined_instruction赋值,.word也是一个伪指令,它说明赋的值为字!这些值应该都在链接时生成
_software_interrupt: .word software_interrupt ;说明下,在ARM中,8位的为字节,十六位的为半字,三十二位的字,下面几条指令一样,不多说
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef ;没搞明白,不是浪费空间么?还是十六的倍数!可以查看http://haoyeren.blog.sohu.com/84511571.html
/*
*************************************************************************
*
* Startup Code (reset vector) 这里介绍了整个start.S代码的作用:
* 初始化内存,移动代码到内存,设置堆栈等
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE ;TEXT_BASE对_TEXT_BASE赋值,TEXT_BASE在board/smdk2410/config.mk文件中定义,值为0x33F80000,如果是64M内存的话,它就是内存的最后一个地址,有点意思
.globl _armboot_start ;声明全局变量
_armboot_start:
.word _start ;_start对_armboot_start赋值,_start的值由链接文件生成,也是程序的入口地址
.globl _bss_start ;声明一个代码段的开始标号
_bss_start:
.word __bss_start ;并用__bss_start对它赋值,这个值应该是在链接时候生成
.globl _bss_end ;声明一个代码段的结束标号
_bss_end:
.word _end ;并用_end对它赋值,也是在链接的时候生成
#ifdef CONFIG_USE_IRQ ;如果定义了CONFIG_USE_IRQ名,可能是在IRQ(包括FIQ)中断保护时对其有定义,先不理,回头再看它
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START ;声明一个外中断处理程序规堆栈的开始名
IRQ_STACK_START:
.word 0x0badc0de ;对刚声明的IRQ_STACK_START进行赋值,值为0x0badc0de,不知道为什么设这个值,它影射不到SDRAM的任何地址吧,带着疑问继续
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START ;声明一个快中断程序处理堆栈的开始名
FIQ_STACK_START:
.word 0x0badc0de ;对刚声明的FIQ_STACK_START赋值为0x0badc0de,它跟外中断的堆栈始地址相同
#endif ;配合#ifdef一起用
/*
* the actual reset code实际的复位代码 ;好了,到这里开始执行CPU初始化工作了,以下用尽量符号表示其指令含义
*/
reset:
/*
* set the cpu to SVC32 mode 设置CPU工作在32位管理模式
*/
mrs r0,cpsr ;r0<--cpsr,把cpsr送到r0中 ;
bic r0,r0,#0x1f ;对r0中的01234位清0,把0x1f转换为二进制为0b00011111,明白为什么为012345位了吧,bic是一个位清0指令
orr r0,r0,#0xd3 ;orr是或逻辑运算指令,它的意思是r0 | 0xd3,再把运算结果放到r0中,0xd3转换为二进制0b11010011,这时r0的8位就应该是这个0b11010011了
msr cpsr,r0 ;再把r0的值送到程序状态寄存器cpsr中,些时cpsr中的值为0b11010011,用这些字符(I F T M4 M3 M2 M1 M0)表示cpsr的低8位,I=1表示禁止IRQ中断,F=1表示禁止FIQ中断,T=0,表示以下的指令是ARM指令集开始执行
;也就是32位指令集,(还有一个THUMB是16位指令集)低5位的值使CPU工作在管理模式,这个模式下可以处理软中断SWI。
/* turn off the watchdog */ ;关闭看门狗,为什么关呢,这时CPU还没有初始化,没有喂狗信号,时间一长CPU就会自动复位了!
/*下面这段代码主要是关闭看门狗,屏蔽所有中断,设置CPU时钟*/
#if defined(CONFIG_S3C2400) ;如果定义了CONFIG_S3C2400,那么执行下面三行语句,s3c2410这段没有定义,下面三行不说了
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410) ;如果定义了CONFIG_S3C2410执行下面四行语句
# define pWTCON 0x53000000 ;宏定义,地球人都知道了 ~_~ ,0x53000000是看门狗控制寄存器,以后再用它的时候是不是很方便,查下DATASHEET就知道了
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ ;0x4A000008是中断屏蔽寄存器
# define INTSUBMSK 0x4A00001C ;0x4A00001C是子中断屏蔽寄存器
# define CLKDIVN 0x4C000014 /* clock divisor register */时钟分频寄存器,用于控制FCLK,HCLK,PCLK之间的时钟关系
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) ;我们定义了2410的的
ldr r0, =pWTCON ;把看门狗寄存器的地址加载到r0
mov r1, #0x0 ;r1 <-- #0
str r1, [r0] ;对看门狗寄存器清0
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff ;r1 <-- #0xffffffff
ldr r0, =INTMSK ;把中断屏蔽寄存器地址赋到r0
str r1, [r0] ;对中断屏蔽寄存器位全置1,屏蔽所有中断
# if defined(CONFIG_S3C2410) ;到这里是不是看到了两种关于#define的用法,对比刚才的,不同的时候不同的妙用!
ldr r1, =0x3ff ;r1 <-- 0x3ff,等号表示后面的是立即数,可以直接用
ldr r0, =INTSUBMSK ;这里引用了宏定义,相当于r0 <--0x4A000008
str r1, [r0] ;把0x3ff存到0x4A000008地址里,也是中断寄存器进行设置,屏蔽部分中断,要根据实际配置
# endif
/* FCLK:HCLKCLK = 1:2:4 */ ;设置FCLK、HCLK、PCLK的比例关系
/* default FCLK is 120 MHz ! */ ;看DATASHEET也没看明白为什么初始FCLK为120M,晕倒,跟DATASHEET不一样
ldr r0, =CLKDIVN ;把时钟频控制寄存器地址赋给r0
mov r1, #3 ;r1 <-- 3
str r1, [r0] ;把3赋给时钟控制寄存器,这时FCLK:HCLK:PCLK=1:2:4,查看DATASHEET可以得到,FCLK指CPU内核的工作频率;HCLK指一些高速设备频率,比如USB,LCD,网口等;PCLK指低速的设备如IDE,SPI等
#endif
/* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifdef CONFIG_INIT_CRITICAL ;到这里忽然意识到,包括上面类似的指令,应该都在UBOOT的Makefile找到定义,也就是判断的这些定义,先不管
bl cpu_init_crit ;要初始化CPU(包括内存),同时把当前r15(PC寄存器)保存到r14(程序链接寄存器),保护现场,上面那个应该条件成立吧,不然后CPU怎么工作,好现在我们看它到哪里去了,到这里要跳转了,跟着程序走哟
#endif ;执行bl的时候系统会自动保存,舒服吧
relocate: /* relocate U-Boot to RAM */ ;这下面这段程序就要移代码了,因为SRAM已经初始化(我们假设的,回头再看那),2410片内的4KSRAM用得应该差不多了吧
adr r0, _start /* r0 <- current position of code */;adr伪指令,把_start的地址赋给r0,_start还记得吧,链接生成的地址
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */;查看上面定义过的_TEXT_BASE,ldr是把地址为0x33F80000的数据读到r1,测试下看内在初始化OK了没,ldr指令一般用于从存储器中读取数据
cmp r0, r1 /* don't relocate during debug */;r0与r1中的数据比较,实际上是r0、r1进行减运算,大、小、等于来更改CPSR的标志位(第31位,也是最高位),1为正数,0为负数,r0>r1标志位清0,些时后面GT后缀的指令可以执行
beq stack_setup ;如果r0=r1,则跳转到stact_setup(用于堆栈设置)
/*下面4行代码用于计算要移动的代码的大小,还不太清楚是怎么计算出来的,到连接的时候再回头看*/
ldr r2, _armboot_start ;把地址给r2
ldr r3, _bss_start ;把代码的起始地址给r3
sub r2, r3, r2 /* r2 <- size of armboot */ ;r2 <-- r3 - r2
add r2, r0, r2 /* r2 <- source end address */ ;r2 <-- r0 + r2
/*下面4行用于移动代码*/
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */ ;以r0的值为地址递增的加载到r3-r10,并把最的的基址给r0
stmia r1!, {r3-r10} /* copy to target address [r1] */ ;把r3-r10的数据送到以r1的值为地址递增的地址空间里,并把最后的基址给r1
cmp r0, r2 /* until source end addreee [r2] */ ;判断有没有移动完成
ble copy_loop ;r0若小于等于r2,则跳转到copy_loop
/* Set up the stack ;堆栈初始化,现在还不知道位置在哪里,链接的时候再回头看 */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ ;把内存地址为0x33F80000(最后一个地址)中的数据加载到r0,可以猜测u-boot可能把堆栈设置在了内存的最高地址
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ ;r0 <-- r0-#CFG_MALLOC_LEN,CFG_MALLOC_LEN在include/configs/smdk2410.h文件中有定义
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ ;r0 <-- r0-#CFG_GBL_DATA_SIZE,CFG_GBL_DATA_SIZE值为128,在include/configs/smdk2410.h文件中有定义,这两条指令可能是用于设置堆栈空间大小和单位
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) ;r0 <-- r0-#CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ,不知道这个在哪里定义,在根目录下搜索也没找到,可能由编译器产生吧
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */ ;sp <-- r0-#12,给堆栈指针赋值
clear_bss:
ldr r0, _bss_start /* find start of bss segment */ ;把_bbs_start地址存到r0
ldr r1, _bss_end /* stop here */ ;把_bbs_end地址存到r1
mov r2, #0x00000000 /* clear */ ;r2 <--#0x00000000
clbss_l:str r2, [r0] /* clear loop... */ ;0x00000000 --> _bss_start
add r0, r0, #4 ;r0 <-- r0 + #4
cmp r0, r1 ;r0与r1比较,实际上是进行减运算,但不改其值,只更新标志位
bne clbss_l ;r0,r1不相等跳转,只到代码段尾,这样所有代码地址就被清0了,难道是为BOOTLOAD 2阶段做准备?
#if 0 ;为假,先不理,继续走
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
/* FCLK:HCLKCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
ldr pc, _start_armboot ;pc <-- _start_armboot
_start_armboot: .word start_armboot ;start_armboot是一个C函数指针,也就是阶段2的入口程序地址。到这里阶段1完成http://www.diybl.com/course/6_sy ... /200876/130696.html
/*
*************************************************************************
*
* CPU_init_critical registers 下面段程序主要设置关键的寄存器,和内存的时序,应该是SDRAM的吧,这里可是算是到了重要的地方了
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
cpu_init_crit:
/*
* flush v4 I/D caches 刷新缓存,V4的意思是S3C2410内核的架构
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ ;把ARM寄存器里的r0,写到协处理器p15的c7(c7用于控制缓存)寄存器中,一般为是只写的,读没有意义,其中的两个0,是操作码
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ ;刷新v4架构的TLB(TLB一般也是只写的),TLB就是内在映射表,MMU才会用得着吧,参考资料http://hi.baidu.com/grqq7/blog/item/6393607fd0b0af0b28388a8d.html
;不明白这里为什么写两次,暂且放过
/*
* disable MMU stuff and caches ;下面就关于到MMU的设置了
*/
mrc p15, 0, r0, c1, c0, 0 ;把协处理器p15的c1(缓冲类型寄存器)、c0(ID号寄存器)寄存器读到ARM寄存器r0中,
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) ;对r0的13、9、8位清0
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) ;对r0的7、2、0位清0
orr r0, r0, #0x00000002 @ set bit 1 (A) Align ;对r0的1位置1(寄存器的最低位为0位)
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache ;对r0的12位置1
mcr p15, 0, r0, c1, c0, 0 ;把r0的数据送到c1、c0中,原来ID号是这样写进去的呀,哈哈
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will ;cpu_init_crit调用快要返回了,不过在返回之前要设置SRAM的操作时序,设置好就可以使用SDRAM了!
* find a memsetup.S in your board directory.
*/
mov ip, lr ;先看下先行指令吧,为避免再次调用把lr寄存器的数据给覆盖掉,给于保护,ip寄存器就是一个过程调用中间的一个临时寄存器
bl memsetup ;跳转到到内存设置程序,bl 是带返回的跳转,对比下本段程序的第一条跳转指令b,好现在看下跑哪里去了,看代码时要注意看注释,就在我们的board/smdk2410下,
mov lr, ip ;内存设置代码先放一下,回头再把它给贴上来!这里假设内存已经初始化成功,把调用memsetup前保存到lr值恢复
mov pc, lr ;这里就是把调用cpu_init_crit时的程序指针恢复,这里PC指向bl调用cpu_init_crit的一条指令的地址,到这里现在返回到调用cpu_init_crit
/*
*************************************************************************
*
* Interrupt handling 下面这段代码是中断处理函数,不理它,累人,先把总体流程给搞清楚再说
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/LUHONGXING/archive/2009/11/30/4907293.aspx