stdlf
分类:
2009-09-04 22:12:19
uboot代码分析之一(/cpu/start.s)
2008-07-13 20:53
/*
* Startup Code for S3C44B0 CPU-core
* (C) Copyright 2004
*
*/
/*U-BOOT是从cpu/s3c44bo/start.s开始的,这个文件的任务是设置处理器状态,初始化中断和内存时序等,并确定是否需要对整个U-BOOT代码重定位,最终从FLASH中跳转到定位好的内存位置执行*/
#include
#include
/*Jump vector table ;异常处理向量表
对于复位异常,CPU通过b reset进入复位代码,重新启动系统。发生复位异常通常意味着出现了较严重的错误,必须对系统重新引导。复位代码总在ROM中执行,而b指令可寻址32MB,应付本例中的2MB Flash绰绰有余。其它类型异常则比较自由,如何处理完全由用户决定。因此它们的处理常常在RAM中进行,而本例中RAM挂接在BANK6,地址范围为0x0c00_0000- 0x0c7f_ffff,已经超出了b指令的寻址范围。于是通过ldr指令重置pc*/
.globl _start //系统复位的位置,由u-boot.ld决定,将_start声明为外部程序可
//的标签,
_start: b reset //在此标签处存放各个异常向量对应的跳转代码
ldr pc,=HandleUndef ;未定义的指令异常
ldr pc,=HandleSWI ;软件中断异常
ldr pc,=HandlePabort ;内存操作异常
ldr pc,=HandleDabort ;数据异常
b .
ldr pc,=HandleIRQ ;慢速中断异常
ldr pc,=HandleFIQ ;快速中断异常
ldr pc,=HandleEINT0 /*mGA H/W interrupt vector table*/
ldr pc,=HandleEINT1 ;外部中断0-8异常
ldr pc,=HandleEINT2
ldr pc,=HandleEINT3
ldr pc,=HandleEINT4567
ldr pc,=HandleTICK /*mGA*/ ;时钟滴答异常
b .
b .
ldr pc,=HandleZDMA0 /*mGB*/ ;两类DMA异常
ldr pc,=HandleZDMA1
ldr pc,=HandleBDMA0
ldr pc,=HandleBDMA1
ldr pc,=HandleWDT ;看门狗异常
ldr pc,=HandleUERR01 /*mGB*/
b .
b .
ldr pc,=HandleTIMER0 /*mGC*/ ;六个定时器异常
ldr pc,=HandleTIMER1
ldr pc,=HandleTIMER2
ldr pc,=HandleTIMER3
ldr pc,=HandleTIMER4
ldr pc,=HandleTIMER5 /*mGC*/
b .
b .
ldr pc,=HandleURXD0 /*mGD*/ ;2个串口接受异常
ldr pc,=HandleURXD1
ldr pc,=HandleIIC ;IIC 异常
ldr pc,=HandleSIO ;同步IO口异常
ldr pc,=HandleUTXD0 ; 2个串口发送异常
ldr pc,=HandleUTXD1 /*mGD*/
b .
b .
ldr pc,=HandleRTC /*mGKA*/ ;实时时钟异常
b .
b .
b .
b .
b . /*mGKA*/
b .
b .
ldr pc,=HandleADC /*mGKB*/ ;模数转换异常
b .
b .
b .
b .
b . /*mGKB*/
b .
b .
;下面是各异常处理对应的函数地址
;.equ 在汇编语言中使用,相当于C语言中的#define
.equ HandleReset, 0xc000000
.equ HandleUndef,0xc000004
.equ HandleSWI, 0xc000008
.equ HandlePabort, 0xc00000c
.equ HandleDabort, 0xc000010
.equ HandleReserved, 0xc000014
.equ HandleIRQ, 0xc000018
.equ HandleFIQ, 0xc00001c
.equ HandleADC, 0xc000020
.equ HandleRTC, 0xc000024
.equ HandleUTXD1, 0xc000028
.equ HandleUTXD0, 0xc00002c
.equ HandleSIO, 0xc000030
.equ HandleIIC, 0xc000034
.equ HandleURXD1, 0xc000038
.equ HandleURXD0, 0xc00003c
.equ HandleTIMER5, 0xc000040
.equ HandleTIMER4, 0xc000044
.equ HandleTIMER3, 0xc000048
.equ HandleTIMER2, 0xc00004c
.equ HandleTIMER1, 0xc000050
.equ HandleTIMER0, 0xc000054
.equ HandleUERR01, 0xc000058
.equ HandleWDT, 0xc00005c
.equ HandleBDMA1, 0xc000060
.equ HandleBDMA0, 0xc000064
.equ HandleZDMA1, 0xc000068
.equ HandleZDMA0, 0xc00006c
.equ HandleTICK, 0xc000070
.equ HandleEINT4567, 0xc000074
.equ HandleEINT3, 0xc000078
.equ HandleEINT2, 0xc00007c
.equ HandleEINT1, 0xc000080
.equ HandleEINT0, 0xc000084
.balignl 16,0xdeadbeef /*.balignl是.balign的变体,为伪操作符,控制对齐方式。 它的意思是以当前地址开始,在地址为16的整数倍的地址处的前面插入0xdeadbeef,从 这个位置往后,就是干什么的内存,这个位置往前,禁止访问。*/
/*启动代码(复位后开始执行)如果不做内存初始化,就只建立堆栈,重新装入u-boot 到ram跳转到启动的第二阶段
*************************************************************************
* Startup Code (reset vector)
* do important init only if we don't start from memory!
* relocate u-boot to ram
* setup stack
* jump to second stage
*************************************************************************/
_TEXT_BASE:
.word TEXT_BASE
/*这个值在board\hfrk\hfrks3c44b0\config.mk中定义
TEXT_BASE = 0x0C700000 ,uboot映像在SDRAM中的重定位地址*/
.globl _armboot_start //声明变量_armboot_start为外部程序可访问的标签
_armboot_start:
.word _start //在当前位置_armboot_start处写入word型的值_start
/*代码段的起始地址也是TEXT_BASE 在_armboot_start标号处,保存了_start的值, 也就是说,_armboot_start是存放_start的地址,该地址对应的存储单元内容是 0x0C700000*/
/* These are defined in the board-specific linker script.*/
.globl _bss_start
_bss_start:
.word __bss_start
/*__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是 __bss_start - _start;由链接程序ld根据链接脚本 \board\hfrk\hfrks3c44b0\u-boot.lds确定*/
.globl _bss_end
_bss_end:
.word _end
//bss段结束地址,由链接程序ld根据链接脚本确定,实际上,_armboot_start并没有实 际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start 也是一样的道理,真正有意义//的应该是_start和 __bss_start本身。
#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 //IRQ 与FIQ栈的地址是同一个地址. 系统中不使用IRQ, 故
//面的代码不用关心在\u-boot\include\configs\hfrks3c44b0.h
//中 #undef CONFIG_USE_IRQ
#endif
/*上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它 的使 用。还有一些变量的长度是通过连接脚本里得到,实际上由编译器算出来的。 */
/*the actual reset code*/
/** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/
/* * 设置cpu运行在SVC32模式。*/
reset:
mrs r0,cpsr /*set the cpu to SVC32 mode*/
/*取得当前程序状态寄存器cpsr到r0。
mrs 读状态寄存器指令,在ARM处理器中只有MRS指令可以将状态寄存器CPSR或SPSR 读出到通用寄存器中*/
bic r0,r0,#0x1f
/*bic 位清除指令,用于清除操作数中为1的位,并把结果放置到目的寄存器中。操作数 1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操 作数2为32位的掩码,
31 30 29 28 --- 7 6 - 4 3 2 1 0
N Z C V I F M4 M3 M2 M1 M0
0 0 0 0 0 User26 模式
0 0 0 0 1 FIQ26 模式
0 0 0 1 0 IRQ26 模式
0 0 0 1 1 SVC26 模式
1 0 0 0 0 User 模式
1 0 0 0 1 FIQ 模式
1 0 0 1 0 IRQ 模式
1 0 0 1 1 SVC 模式
1 0 1 1 1 ABT 模式
1 1 0 1 1 UND 模式
1 1 1 1 1 SYS 模式
orr r0,r0,#0x13
/*ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。r0的 0,1,4,位置1. M=10011,对应的cpu模式为管理模式.
通过设置ARM的CPSR寄存器,让CPU运行在管理模式,为后面进行其它操作作好预备了*/
msr cpsr,r0
//we do sys-critical inits only at reboot,
// not when booting from ram!
//重新启动时是否进行初始化, 这个宏要定义在用户的目标板配置头文件中
// 一般情况下是需要的, 不过, 在移植到其它开发板时可就要注意了!!
// 在我们的系统中定义了这个宏, 同时我们也是靠这个函数实现了硬件
// 相关的代码的初始化.
#ifdef CONFIG_INIT_CRITICAL //在\u-boot\include\configs\hfrks3c44b0.h 中定义,因此要执行下面的指令
bl cpu_init_crit //跳转到该标号执行,见下面。BL 是另一个跳转指令,但跳转之前, 会在寄存器R14中保存PC的当前内容,因此,可以通过将R14 的内容重新加载到PC中, 来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本常用的 手段。
/* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a memsetup.S in your board directory.
*/
// 在重新定位之前,要进行RAM访问时间测试,因为每个开发都是不一样的。可以在文件//memsetup.S里看到它的说明
Bl memsetup //memsetup子程序在\board\hfrk\hfrks3c44b0\memsetup.S中实现, //参考该文件注释;
#endif
/*调试阶段的代码是直接在ram中运行的,而最后需要把这些代码固化到FLASH中,因 此U-Boot需要自己从Flash转移到RAM中运行。这也是重定向的目的所在
其中关键的一步就是通过adr r0, _start得到地址信息r0=_start =_TEXT_BASE=TEXT_BASE=0x0C700000 。若r0=r1,代码是在ram里运行,不需要重定位; 不然,uboot代码从flash开始运行,这时需要执行copy_loop那段代码了*/
relocate: /* relocate U-Boot to RAM 把U-Boot重新定位到RAM */
adr r0, _start // r0 <- current position of code r0是代码的 //当前位置
ldr r1, _TEXT_BASE //test if we run from flash or RAM 测试判断是从Flash启动,//还是RAM启动
cmp r0, r1 // don't reloc during debug 比较r0和r1
//调试的时候不要执行重定位
beq stack_setup //如果r0等于r1,跳过重定位代码到stack_setup处执行
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot r2 为 armboot的大小 */
add r2, r0, r2 /* r2 <- source end address r2 得到要复制代码的末地址 */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] 批量加载指令用于将一片 连续的存储器中的数据传送到多个寄存器 IA 每次传送后地址加1;从源地址[r0] 复 制 不允许直接存储器地址相互操作,通过r3-r10寄存器进行 */
stmia r1!, {r3-r10} /* copy to target address [r1] 将当前代码地址内容复制 到0x0c700000*/
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop // 检查是否全部复制完
/*now copy to sram the interrupt vector
发生异常(复位异常除外)后会从ROM的异常向量跳转到RAM*/
adr r0, real_vectors //加载实际的异常中断向量表,见最后;将该real_vectors表起 始的地址放到r0里
add r2, r0, #1024 //r2=r0+1024 复制real_vectors开始的1024字节内容
ldr r1, =0x0c000000
add r1, r1, #0x08 //r1=0x0c000008
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
/*复制0x0c000008开始的目标区域中去,相当于在RAM中建立一个二级跳转表
因此在0x0c000008处放的是复位指令,0x0c00000b放的是未定义异常处理程序, 0x0c000010放的是软中断异常处理程序。以SWI为例,从0x00000008跳转到0x0c000010, 然后跳转到software_interrupt开始执行,最终reset。*/
cmp r0, r2
ble vector_copy_loop
/* Set up the stack 接下来设置堆栈, 这里需要注意的是堆栈在代码段的下方, 不 是上方!!宏CFG_MALLOC_LEN和CFG_GBL_DATA_SIZE在目标板的头文件中定 义.\u-boot\include\configs\hfrks3c44b0.h */
stack_setup:
ldr r0, _TEXT_BASE //upper 128 KiB: relocated uboot 上面是128 KiB重定位的u-boot
sub r0, r0, #CFG_MALLOC_LEN /* malloc area 向下是内存分配空间 */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo 然后是bdinfo结构体地址空间 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) //这里如果需要使用 IRQ, 还有给IRQ保留堆栈空间, 一般不使用
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack 给堆栈异常留3个字的空间 */
ldr pc, _start_armboot //最终通过该语句跳转到c代码处执行,stage1的使 命就完成了
_start_armboot: .word start_armboot
/*start_armboot()在lib_arm\board.c中定义,它类似于Linux内核的 start_kernel(),它们都是一种系统初始化的接口函数:在start_kernel()中集中 完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在 start_armboot()中也要完成一些初始化工作。*/
/*把引导的汇编看完,已经预备C的运行环境,下面就开始学习C的源程序,从 start.S文件跳到文件lib_ARM\board.c里运行. */
/*************************************************************************
* CPU_init_critical registers
* setup important registers
* setup memory timing
*************************************************************************
* 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)
#define BDIDES0 0x1f80008
#define BDIDES1 0x1f80028
接下来的代码用来关闭看门狗,通过INTMR寄存器屏蔽所有的中断,并配置处理器 内部的时钟频率。
cpu_init_crit:
/* disable watch dog */ //禁用看门狗; WTCON看门狗定时器控制寄存器地 址为0x01d30000,第5位为看门狗定时器允许位,为0则禁止看门狗定时器。
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
;使看门狗寄存器WTCON=0x0.
// mask all IRQs by clearing all bits in the INTMRs
ldr r1,=INTMSK
ldr r0, =0x03fffeff
str r0, [r1]
//INTMSK中断屏蔽寄存器,除了全局屏蔽位外,其余的26位都分别对应一个中断源。 当屏蔽位为1时,对应的中断被屏蔽,为0,该中断可正常执行。若全局屏蔽位为1,则 所有的中断都不执行。该寄存器的地址为0x01e000c 使INTMSK=0000 0011 1111 1111 1111 1110 1111 1111,第26位为全局屏蔽位=0。
// Set Clock Control Register
ldr r1, =LOCKTIME
ldrb r0, =800
strb r0, [r1]
//LOCKTIME锁时计数寄存器,用于放PLL锁时计数值
ldr r1, =PLLCON
// 设置锁相环,控制CPU运行速度
#if CONFIG_S3C44B0_CLOCK_SPEED==60
ldr r0, =0x88042 /* 60MHz (Quartz=10MHz) */
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
ldr r0, =0xac042 /* 75MHz */
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
str r0, [r1]
ldr r1,=CLKCON
ldr r0, =0x7ff8
str r0, [r1]
/*CLKCON时钟控制寄存器。CLKCON=0111 1111 1111 1000.打开内部所有模块的钟控
为BDMA设置复位值,BDIDES为初始目的地址寄存器 [31:30] TDM 01=M2IO (从外部存 储器到内部外设)*/
ldr r0, =BDIDES0
ldr r1, =0x40000000 //BDIDESn reset value should be 0x40000000
str r1, [r0]
ldr r0, =BDIDES1
ldr r1, =0x40000000 //BDIDESn reset value should be 0x40000000
str r1, [r0]
mov pc, lr //返回到bl cpu_init_crit 指令的下面
/* 实际的中断向量表 */
/*************************************************/
/* interrupt vectors */
/*************************************************/
real_vectors:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq
b fiq
/*************************************************/
undefined_instruction:
mov r6, #3
b reset
software_interrupt:
mov r6, #4
b reset
prefetch_abort:
mov r6, #5
b reset
data_abort:
mov r6, #6
b reset
not_used:
/* we *should* never reach this */
mov r6, #7
b reset
irq:
mov r6, #8
b reset
fiq:
mov r6, #9
b reset