|
/******************************************************************
* ARM启动代码。
** 假如不作内存初始化,就只建立堆栈,重新定位代码到RAM位置。
* 然后就可以跳到第二阶段的代码运行了。
**********************************************************************/
/* 保存变量的数据区 */
_TEXT_BASE:
.word TEXT_BASE
.globl _ARMboot_start
_ARMboot_start:
.word _start
/** These are defined in the board-specific linker script.*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
ARM
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
ARM
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif 字串5
上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它的使用。还有一些变量的长度是通过连接脚本里得到,实际上由编译器算出来的。
字串4
看了数据区,这次要看从引导那里跳到这里执行时,运行什么东西了。
/** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/
reset:
/* * 设置cpu运行在SVC32模式。*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0x13
msr cpsr,r0
51soc嵌入式SOC开发论坛
具体分析如下:
/** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/ ARM
reset:
/** 设置cpu运行在SVC32模式。S3C44B0共有7种模式。 */
mrs r0,cpsr
取得当前程序状态寄存器cpsr到r0。
bic r0,r0,#0x1f
这里使用位清除指令,把中断全部清除,只置位模式控制位。
orr r0,r0,#0x13
计算为超级保护模式。
msr cpsr,r0
设置cpsr为超级保护模式。
通过设置ARM的CPSR寄存器,让CPU运行在操作系统模式,为后面进行其它操作作好预备了。后面的代码如下:
/* * 当是从FLASH启动时,就要进行内存测试,当
* 是从RAM启动时,一般就是开发本源程序时,就
* 可以跳过。
*
*/
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
/*
* 在重新定位之前,要进行RAM访问时间测试,因为每个开发
* 都是不一样的。
* 可以在文件memsetup.S里看到它的说明。
*/
bl memsetup
#endif
/* 进行重定位 */
relocate: /* 重定位Boot代码到RAM内存,比如从FLASH移到RAM */
adr r0, _start /* 把_start的相对地址移到r0 */
ldr r1, _TEXT_BASE /* 把_TEXT_BASE地址,就是BOOT在RAM中运行地址 */
cmp r0, r1 /* 比较两个地址是否相同,假如相同,就已经在RAM运行,否则就是FLASH中运行。*/
beq stack_setup
M开发论坛
/* 是在FLASH中运行,要把FLASH中的BOOT代码移到RAM中,然后再运行. */
ldr r2, _ARMboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2保存引导代码大小 */
add r2, r0, r2 /* r2保存引导代码最后地址 */
copy_loop:
ldmia r0!, {r3-r10} /* 从源地址[r0]读取8个字节到寄存器,每读一个就更新一
字串5
次r0地址 */
stmia r1!, {r3-r10} /* 拷贝寄存器r3-r10的值保存到 [r1]指明的地址,每写一 ARM开发论坛
个字节,就增加1. */
cmp r0, r2 /* 判定是否拷贝到[r2]地址,就是引导代码结束位置。 */
ble copy_loop /* 循环拷贝 */
/*拷贝中断向量表,实际是建立起二级中断向量表,当CPU中断时,先运行FLASH中断,接着就转移到实际中向表执行
ARM
中断程序。*/
adr r0, real_vectors
add r2, r0, #1024
ldr r1, =0x0c000000
add r1, r1, #0x08
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble vector_copy_loop
/* 建立起堆栈 */
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 */
ldr pc, _start_armboot /* 已经预备好了堆栈,就可跳到C写的代码里,由于我的代码是ARM,就是
跳到lib_arm\board.c(208):void start_ARMboot (void)中运行。 */ 字串5
_start_armboot: .word start_ARMboot 字串5
这样一个ARM的启动代码就写完毕了!
/**************************************************************************
* CPU_init_critical临界区寄存器
* 设置一些重要的寄存器,并进行内存测试。
*************************************************************************
*/
#define INTCON (0x01c00000 0x200000) /* 中断控制器 */
#define INTMSK (0x01c00000 0x20000c) /* 中断控制屏蔽寄存器 */
#define LOCKTIME (0x01c00000 0x18000c)
#define PLLCON (0x01c00000 0x180000)
#define CLKCON (0x01c00000 0x180004)
#define WTCON (0x01c00000 0x130000) ARM
cpu_init_crit:
字串8
/* 关闭看门狗 */
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0] ARM
/** 清除所有中断位,设置INTMRs实现。*/
ldr r1,=INTMSK
ldr r0, =0x03fffeff
str r0, [r1]
ldr r1, =INTCON
ldr r0, =0x05
str r0, [r1]
ARM开发论坛
/* 设置时钟控制寄存器 */
ldr r1, =LOCKTIME
ldrb r0, =800
strb r0, [r1]
字串4
/* 设置锁相环,控制CPU运行速度。 */
ldr r1, =PLLCON
#if CONFIG_S3C44B0_CLOCK_SPEED==66
ldr r0, =0x34031 /* 66MHz (Quartz=11MHz) */
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
ldr r0, =0x610c1 /*B2: Xtal=20mhz Fclk=75MHz */
#else
字串4
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
str r0, [r1]
ldr r1,=CLKCON
ldr r0, =0x7ff8
str r0, [r1] ARM
/* 调用子函数返回 */
mov pc, lr
字串8
/*************************************************/
/* 实际的中断向量表 */
/*************************************************/
字串8
real_vectors:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq
b fiq
/*************************************************/
字串5
undefined_instruction:
mov r6, #3
b reset ARM开发论坛
software_interrupt:
mov r6, #4
b reset ARM
prefetch_abort:
mov r6, #5
b reset ARM
data_abort:
mov r6, #6
b reset
字串8
not_used:
/* we *should* never reach this */
mov r6, #7
b reset
irq:
mov r6, #8
b reset
fiq:
mov r6, #9
b reset ARM
把引导的汇编看完,已经预备C的运行环境,下面就开始学习C的源程序,从start.S文件里到跳文件lib_ARM\board.c里运 字串5
行.
/*引导程序从汇编start.S里跳到这里执行。蔡军生 2005/07/19*/
void start_ARMboot (void)
{
/* 声明一个全局指针,它是指向一个数据结构,用于保存参数。并且它占用r8寄存器,用它来保存内存地址,达到全局使用目的。*/
DECLARE_GLOBAL_DATA_PTR;
ulong size;
init_fnc_t **init_fnc_ptr;
char *s; ARM
#if defined(CONFIG_VFD) defined(CONFIG_LCD) unsigned long addr; 字串4
#endif
/* gd指针可写,因为已经分配一个寄存器给它作为变量。
这里就相当于把后面算出来的地址保存到r8寄存器.
*/
gd = (gd_t*)(_ARMboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* 下面一句是阻止3.4以上版本的GCC进行代码优化,把后面的代码删除掉。 */ 字串4
__asm__ __volatile__("": : :"memory");
字串8
/* 清空gd指向的结构 */
memset ((void*)gd, 0, sizeof (gd_t));
/* */
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _ARMboot_start; 字串5
这一段预备好保存参数的全局变量区.
后面就是一系列的初始化和获取正确的参数.
字串4
/* 用循环调用所有初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; init_fnc_ptr)
{
if ((*init_fnc_ptr)() != 0)
{
/* 当每个函数初始化失败时,就会挂机在这里。 */
hang();
}
}
上次说到在函数指针数组里,不断地调用所有初始化函数进行初始化,下面就来仔细地分析一下,它们到底是做什么 ARM
的,做什么样的初始化,怎么样为后面做好运行的预备工作。看到第一个初始化函数,就是CPU初始化(cpu_init),
这个函数是在cpu\s3c44b0\cpu.c里,它的作用就是进行S3C44B0初始化工作。看到这个函数内容如下: 字串5
/* CPU初始化。蔡军生 2005/07/23*/
int cpu_init (void)
{
/* 清空缓冲区 */
icache_enable();
return 0;
}
它在里面调用了函数icache_enable(),它就是用来初始化S3C44B0的缓冲区,并且启用CPU缓冲区。因为CPU在加电之 字串8
后,它的初始化值是不启用内部的8K缓冲区的,必须由程序进行设置。接着看看
那个调用的函数又是怎么样初始化内
部缓存区的呢?
/* CPU内存的缓冲初始化。蔡军生 2005/07/23*/
void icache_enable (void)
{
ulong reg;
/* 清空内存的缓冲区. */
s3c44b0_flush_cache();
/* 初始化缓冲区,设置非缓冲区的起始地址和结束地址。第一个寄存器指明下面的地址不要缓存,低16位是起始地址,高16位是结束地址。并且空间大小都是以4K为界。0x0000:0000 - 0x0C00:0000*/ M开发论坛
NCACHBE0 = 0xC0000000;
NCACHBE1 = 0x00000000; ARM
/* 设置SYSCFG寄存器启用8K缓冲区。 */ M开发论坛
reg = SYSCFG;
reg = 0x00000006; /* 8kB */ 字串8
SYSCFG = reg;
}
字串4
在这个函数里,第一个先调用函数是进行缓冲区清0的工作,它有一些非凡的地方
|
|