- 启动配置
当芯片上电后,CPU会先根据BOOT的配置值,选择从什么地方作为启动区域,本次测试的芯片是以flash作为启动区域。
通常,CPU从0x00000000地址开始执行,当选择flash作为启动区域后,主flash存储器会被映射到启动存储空间(0x00000000),此时flash的内容可以被两个地址开始访问(0x00000000或0x08000000),当我们从0x00000000或0x08000000访问一个从flash启动的芯片时,会发现读取到的内容相同:
在CPU确定启动区域后,就会根据启动区域开始执行程序映像。
- CPU的启动和执行
程序的启动从0x00000000开始,首先会从前四个字节取出栈顶指针的值,然后从0x00000004处开始执行代码,而执行的代码由启动文件决定。启动文件一般会定义一张向量表,而CPU则按照向量表指示地址开始执行。
我使用的的芯片启动文件中的向量表如下:
DCD指令:用于分配一片连续的字存储单元(32bit)并用指定的数据初始化。
取栈顶初始值
由于从0x00000000地址开始执行,向量表第一行的意思就是将头四个字节空间分配用来存储__initial_sp,__initial_sp是栈初始值(栈顶地址),在CPU取完栈指针初始值并将它赋给寄存器SP后,从0x00000004处取出值放入程序计数器PC开始执行,从向量表中可以看到0x00000004处存放的是复位函数的地址,此时程序从复位函数的地址开始执行。
Flash中的复位向量地址是0x080000D5,但是传递到PC指针中的却是0x080000D4,因为在ARM中,函数调用的地址最后一位被用于指示函数运行的状态,为1表示处于Thumb状态,为0表示处于ARM状态,ST的M0和M3内核的芯片都不支持ARM状态,一旦试图切换为ARM状态,则会引发fault。
复位函数:
当把复位函数地址赋值给PC指针后,CPU跳转到复位函数处执行:
PROC:子程序定义伪指令,PROC…ENDP结构表示程序的开始和结束
IMPORT:表示变量是外部定义的
EXPORT:声明一个标号具有全局属性,可被外部文件使用
LDR:这里是一个伪指令,表示加载一个地址值到R0寄存器
BLX:跳转到指令中指定的目标地址,并切换处理器工作状态到Thumb状态
BX:跳转到指令中指定的目标地址
复位函数中的处理其实很简单,指定了CPU接下来要运行的程序,先是跳转到SystemInit中,做了关于flash和时钟的初始化,然后又跳转到__main中,值得注意的是,__main并不是我们熟悉的main函数,它是由编译器提供的一个未开放的函数,它会做一些初始化的工作:将RW、ZI等字段从flash拷贝到RAM中,库函数以及堆栈的初始化等,最后自动跳转到main函数。