; --- Initialize stack pointer registers
BL InitStack
IMPORT __main
; --- Now enter the C code
B __main ;
InitStack就不用废话了,操作cpsr切换到各种模式,然后设置各种模式下的堆栈,就可以允许跑c代码了。设置完之后调用ARM提供的__main库函数,如果你使用了这个库函数,那么你必须保证你的c代码中有main函数,__main在做完分散加载,内存初始化,代码拷贝等工作之后默认跳到main函数。因为我只用过ARM官方的编译链接器,所以不知道其他人是怎么做的,不过我估计大概意思都差不多,以前用MIPS的时候用的GCC,也一样要自己写好LD(gcc的分散加载描述文件),不过当时分散加载,代码拷贝和内存初始化都是自己用汇编完成的,不清楚gcc是否也提供了类似于arm这样方便的工具。为什么用B __main呢,因为这个函数是不会返回的,直接把控制权交给main(),而不是交还给调用它的初始化代码。
InitStack
MOV R0, LR
;Build the FIQ stack
MSR CPSR_c, #0xd1
LDR SP, StackFiq
;Build the IRQ stack
MSR CPSR_c, #0xd2
LDR SP, StackIrq
;Build the DATAABORT stack
MSR CPSR_c, #0xd7
LDR SP, StackAbt
;Build the UDF stack
MSR CPSR_c, #0xdb
LDR SP, StackUnd
;Build the SVC stack
MSR CPSR_c, #0xd3 ;/*uCOS starts with SVC mode */
LDR SP, StackSvc
;Build the SYS stack
; MSR CPSR_c, #0xd3 ;
; LDR SP, =StackUsr
;Return
MOV PC, R0
;must be 8 byte aligned
FIQ_STACK_LEGTH EQU 128
IRQ_STACK_LEGTH EQU 2048
ABT_STACK_LEGTH EQU 128
UND_STACK_LEGTH EQU 128
SVC_STACK_LEGTH EQU 2048
StackAbt DCD top_of_stack -UND_STACK_LEGTH - IRQ_STACK_LEGTH-FIQ_STACK_LEGTH-SVC_STACK_LEGTH
StackSvc DCD top_of_stack -UND_STACK_LEGTH - IRQ_STACK_LEGTH-FIQ_STACK_LEGTH
StackFiq DCD top_of_stack -UND_STACK_LEGTH - IRQ_STACK_LEGTH
StackIrq DCD top_of_stack-UND_STACK_LEGTH
StackUnd DCD top_of_stack
AREA MyStacks, DATA, NOINIT
top_of_stack SPACE 4 ;此处的space定义可以不用管,只是为了确定top_of_stack位置
EXPORT bottom_of_heap
AREA Heap, DATA, NOINIT
bottom_of_heap SPACE 1 ;道理同上
END
以上部分我给大家看的是runtime image部分的启动代码,下面再看一下bootloader部分的启动代码,略微有些差别。
因为绝大多数情况下,bootloader中我们不想启用中断 ,也绝不会去处理异常,所以在bootloader中就没有去写中断向量表。但是,没有中断向量表并不意味着cpu就不去响应异常,如果你的程序中有bug,比如说读写了不存在的地址或者函数指针错误,这样一定还是会激起异常,cpu照常去0xc(prefetch abort)或者0x10去拿指令执行,即使这里已经不是中断向量表了,但是cpu依然按照内置的逻辑去执行,所以bootloader要小心再小心,那种固化在SoC内部的bootloader,一旦出问题,你生产出来的芯片就是块石头。 |