cpu/arm920t/start.S程序步骤大致有以下几个
1、设置中断向量表
2、设置CPU模式为SVC32 mode并且关闭IRQ与FIQ中断
3、关闭看门狗
4、屏蔽所有中断
5、判断程序是否在RAM中运行如果不是的话则先关闭MMU再则需要初始化RAM。
6、设置堆栈准备在C函数中运行了
7、 跳转到C函数clock_init初始化系统时钟
8、跳转到C函数CopyCode2Ram将代码拷贝到RAM中
9、清零BSS段
10、跳转到_start_armboot运行,此时代码已经在RAM中运行了
11、IRQ中断与FIQ中断发生后的上下文处理
1、cpu/arm920t/start.S文件中存放着S3C2440芯片(内核是ARM920T)的异常向量表(即异常入口地址),从S3C2440芯片手册得知每当板子重新上电即芯片复位后pc的值总是指向0,所以在链接时存放在最前面的文件肯定是cpu/arm920t/start.S。
.globl _start
_start: b reset //复位异常地址
ldr pc, _undefined_instruction //未定义异常地址
ldr pc, _software_interrupt //软件中断异常地址
ldr pc, _prefetch_abort //预取指异常地址
ldr pc, _data_abort //数据异常地址
ldr pc, _not_used //保留
ldr pc, _irq //IRQ中断异常地址
ldr pc, _fiq //FIQ快速中断异常地址
_undefined_instruction: .word undefined_instruction//0x20地址处存放着未定义异常跳转地址
_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 16,0xdeadbeef //0x3c地址存放一个固定的标志表明前面的数据为中断向量表占用
2、当复位异常出现后,程序执行b reset后设置CPU模式为SVC32 mode并且关闭IRQ与FIQ中断,b指令为位置无关码,它的寻址方式是先找到reset程序段所在地址然后计算出当前程序地址与reset段地址的偏移然后根据偏移值跳转,这么做的目的是因为:假设开发板通过nand启动的,那么一上电后硬件自动将nand内的前4K程序拷贝到s3c2440内部的ram中运行程序,这段程序的地址与链接地址不一致,当程序运行地址与链接地址不一致时只能使用位置无关码进行跳转
reset:
/*
* set the cpu to SVC32 mode //设置cpsr寄存器使CPU进入系统模式,并且关闭FIQ与IRQ
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
3、关闭看门狗
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON//pWTCON为看门狗寄存器地址,全部清0即关闭看门狗
mov r1, #0x0
str r1, [r0]//关闭看门狗
4、屏蔽所有中断,S3C2440的中断控制器可以控制多个中断源,可以设置中断优先级。通过选出优先级最高的中断源送到ARM920T的IRQ或FIQ异常,最后通过IRQ或FIQ异常响应相应的中断源
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK //INTMSK为中断控制寄存器
str r1, [r0] //屏蔽所有without sub中断源
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK //INTSUBMSK为中断控制寄存器
str r1, [r0] //屏蔽所有with sub中断源
# endif
5、判断程序是否在RAM中运行如果不是的话则先关闭MMU再则需要初始化RAM。下面代码注释引自博客http://www.cnblogs.com/lifexy/p/7309791.html
#ifndef CONFIG_SKIP_LOWLEVEL_INIT //这个在100ask24x0里未定义
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 */
blne cpu_init_crit //未在ram中运行,所以要初始化ram
cpu_init_crit:
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭)
mcr p15, 0, r0, c8, c7, 0 //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址)
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置中断向量表的位置为0x0~0x1c,即异常模式基地址为0X0
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式
orr r0, r0, #0x00000002 @ set bit 2 (A) Align //bit2:开启数据Cache
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache //bit12:开启指令Cache
mcr p15, 0, r0, c1, c0, 0
/*
mcr/mrc:
Caches:是一种高速缓存存储器,用于保存CPU频繁使用的数据。在使用Cache技术的处理器上,当一条指令要访问内存的数据时,
首先查询cache缓存中是否有数据以及数据是否过期,如果数据未过期则从cache读出数据。处理器会定期回写cache中的数据到内存。
根据程序的局部性原理,使用cache后可以大大加快处理器访问内存数据的速度。
其中DCaches和ICaches分别用来存放数据和执行这些数据的指令
TLB:就是负责将虚拟内存地址翻译成实际的物理内存地址,TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。
当应用程序访问一个虚拟地址的时候,会从TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构,
使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先访问的层次。
这里禁用MMU,是方便后面直接使用物理地址来设置控制寄存器
*/
mov ip, lr //临时保存当前子程序返回地址,因为接下来执行bl会覆盖当前返回地址.
bl lowlevel_init //跳转到lowlevel_init(位于u-boot-1.1.6/board/100ask24x0/lowlevel_init.S)
mov lr, ip //恢复当前返回地址
mov pc, lr //退出
.globl lowlevel_init
lowlevel_init://配置存储控制器 by andy
/* 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
sub r0, r0, r1 //求出SMRDATA实际位于的地址,nand启动的话起始是位于0地址的,而不是链接地址
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:
.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 ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0xb1
.word 0x30
.word 0x30
6、设置堆栈准备在C函数中运行了。在SDRAM中,代码段位于0x3f80000以上,sp=_TEXT_BASE-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE-CONFIG_STACKSIZE_IRQ-CONFIG_STACKSIZE_FIQ-12
/* Set up the stack */ //设置堆栈需要放到前面,因为clock_init是C函数
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot *///_TEXT_BASE之前为代码段
sub r0, r0, #CFG_MALLOC_LEN /* malloc area *///减去堆区
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo *///减去全局变量区
#endif
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//减去IRQ与FIQ占用的栈区
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack *///再预留12字节sp为堆栈寄存器,堆栈为向下增长
7、跳转到C函数clock_init初始化系统时钟bl clock_init,clock_init具体代码如下:代码注释引自博客http://www.cnblogs.com/lifexy/p/7309791.html
void clock_init(void)
{
//时钟的寄存器地址只能在函数内部定义,因为代码还不在链接地址处运行
S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000; //定义一个S3C24X0_CLOCK_POWER型结构体指针,clk_power->LOCKTIME=0x4C000000
if (isS3C2410) //isS3C2410为0,执行else
{... ...}
else
{
/* FCLK:HCLK:PCLK = 1:4:8 */
clk_power->CLKDIVN = S3C2440_CLKDIV; //S3C2440_CLKDIV=0X05
/* change to asynchronous bus mod *///C语言内嵌汇编
__asm__( 'mrc p15, 0, r1, c1, c0, 0n' /* read ctrl register */
'orr r1, r1, #0xc0000000n' //使其从快总线模式改变为异步总线模式,在2440手册上看到
'mcr p15, 0, r1, c1, c0, 0n' /* write ctrl register */
:::'r1' //
);
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFFFF; //PLL 锁定时间计数寄存器
/* configure UPLL */
clk_power->UPLLCON = S3C2440_UPLL_48MHZ; //UCLK=48Mhz
/* some delay between MPLL and UPLL */