都江堰操作系统源码分析之initcpu.s(启动文件)分析

此处使用的DJYOS源码均为V1.0.0版本下的,以DIYSTM32版本的代码为例:
系统一复位,首先运行的是initcpu.s函数(对应在ST的文件夹目录下),先来分析一下这个文件。
    文件一开头就有以下的定义:
    IMPORT     |Image$$handle_msp$$ZI$$Limit|    
    IMPORT     |Image$$handle_msp$$ZI$$Base|     
    IMPORT     cpu_init                         
IMPORT     load_preload

首先,这里声明了Image$$handle_msp$$ZI$$Limit、Image$$handle_msp$$ZI$$Base,这两个是什么东西呢?这里需要用到SCAT文件的知识,Image是执行区的意思,Base:首地址,Limit:尾地址,我们可以看到在工程文件目录下的scatter目录下有两个文件,一个是debug.scat、release.scat,分别是debug版本和release版本下的链接文件,随便打开其中一个,都可以看到其下面有handle_msp +0 EMPTY 0x400这样的定义,初步可以看出,应该是执行区(关于加载区和执行区,请参考SCT文件相关资料)handle_msp零初始化数据段的地址,再打开工程的MAP文件,(双击工程),搜索Image$$handle_msp$$ZI$$Limit、Image$$handle_msp$$ZI$$Base,可以看到
Image$$handle_msp$$ZI$$Base              0x2000bc00   Number         0  anon$$obj.o(handle_msp.bss)
Image$$handle_msp$$ZI$$Limit             0x2000c000   Number         0  anon$$obj.o(handle_msp.bss)
由此可以知道Image$$handle_msp$$ZI$$Base=0x2000bc00,Image$$handle_msp$$ZI$$Limit=0x2000c000,至于为什么是这两个数值呢?我们再回去看一下SCAT文件,我们发现handle_msp的偏移地址是0:+0,在handle_msp前面的一个执行区:heap_top,其地址是多少呢?heap_top 0x2000c000-0x400 EMPTY 0,其首地址是0x2000c000-0x400=0x2000bc00,大小是0,因为其大小是0,所以,handle_msp作为其相邻的执行区,其首地址应该与其一样是0x2000bc00,那么其尾地址自然等于其首地址加上其大小,所以=0x2000bc00+0x400=0x2000c000,这两个数值就可以得到了,接下来,还声明了两个函数cpu_init、load_preload,我们先不管这两个函数是干嘛用的,接着往下看:
AREA        RESET, DATA, READONLY
EXPORT      __Vectors
__Vectors
DCD |Image$$handle_msp$$ZI$$Limit|
DCD Reset_Handler
DCD rst_fault_handler     ;nmi_fault_handler
DCD rst_fault_handler     ;hard_fault_handler
这里定义了一个__Vectors并把它放在RESET段,READONLY代表只读,也就是说放在FLASH区,也就是说在FLASH的最初的存储空间内存储了4个数值:Image$$handle_msp$$ZI$$Limit、Reset_Handler、rst_fault_handler、rst_fault_handler。Reset_Handler和rst_fault_handler是函数的地址。为什么是这四个数值呢?原来,CM3在复位的时候,默认(在不改变0xe000ed08的情况下)是在FLASH的起始位置把堆栈的栈顶指针读出来,赋给SP,然后在把第二个地址的数值读出来,赋给PC,所以,我们这里实际上是把Image$$handle_msp$$ZI$$Limit设为栈顶地址,
Reset_Handler设为复位后第一个执行的函数,再接下来,我们可以看到以下代码:
  AREA    |.text|, CODE, READONLY
rst_fault_handler
nop
bx lr


ENTRY
Reset_Handler
ldr     r0,=|Image$$handle_msp$$ZI$$Limit|
msr     psp,r0
CPSID   I               
CPSID   F               
mov r0,#0
msr control,r0


mov     r0,#0x20000000
ldr     r1,=|Image$$handle_msp$$ZI$$Limit|
str     r1,[r0]
ldr     r1,=Reset_Handler
str     r1,[r0,#0x04]
ldr     r1,=rst_fault_handler
str     r1,[r0,#0x08]
str     r1,[r0,#0x0c]
str     r1,[r0,#0x10]
str     r1,[r0,#0x14]
str     r1,[r0,#0x18]
str     r1,[r0,#0x1c]
str     r1,[r0,#0x20]
str     r1,[r0,#0x24]
str     r1,[r0,#0x28]
str     r1,[r0,#0x2c]
str     r1,[r0,#0x30]
str     r1,[r0,#0x34]
str     r1,[r0,#0x38]
str     r1,[r0,#0x3c]


ldr     r1,=0xe000ed08     
str     r0,[r1]


bl      cpu_init        
bl      load_preload
ALIGN
END

rst_fault_handler是错误处理函数,是一个死循环,Reset_Handler则是程序一复位首先会执行的函数,接下来重点分析一下Reset_Handler函数,
ldr     r0,=|Image$$handle_msp$$ZI$$Limit|
msr     psp,r0
CPSID   I               
CPSID   F               
mov r0,#0
msr control,r0
这几句的注释已经很清楚了,我们主要是看下面的代码,首先,把Image$$handle_msp$$ZI$$Limit的值存到地址为0x20000000的位置,再把Reset_Handler的值存到地址为0x20000004的位置,接下来就是初始化中断的向量表了,全部用rst_fault_handler函数进行初始化,然后再把r0的值0x20000000存进地址为0xe000ed08的位置,为什么要这样做呢?原来,因为CM3在发生中断的时候是从0xe000ed08存储的数值指向的地方把前面所提到的中断向量表读出来的,这里把中断向量表存到RAM里的意义是什么呢?我猜测是因为方便修改,在程序里面可以随意增加或减少中断,实际上后面也是通过往RAM里面写数值来修改这个向量表的。最后,跳转到cpu_init和load_preload中执行。
最后再啰嗦一句,不知道大家有没有疑问,就是为什么initcpu.s是整个工程的启动文件而不是其他文件呢?也就是说为什么程序一上电之后就会跳到这个执行呢?
其实这个问题在前面已经解答过了,我们看一下SCAT文件就会明白,SCAT文件中有这样的定义:
ROM_LOAD 0
{
text_sysload 0
{
initcpu.o (RESET, +FIRST)            ; Initialization code
initcpu.o (+RO)
* (+RO)
}
.
.
.

也就是说,initcpu.s里面的RESET段是放在整个程序的最前面的,所以,__Vectors是放在整个FLASH的最前面的地址的,程序一上电,就会从FLASH的最前面的地址里把SP、PC指针取出来,然后就跳转到了Reset_Handler去执行了。

你可能感兴趣的:(都江堰操作系统源码分析之initcpu.s(启动文件)分析)