1. 程式人生 > >STM32 eCos 啟動程式碼分析(一)系統復位

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的初始化函式。