嵌入式软件开发之: 复位和初始化
13.5.4 栈指针初始化
在程序的初始化代码中,用户必须要为处理器用到的各种模式设置堆栈,也就是说,复位处理程序必须为应用程序所使用的任何执行模式的栈指针分配初始值。
下面的例子显示了如何在初始化代码中启用不同模式下的堆栈。
; 启用系统模式堆栈
LDR r2,INT_System_Stack ;将系统堆栈的全局变量放到r2中
STR sp,[r2] ;将系统堆栈指针存储到系统模式下的sp
; 启用系统堆栈限制 (为ARM编译器的堆栈检测做准备)
SUB r1,sp,#SYSTEM_STACK_SIZE ;跳转堆栈指针
BIC r1,r1,#0x03 ;4字节对齐
MOV r10,r1 ;将堆栈的限制放入r10寄存器(AAPCS规则)
LDR r2,INT_System_Limit ;得到堆栈限制全局变量地址
STR r1,[r2] ;将堆栈限制存入全局变量
; 切换到IRQ模式
MRS r0,CPSR ;得到当前的CPSR值
BIC r0,r0,#MODE_MASK ;清除模式位
ORR r1,r0,#IRQ_MODE ;设为IRQ模式
MSR CPSR_cxsf,r1 ;切换到IRQ模式
;启用IRQ模式堆栈
LDR sp,=INT_Irq_SP ;将IRQ模式堆栈指针放入sp_irq
; 切换到FIQ
ORR r1,r0,#FIQ_MODE ;设置FIQ模式位
MSR CPSR_cxsf,r1 ;切换到FIQ模式
; Set-up FIQ stack
LDR sp,=INT_Fiq_SP ;得到FIQ模式指针
; 切换到Abort模式
ORR r1,r0,#ABT_MODE ;设置Abort模式位
MSR CPSR_cxsf,r1 ;切换到ABT模式
; 启用Abort堆栈
LDR sp,=INT_Abort_SP
; 切换到未定义异常模式
ORR r1,r0,#UNDEF_MODE
MSR CPSR_cxsf,r1
;启用未定义指令模式堆栈
LDR sp,=INT_Undefined_SP
; 启用系统/用户堆栈
……
……
为了设置栈指针,进入每种模式(中断禁用)并为栈指针分配适合的值。要利用软件栈检查,也必须在此设置栈限制。
复位处理程序中设置的栈指针和栈限制值由C库初始化代码作为参数自动传递给__user_initial_stackheap()。因此,不允许__user_initial_stackheap()更改这些值。
下面的例子显示了如何实现__user_initial_stackheap(),该段代码可以和上面的堆栈指针设置程序配合使用。
IMPORT heap_base
EXPORT __user_initial_stackheap()
__user_initial_stackheap()
; 程序中指定栈基地址或在描述文件中指定该地址
LDR r0,=heap_base
; r1 contains SB value
MOV pc,lr
13.5.5 硬件初始化
一般情况下,系统初始化代码和主应用程序是分开的。系统初始化要在主应用程序启动前完成。但部分与硬件相关的系统初始化过程,如启用Cache和中断,必须在C库初始化代码执行完成后才能执行。
为了在进入主应用程序之前,完成系统初始化,可以使用$sub和$super函数标识符在进入主程序之前插入一个例程。这一机制可以在不改变源代码的情况下扩展函数的功能。
下面的例子说明了如何使用$sub和$super函数标识。链接程序通过调用$sub$$main()函数取代对main()的调用。所以用户可以在自己编写的$sub$$main()例程中启用Cache或使能中断。
extern void $Super$$main(void);
void $Sub$$main(void)
{
cache_enable(); // 使能caches
int_enable(); // 使能中断
$Super$$main(); // 调用原来的main()函数
}
在$Sub$$main(void)函数中,链接程序通过调用$Super$$main(),使代码跳转到实际的main()函数。
在完成硬件初始化之后,必须考虑主应用程序运行在何种模式。如果应用程序运行在特权模式(Privileged mode),只需在退出复位处理程序前切换到适当的模式;如果应用程序运行在用户模式下,要在完成系统初始化之后,再切换到用户模式。模式的切换工作,一般在$Sub$$main(void)函数中完成。
存储器相关文章:存储器原理
评论