startup.S 简介
startup.S 是内核启动时执行的第一个文件。它主要用于实现系统启动时的复位操作。本节以上述创建的空 BSP 工程为例,简要介绍 IDE 中自动生成的 startup.S 文件里实现的相关操作。
堆栈设置
堆栈设置详情如下,startup.S 文件中包含的几个宏定义,用于配置 CPU 在各个处理器模式下的堆栈空间大小。CPU 总的堆栈大小是这几种模式下堆栈大小的总和,一般情况下,不需要对其做修改。
#define SVC_STACK_SIZE 0x00002000
#define SYS_STACK_SIZE 0x00001000
#define FIQ_STACK_SIZE 0x00001000
#define UND_STACK_SIZE 0x00001000
#define ABT_STACK_SIZE 0x00001000
#define IRQ_STACK_SIZE 0x00001000
#define CPU_STACKS_SIZE (SVC_STACK_SIZE + \
SYS_STACK_SIZE + \
FIQ_STACK_SIZE + \
IRQ_STACK_SIZE + \
UND_STACK_SIZE + \
ABT_STACK_SIZE)
函数的声明与导出
汇编代码中如需使用外部函数,则要先对其进行声明。startup.S 中外部函数的声明详情如下,前面声明的几个函数用于处理异常向量。bspInit 函数作为从汇编代码跳入 C 代码执行的入口函数。
IMPORT_LABEL(archIntEntry)
IMPORT_LABEL(archAbtEntry)
IMPORT_LABEL(archPreEntry)
IMPORT_LABEL(archUndEntry)
IMPORT_LABEL(archSwiEntry)
IMPORT_LABEL(bspInit)
注:在创建的空的 BSP 工程里没有将 startup.S 里的函数作导出的操作。
异常向量表
异常向量表大多数情况下是通过汇编代码来实现,startup.S 中的 vector 段详情如下所示。vector 段内定义了一些标签,用伪指令将其指向对应的异常处理函数,而异常处理函数就是上文“函数的声明与导出”中声明的外部函数。异常向量表把这些标签赋值给 PC 指针,当发生对应异常时,程序就能通过异常向量表跳转到对应的异常处理函数中执行。
SECTION(.vector)
FUNC_DEF(vector)
LDR PC, resetEntry
LDR PC, undefineEntry
LDR PC, swiEntry
LDR PC, prefetchEntry
LDR PC, abortEntry
LDR PC, reserveEntry
LDR PC, irqEntry
LDR PC, fiqEntry
FUNC_END()
FUNC_LABEL(resetEntry)
.word reset
FUNC_LABEL(undefineEntry)
.word archUndEntry
FUNC_LABEL(swiEntry)
.word archSwiEntry
FUNC_LABEL(prefetchEntry)
.word archPreEntry
FUNC_LABEL(abortEntry)
.word archAbtEntry
FUNC_LABEL(reserveEntry)
.word 0
FUNC_LABEL(irqEntry)
.word archIntEntry
FUNC_LABEL(fiqEntry)
.word 0
代码段
startup.S 的代码段主要定义了 reset 函数。它是系统复位及启动时第一个执行的函数。reset 函数主要进行以下操作:
- 关看门狗
系统启动时,要将看门狗先关闭,防止不喂狗导致系统不断复位,不能正常启动。
- 初始化堆栈
为了初始化不同工作模式下的堆栈,reset 函数会通过设置 CPSR 寄存器和关中断的操作,让 CPU 处于对应的工作模式,然后根据配置的堆栈宏,设置 CPU 各个模式下的堆栈指针。
- 对核心硬件接口的操作
如 SDRAM、PLL 等,但一般这部分内容在 bootloader 中已经实现,因此这里可以不做操作。
- 初始化 data 段
参看 SylixOSBSP.ld 链接脚本,在链接器进行链接时会将 data 段初始化的数据装载在 text 段,装载地址为 _etext,但 data 段真正的运行地址为 ORIGIN(DATA),因此 startup.S 初始化 data 段的主要工作就是将 _etext 的内容搬运到 ORIGIN(DATA)处,搬运的大小为 SIZEOF(.data)。
- 清 bss 段
reset 函数初始化 data 段之后,需要将 bss 段清零。
- 执行 C 程序
进入 bspInit 函数。根据 ATPCS 协议,reset 函数会设置 R0、R1、R2 和 FD,并把当前的地址保存到 LR 寄存器用于返回,然后跳转到 bspInit 函数中执行。
注意 :
当前工程 bspinit 函数没有接收参数,若需要,可以在汇编中对 R0 等寄存器做操作。