C 语言运行前的设置
C 语言环境就是保证 C 语言能够正常运行,C 语言中涉及到入栈与出栈,所以把栈指针设置好之后,C 语言运行环境就设置好了。
SP 寄存器设置
对于 Cortex-A 芯片来讲,大部分芯片在上电以后 C 语言环境还没准备好,所以第一行程序肯定是汇编的,至于要写多少汇编程序,那就看你能在哪一步把 C 语言环境准备好。
所谓的 C 语言环境就是保证 C 语言能够正常运行。C 语言中的函数调用涉及到出栈入栈,出栈入栈就要对堆栈进行操作,所谓的堆栈其实就是一段内存,这段内存 SP 指针访问,SP 指针指向栈顶。芯片一上电 SP 指针还没有初始化,所以 C 语言没法运行,对于有些芯片还需要初始化 DDR,因为芯片本身没有 RAM ,用户代码需要放到 DDR 中去运行。
qemu
对于 qemu 来说,DDR 模式的内存地址默认是能够能够访问的所以不需要关心 DDR 初始化的代码
例如如果在第一条语句就设置 SP 指针,然后就跳到 main 中运行的代码如下
_start:
ldr sp, =0x80200000
b main
main
函数即为 C 语言代码,当然这个时候芯片的外设啥也没有初始化,跳转到 main
中进行初始化也是可行的。
代码示例
还有一种方式是切到 C 语言之前可以先把各个处理器各个模式的栈都初始化好,示例代码如下
.equ Mode_USR, 0x10
.equ Mode_FIQ, 0x11
.equ Mode_IRQ, 0x12
.equ Mode_SVC, 0x13
.equ Mode_MON, 0x16
.equ Mode_ABT, 0x17
.equ Mode_HYP, 0x1A
.equ Mode_UND, 0x1B
.equ Mode_SYS, 0x1F
.equ Stack_size, 0x400
.equ Stack_Start, 0x80200000
.equ Mode_USR_Stack, Stack_Start + Stack_size
.equ Mode_FIQ_Stack, Mode_USR_Stack + Stack_size
.equ Mode_IRQ_Stack, Mode_FIQ_Stack + Stack_size
.equ Mode_SVC_Stack, Mode_IRQ_Stack + Stack_size
.equ Mode_MON_Stack, Mode_SVC_Stack + Stack_size
.equ Mode_ABT_Stack, Mode_MON_Stack + Stack_size
.equ Mode_HYP_Stack, Mode_ABT_Stack + Stack_size
.equ Mode_UND_Stack, Mode_HYP_Stack + Stack_size
.equ Mode_SYS_Stack, Mode_UND_Stack + Stack_size
.global _start
.text
.align 2
_start:
cps #Mode_FIQ
ldr sp, =Mode_FIQ_Stack
cps #Mode_IRQ
ldr sp, =Mode_IRQ_Stack
cps #Mode_SVC
ldr sp, =Mode_SVC_Stack
cps #Mode_MON
ldr sp, =Mode_MON_Stack
cps #Mode_ABT
ldr sp, =Mode_ABT_Stack
cps #Mode_HYP
ldr sp, =Mode_HYP_Stack
cps #Mode_UND
ldr sp, =Mode_UND_Stack
/* sys mode and user have common sp register */
@cps #Mode_SYS
@ldr sp, =Mode_SYS_Stack
/* last enter user mode, and user can't switch to other mode */
cps #Mode_USR
ldr sp, =Mode_USR_Stack
/* switch fail cpu run in user mode */
cps #Mode_SVC
b main
.end
总结
C 语言运行环境准备其实就是设置不同处理器模式下的 SP 指针,使其指向内存中的某块区域
当然,实际使用中需要考虑 DDR 的初始化,MMU,cache 等,这里只是介绍了如何切到 C 语言的方法,具体的芯片外设需要考虑实际情况。