cpu/arm920t/start.S中重要代码分析
cpu/arm920t/start.S中重要代码分析
前言:总结了网上多个帖子,而且还从书上手工抄写了许多讲解的部分写了这篇cpu/arm920t/start.S中重要代码分析。
U-Boot是从cpu/arm920t/start.S开始的,这个文件的任务是设置处理器状态、初始化中断和内存时序等,并确定是否需要对整个U-Boot代码重定位,最终从Flash中跳转到定位好的内存位置执行。
开始的一段代码是处理的异常处理向量表
.globl _start ;系统复位的位置,由u-boot.ld决定
_start: b reset ;复位向量
;各个异常向量对应的跳转代码
ldr pc, _undefined_instruction ;未定义的指令异常
ldr pc, _software_interrupt ;软件中断异常
ldr pc, _prefetch_abort ;内存操作异常
ldr pc, _data_abort ;数据异常
ldr pc, _not_used ;未使用0x14
ldr pc, _irq ;普通中断异常
ldr pc, _fiq ;快速中断异常
_undefined_instruction: .word undefined_instruction
_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
下面的语句很重要:
_TEXT_BASE:
.wordTEXT_BASE ;这个值在/board/smdk2410/config.mk中定义
.globl _armboot_start
_armboot_start:
.word _start ;代码段的起始地址也是TEXT_BASE
.globl _bss_start
_bss_start:
.word __bss_start ;由链接程序ld根据链接脚本确定/board/smdk2410/u-boot.lds
.globl _bss_end
_bss_end:
.word _end ;由链接程序ld根据链接脚本确定。
接下来是系统复位,这个过程可参考处理器手册。
reset: ;位启动子程序
mrs r0,cpsr ;保存cpsr到r0中
bic r0,r0,#0x1f ;保留后十六位,其余清零
orr r0,r0,#0xd3 ;相或
msr cpsr,r0
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit ;跳转
#endif
接下来的代码用来关闭看门狗,通过INTMR寄存器屏蔽所有的中断,并配置处理器内部时钟频率,需要仔细研究并深刻理解的是关于重定向(relocate)的代码。
在这里插播一下看门狗的一些说明:看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,使MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。
relocate: ;U-Boot重新定位到RAM
adr r0, _start ;装载指令,r0是代码的当前位置
ldr r1, _TEXT_BASE ;装载寄存器,测试判断是从Flash启动还是RAM
cmp r0, r1 ;比较r0和r1,调试的时候不要执行重定位
beq stack_setup ;如果r0等于r1,跳过重定位代码
;准备重新定位代码
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 ;r2 得到armboot的大小
add r2, r0, r2 ;r2 得到要复制代码的末尾地址
copy_loop:
ldmia r0!, {r3-r10} ;过后增加装置命令,从源地址[r0]复制
stmia r1!, {r3-r10} ;过后增加存储命令,复制到目的地址[r1]
cmp r0, r2 ;复制数据块直到源数据末尾地址[r2]
ble copy_loop ;小于等于,则复制
调试阶段的代码是直接在RAM中运行的,而最后需要把这些代码固化到Flash中,因此U-Boot需要自己从Flash转移到RAM中运行。这也是重定向的目的所在。
其中关键的一步就是通过adr指令得到当前代码的地址信息:
1. 如果U-Boot是从RAM开始运行,则从ard r0,_start得到的地址信息为r0 = _start = _TEXT_BASE = TEXT_BASE = 0x33f8000;
2. 如果U-Boot从Flash开始运行,即从处理器对应的地址运行,则r0=0x0(Flash起始地址),这时将会执行copy_loop标识那段代码。
;初始化堆栈等
stack_setup:
ldr r0, _TEXT_BASE ;上面是128 KiB重定位的u-boot
sub r0, r0, #CFG_MALLOC_LEN ;向下是内存分配空间
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 ;为abort-stack预留3个字
clear_bss:
ldr r0, _bss_start ;找到bss段起始地址
ldr r1, _bss_end ;bss段末尾地址
mov r2, #0x00000000 ;清零
clbss_l:str r2, [r0] ;r2装载到[r0]中,bss段地址空间清零循环...
add r0, r0, #4 ;每次复制一个机器字长(32位,4字节)
cmp r0, r1
bne clbss_l ;如果不等于或小于,跳转到clbss_l继续循环
最终将通过下面的语句跳转到C代码执行。
ldr pc, _start_armboot;跳转到start_armboot函数入口,
;_start_armboot字保存函数入口指针
_start_armboot:
.word start_armboot ;start_armboot函数在lib_arm/board.c
start_armboot()在lib_arm/board.c中定义,它类似于Linux内核的start_kernel(),它们都是一种系统初始化的接口函数:在内核的start_kernel()中集中完成了内核几乎所有资源的初始化,包括CPU相关的资源和外设接口等;而在 start_armboot()中也要完成一些初始化工作。