概述
最近接触了STM32,开始了解CortexM3系列ARM处理器上RTOS的移植和启动。
开始总是艰难的,CortexM3是arm7tdmi的升级产品,但实际上和之前的ARM7有着很大的区别。
首先,我们必须有支持CortexM3的编译器,因为CortexM3采用的是Thumb和Thumb2指令集,而不是ARM指令集。
好在eCos 3.0以后ecoscentric提供的toolchain已经支持了CortexM3虽然可能没有采用Thumb2指令集,但是编译和运行eCos是没问题的。
因为CortexM3的中断和异常的特殊性决定了它上面运行的启动代码的不同,这也是为什么我们看到ecos把cortexm的体系结构和之前
的ARM平行放置了。
我们首先看看eCos的第一条语句是怎么运行的,这样我们就比较容易理解后面硬件初始化和中断入口表的代码了。
纵观CortexM启动代码
要了解eCos的CortexM启动代码,需要理清下面几个文件的编译和链接关系。
packages/hal/cortexm/arch/current/src/vector.S
packages/hal/cortexm/arch/current/src/hal_misc.c
packages/hal/cortexm/stm32/stm3210e_eval/current/include/pkgconf/mlt_cortexm_stm3210e_eval_rom.ldi
packages/hal/cortexm/arch/current/src/vector.S
//==========================================================================
.syntaxunified
.thumb
//==========================================================================
//Initial exception vector table
//
//This only contains the stack and entry point for reset. The table
//to be used at runtime is constructed by code in hal_reset_vsr().
.section ".vectors","ax"
.global hal_vsr_table
hal_vsr_table_init:
.long hal_startup_stack // 0 Reset stack
.long hal_reset_vsr // 1 Reset entry
这里我们可以看到,hal_vsr_table_init开始的预留了8个byte,我们可以猜想这个就是系统的启动部分。
但是,要想了解eCos的启动代码,我们必须知道hal_startup_stack和hal_reset_vsr来源于何处如何设定。
//==========================================================================
packages/hal/cortexm/stm32/stm3210e_eval/current/include/pkgconf/mlt_cortexm_stm3210e_eval_rom.ldi
//eCos memory layout
#include<pkgconf/hal.h>
#include<cyg/infra/cyg_type.inc>
MEMORY
{
sram : ORIGIN = 0x20000000, LENGTH =0x00010000-CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
flash: ORIGIN = 0x08000000, LENGTH = 0x00080000
rom : ORIGIN = 0x64000000, LENGTH = 0x01000000
ram : ORIGIN = 0x68000000, LENGTH = 0x00100000
}
SECTIONS
{
SECTIONS_BEGIN
SECTION_rom_vectors(flash, 0x08000000, LMA_EQ_VMA)
SECTION_RELOCS(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_text(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_fini(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_rodata(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_rodata1(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_fixup(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_gcc_except_table(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_eh_frame(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_got(flash, ALIGN (0x8), LMA_EQ_VMA)
SECTION_sram(sram, 0x20000400, FOLLOWING (.got))
SECTION_data(ram, 0x68000000, FOLLOWING (.sram))
SECTION_bss(ram, ALIGN (0x8), LMA_EQ_VMA)
CYG_LABEL_DEFN(__heap1)= ALIGN (0x8);
SECTIONS_END
}
hal_vsr_table= 0x20000000;
hal_virtual_vector_table= hal_vsr_table + 128*4;
hal_startup_stack= 0x20000000 + 1024*64;
这里我们可以看到,这个链接管理脚本设置了hal_startup_stack的大小。
观察map文件
我们通过arm-eabi-nm这样的工具把elf文件生成几个map文件看一看这些symbol的具体位置。
hal_reset_vsr
hal_startup_stack
hal_reset_vsr是系统初始化的一个重要函数
hal_reset_vsr还调用了stm32变体层的初始化函数hal_system_init。分析一下生成的binary文件
以ROM类型的二进制文件为例,如果我们把STM32的芯片设定为从内部的ROM启动。
他会默认的执行0x0000000也就是0x0800000,我们通过Linux的ghex来看这个二进制文件的前8个字节。
我们会看到
0x20010000
0x08003085
而之前我们通过map文件查到的
0x20010000 hal_startup_stack
0x08003084 hal_reset_vsr
那么为什么这两个地址会差1呢,如果我们能合理的解释。那么我们基本上就清晰的了解了CortexM的启动部分了。
查阅CorteM3权威指南
查阅以后我们看到有这样的解释,
在离开复位状态后,CM3 做的第一件事就是读取下列两个 32 位整数的值:
-从地址 0x0000,0000 处取出 MSP 的初始值。
-从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量,LSB 必须是 1。然后从这个值所对应的地址处取指。
因为 CM3 使用的是向下生长的满栈,所以 MSP 的初始值必须是堆栈内存的末地址加 1。
举例来说,如果你的堆栈区域在 0x20007C00‐0x20007FFF 之间,那么 MSP 的初始值就必须是0x20008000。
向量表跟随在 MSP 的初始值之后——也就是第 2 个表目。要注意因为 CM3 是在 Thumb态下执行,
所以向量表中的每个数值都必须把 LSB 置 1(也就是奇数) 正是因为这个原因,图 3.18 中使用 0x101 来表达地址 0x100。
当 0x100 处的指令得到执行后,就正式开始了程序的执行。在此之前初始化 MSP 是必需的,因为可能第 1 条指令还没执行就会被 NMI 或是其
它 fault 打断。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。
对于不同的开发工具,需要使用不同的格式来设置 MSP 初值和复位向量——有些则开发工具自行计算。
如果想要获知细节,最快的办法就是参考开发工具提供的一个示例工程。
结论
通过以上的一些论证,我们了解了CortexM的启动特性,以及如何根据ecos的memory配置文件,
保证系统启动的时候设定好stack并且执行到eCos的初始化函数。