STM32 eCos 啟動程式碼分析(一)系統復位
概述
最近接觸了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的初始化函式。