FSP庫啟動(dòng)文件詳解
13.1. 什么是啟動(dòng)文件
啟動(dòng)文件是系統(tǒng)上電復(fù)位后執(zhí)行的第一個(gè)程序。 主要做了以下工作:
- 初始化堆棧。
- 使能FPU(float-point unit,即浮點(diǎn)單元)。
- 定位中斷向量表。
- 配置系統(tǒng)時(shí)鐘。
- 啟用CORTEX-M33棧監(jiān)視器。
- 初始化C語言運(yùn)行環(huán)境。
- 初始化變量SystemCoreClock,這個(gè)變量存放的是處理器時(shí)鐘的頻率。
- 初始化用于觸發(fā)NVIC中斷的ELC(Event Link Controller)事件。
- 初始化IO口
13.2. 啟動(dòng)文件代碼講解
13.2.1. 復(fù)位程序
14-0:復(fù)位程序
void Reset_Handler (void)
{
/* 使用BSP對(duì)系統(tǒng)進(jìn)行初始化. */
SystemInit();
/* Call user application. */
main();
while (1)
{
/* Infinite Loop. */
}
}
這是系統(tǒng)上電或復(fù)位后執(zhí)行的第一個(gè)程序,使用BSP對(duì)系統(tǒng)進(jìn)行初始化,隨后通過main函數(shù)進(jìn)入用戶代碼。 BSP負(fù)責(zé)使MCU從復(fù)位狀態(tài)進(jìn)入到用戶的應(yīng)用程序。 在到達(dá)用戶的應(yīng)用程序之前,BSP設(shè)置棧、堆、時(shí)鐘、中斷、C語言運(yùn)行環(huán)境和堆棧監(jiān)視器。
13.2.2. 棧區(qū)初始化
14-1:棧區(qū)初始化
/* Main stack */
static uint8_t g_main_stack[BSP_CFG_STACK_MAIN_BYTES + BSP_TZ_STACK_SEAL_SIZE]
BSP_ALIGN_VARIABLE(BSP_STACK_ALIGNMENT) //宏展開后為“__attribute__((aligned(8)))”
BSP_PLACE_IN_SECTION(BSP_SECTION_STACK); //宏展開后為“__attribute__((section( ".stack"))) __attribute__((__used__))”
棧是一種先進(jìn)后出的內(nèi)存結(jié)構(gòu),存放函數(shù)的參數(shù)值、返回值、局部變量等,在程序運(yùn)行過程中實(shí)時(shí)加載和釋放。 如果代碼中使用的局部變量和函數(shù)嵌套較多,則需要增加棧區(qū)的大小,需要注意的是, 棧區(qū)分配大小不能超過RAM的大小。 宏“BSP_CFG_STACK_MAIN_BYTES”可以在FSP Configuration的“BSP”屬性欄中的“RA Common”中通過修改“Main stack size”設(shè)置,默認(rèn)為1KB(0x400 B)。
宏“BSP_TZ_STACK_SEAL_SIZE”用于封裝棧頂,便于檢測并阻止攻擊者對(duì)棧的攻擊。 若使用TrustZone,則該宏為8,反之為0。
代碼中的“BSP_ALIGN_VARIABLE(BSP_STACK_ALIGNMENT)” ,宏展開后為“ attribute ((aligned(8)))”,
“BSP_PLACE_IN_SECTION(BSP_SECTION_STACK)”,宏展開后為“ attribute ((section( “.stack”))) attribute ((used ))”
__attribute__可以設(shè)置類型,變量或函數(shù)的屬性,下面將逐一解釋修飾“g_main_stack”的屬性。
- attribute ((aligned(8))):參數(shù)“aligned”指定被修飾對(duì)象的對(duì)齊方式(以字節(jié)為單位)。 “aligned(8)”則意為棧區(qū)在分配時(shí)將 采用8字節(jié)對(duì)齊方式。
- attribute ((section( “.stack”))):參數(shù)“section”可以將變量定義到指定的輸入段中。 “section( “.stack”)”則意為將棧 放到名為“.stack”的輸入段中。
- attribute (( used )):參數(shù)“ used ”告訴編譯器,這個(gè)變量會(huì)被使用,即使沒被調(diào)用也不會(huì)被編譯器警告, 之所以要用這個(gè)屬性修飾變量,是因?yàn)槭褂肅或C++語言的用戶一般不會(huì)直接操作棧, 絕大部分時(shí)候這個(gè)變量是不會(huì)被用戶直接調(diào)用的。
13.2.3. 堆區(qū)初始化
14-2:堆區(qū)初始化
/* Heap */
#if (BSP_CFG_HEAP_BYTES > 0) //若分配堆區(qū)大小為0則不進(jìn)行初始化
BSP_DONT_REMOVE static uint8_t g_heap[BSP_CFG_HEAP_BYTES]
BSP_ALIGN_VARIABLE(BSP_STACK_ALIGNMENT) //宏展開后為“__attribute__((aligned(8)))”
BSP_PLACE_IN_SECTION(BSP_SECTION_HEAP); //宏展開后為“__attribute__((section(".heap"))) __attribute__((__used__))”
#endif
堆沒有棧那樣先進(jìn)后出的順序,用于動(dòng)態(tài)內(nèi)存分配,一般由程序員使用malloc和free進(jìn)行分配和釋放。 BSP_CFG_HEAP_BYTES用于配置堆區(qū)大小,當(dāng)這個(gè)宏定義為0,則不對(duì)堆區(qū)進(jìn)行初始化。 由于MCU中可用的片上SRAM相對(duì)較少,且缺乏內(nèi)存保護(hù),這意味著必須非常小心地控制堆的使用,以避免內(nèi)存泄漏、溢出和試圖過度分配。 因此默認(rèn)堆區(qū)大小被設(shè)置為0。 如果用戶需要(例如一些C標(biāo)準(zhǔn)庫函數(shù)需要使用堆), 可以在FSP Configuration中“BSP”屬性欄的“RA Common”中通過修改“Heap size”來設(shè)置堆區(qū)大小。
代碼中的BSP_DONT_REMOVE只有在使用IAR編譯器的情況下才會(huì)被宏展開為“__root”,意為強(qiáng)制編譯, 保證沒有使用的函數(shù)或者變量也能夠包含在目標(biāo)代碼中。 其他環(huán)境下這個(gè)宏沒有宏展開,可以忽略。
宏“BSP_ALIGN_VARIABLE(BSP_STACK_ALIGNMENT)”同樣也修飾棧,這里的作用和上文的修飾棧的宏相同。
宏“BSP_PLACE_IN_SECTION(BSP_SECTION_HEAP)”展開后為“ attribute ((section(“.heap”))) attribute ((used ))”, 這個(gè)宏與上文的BSP_PLACE_IN_SECTION(BSP_SECTION_STACK)作用差不多,只是參數(shù)“section”不同,這里的“section(“.heap”)” 意為將堆放到名為“.heap”的輸入段中。
13.2.4. 中斷向量表初始化
14-3:中斷向量表初始化
/* Vector table. */
BSP_DONT_REMOVE const exc_ptr_t __Vectors[BSP_CORTEX_VECTOR_TABLE_ENTRIES]
BSP_PLACE_IN_SECTION(BSP_SECTION_FIXED_VECTORS) =
{
(exc_ptr_t) (&g_main_stack[0] + BSP_CFG_STACK_MAIN_BYTES), /* Initial Stack Pointer */
Reset_Handler, /* Reset Handler */
NMI_Handler, /* NMI Handler */
HardFault_Handler, /* Hard Fault Handler */
MemManage_Handler, /* MPU Fault Handler */
BusFault_Handler, /* Bus Fault Handler */
UsageFault_Handler, /* Usage Fault Handler */
SecureFault_Handler, /* Secure Fault Handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
SVC_Handler, /* SVCall Handler */
DebugMon_Handler, /* Debug Monitor Handler */
0, /* Reserved */
PendSV_Handler, /* PendSV Handler */
SysTick_Handler, /* SysTick Handler */
};
宏“BSP_PLACE_IN_SECTION(BSP_SECTION_FIXED_VECTORS)”展開后為“ attribute ((section(“.fixed_vectors”))) attribute (( ** used** ))”。 意為將表放到名為“.fixed_vectors”的輸入段中,并且即使不被使用,編譯器也不會(huì)警告。
13.2.5. 系統(tǒng)初始化()
14-4:SystemInit()
void SystemInit (void)
{
#if __FPU_USED
/* Enable the FPU only when it is used.
* Code taken from Section 7.1, Cortex-M4 TRM (DDI0439C) */
/* Set bits 20-23 (CP10 and CP11) to enable FPU. */
SCB->CPACR = (uint32_t) CP_MASK;
#endif
#if BSP_TZ_SECURE_BUILD
uint32_t * p_main_stack_top = (uint32_t *) __Vectors[0];
*p_main_stack_top = BSP_TZ_STACK_SEAL_VALUE;
#endif
.............. //由于篇幅所限,省略中間代碼
/* Call Post C runtime initialization hook. */
R_BSP_WarmStart(BSP_WARM_START_POST_C);
/* Initialize ELC events that will be used to trigger NVIC interrupts. */
bsp_irq_cfg();
/* Call any BSP specific code. No arguments are needed so NULL is sent. */
bsp_init(NULL);
}
這是MCU進(jìn)入Reset_Handler后執(zhí)行的第一個(gè)函數(shù),正如函數(shù)的字面意思,用于初始化MCU和運(yùn)行環(huán)境, 運(yùn)行完這段代碼后將由main進(jìn)入用戶的hal_entry函數(shù),由于代碼較長,下面將分為幾個(gè)部分對(duì)代碼進(jìn)行分析。
13.2.5.1. 使能FPU
14-5:使能 FPU
#if __FPU_USED
/* Enable the FPU only when it is used.
* Code taken from Section 7.1, Cortex-M4 TRM (DDI0439C) */
/* Set bits 20-23 (CP10 and CP11) to enable FPU. */
SCB->CPACR = (uint32_t) CP_MASK;
#endif
FPU(Float-Point Unit)支持單精度加、減、乘、除、乘、累加、平方根運(yùn)算。 它還提供了定點(diǎn)和浮點(diǎn)數(shù)據(jù)格式以及浮點(diǎn)常量之間的轉(zhuǎn)換的命令。
13.2.5.2. 封裝棧頂
14-6:封裝棧頂
#if BSP_TZ_SECURE_BUILD
uint32_t * p_main_stack_top = (uint32_t *) __Vectors[0];
*p_main_stack_top = BSP_TZ_STACK_SEAL_VALUE;
#endif
這里獲取棧頂指針,并將棧頂賦值為“BSP_TZ_STACK_SEAL_VALUE”,它的宏展開為0xFEF5EDA5,不能用作程序地址,因?yàn)榈刂贩秶鷱?0xE0000000到0xFFFFFFFF是不可執(zhí)行的。 這個(gè)過程被稱為封棧(Sealing Stack),過程如圖。 如果有人針對(duì)棧進(jìn)行攻擊,那么這個(gè)地址 的值會(huì)被覆蓋掉,這會(huì)被檢測并阻止。
13.2.5.3. 設(shè)置中斷向量表的基地址
14-7:設(shè)置中斷向量表的基地址
#if !BSP_TZ_NONSECURE_BUILD
SCB->VTOR = (uint32_t) &__Vectors;
#endif
這里通過直接設(shè)置SCB->VTOR的值來設(shè)置中斷向量表的基地址。 在非安全項(xiàng)目中,這一步會(huì)被跳過。
13.2.5.4. 熱啟動(dòng)回調(diào)函數(shù)
14-8:函數(shù)聲明
void R_BSP_WarmStart(bsp_warm_start_event_t event) __attribute__((weak));
14-9:熱啟動(dòng)回調(diào)函數(shù)
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
/* C runtime environment has not been setup so you cannot use globals. System clocks are not setup. */
}
if (BSP_WARM_START_POST_CLOCK == event)
{
/* C runtime environment has not been setup so you cannot use globals. Clocks have been initialized. */
}
else if (BSP_WARM_START_POST_C == event)
{
/* C runtime environment, system clocks, and pins are all setup. */
}
else
{
/* Do nothing */
}
}
這個(gè)函數(shù)會(huì)被調(diào)用三次,分別在時(shí)鐘初始化前,時(shí)鐘初始化后和C語言運(yùn)行環(huán)境初始化后被調(diào)用,這個(gè)函數(shù)被聲明了“weak”屬性,因此其可以被用戶重寫。 在默認(rèn)情況下,這個(gè)函數(shù)會(huì)在hal_entry.c中被重寫。
14-10:hal_entry中被重寫的函數(shù)
void R_BSP_WarmStart(bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0 //因?yàn)镽A6M5上沒有這個(gè)功能,因此可以忽略這部分
/* Enable reading from data flash. */
R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and
* C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */
#endif
}
if (BSP_WARM_START_POST_C == event)
{
/* C runtime environment and system clocks are setup. */
/* Configure pins. */
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
}
}
默認(rèn)情況下,這里只有在C語言運(yùn)行環(huán)境初始化后,也就是函數(shù)傳入?yún)?shù)為“BSP_WARM_START_POST_C”時(shí),才會(huì)對(duì)用戶在FSP Configuration中配置的引腳進(jìn)行初始化,其他時(shí)候這里不會(huì)進(jìn)行操作。
13.2.5.5. 時(shí)鐘初始化
14-11:時(shí)鐘初始化
bsp_clock_init();
根據(jù)bsp_clock_cfg.h中的設(shè)置來設(shè)置所有系統(tǒng)時(shí)鐘,這些配置來自于FSP Configuration中的Clocks選項(xiàng)卡。
13.2.5.6. 啟用CORTEX-M33棧監(jiān)視器
14-12:啟用CORTEX-M33棧監(jiān)視器
/* Use CM33 stack monitor. */
__set_MSPLIM(BSP_PRV_STACK_LIMIT);
設(shè)置主堆棧指針限制。 沒有ARMv8-M主擴(kuò)展的設(shè)備(即Cortex-M23)缺乏不安全的堆棧指針限制寄存器,因此在非安全模式下此操作被忽略。
13.2.5.7. 初始化C語言運(yùn)行環(huán)境
13.2.5.7.1. 初始化BSS區(qū)
14-13:BSS區(qū)初始化
/* Zero out BSS */
#if defined(__ARMCC_VERSION)
memset((uint8_t *) &Image$$BSS$$ZI$$Base, 0U, (uint32_t) &Image$$BSS$$ZI$$Length);
#elif defined(__GNUC__)
memset(&__bss_start__, 0U, ((uint32_t) &__bss_end__ - (uint32_t) &__bss_start__));
#elif defined(__ICCARM__)
memset((uint32_t *) __section_begin(".bss"), 0U, (uint32_t) __section_size(".bss"));
#endif
BSS(Block Start by Symbol)指用來存放程序中未初始化的全局變量和靜態(tài)變量的一塊內(nèi)存區(qū)域,在這里BSS區(qū)所有數(shù)據(jù)都會(huì)被初始化為0。
13.2.5.7.2. 初始化data區(qū)
14-14:DATA區(qū)初始化
/* Copy initialized RAM data from ROM to RAM. */
#if defined(__ARMCC_VERSION)
memcpy((uint8_t *) &Image$$DATA$$Base, (uint8_t *) &Load$$DATA$$Base, (uint32_t) &Image$$DATA$$Length);
#elif defined(__GNUC__)
memcpy(&__data_start__, &__etext, ((uint32_t) &__data_end__ - (uint32_t) &__data_start__));
#elif defined(__ICCARM__)
memcpy((uint32_t *) __section_begin(".data"), (uint32_t *) __section_begin(".data_init"),
(uint32_t) __section_size(".data"));
data區(qū)是用來存放已初始化的全局變量,靜態(tài)變量和常量的一塊內(nèi)存區(qū)域。 在這段代碼,數(shù)據(jù)會(huì)從ROM被拷貝到RAM的data區(qū)。
13.2.5.7.3. 調(diào)用全局對(duì)象的構(gòu)造函數(shù)
14-15:調(diào)用全局或靜態(tài)對(duì)象的構(gòu)造函數(shù)
#if defined(__ARMCC_VERSION)
int32_t count = Image$$INIT_ARRAY$$Limit - Image$$INIT_ARRAY$$Base;
for (int32_t i = 0; i < count; i++)
{
void (* p_init_func)(void) =
(void (*)(void))((uint32_t) &Image$$INIT_ARRAY$$Base + (uint32_t) Image$$INIT_ARRAY$$Base[i]);
p_init_func();
}
#elif defined(__GNUC__)
int32_t count = __init_array_end - __init_array_start;
for (int32_t i = 0; i < count; i++)
{
__init_array_start[i]();
}
#elif defined(__ICCARM__)
void const * pibase = __section_begin("SHT$$PREINIT_ARRAY");
void const * ilimit = __section_end("SHT$$INIT_ARRAY");
__call_ctors(pibase, ilimit);
#endif
RA系列MCU是支持使用C++語言進(jìn)行開發(fā)的,這段代碼用于調(diào)用C++的全局對(duì)象的構(gòu)造函數(shù)。
13.2.5.8. 初始化SystemCoreClock的值
14-16:初始化SystemCoreClock的值
/* Initialize SystemCoreClock variable. */
SystemCoreClockUpdate();
初始化SystemCoreClock的值,這個(gè)值表示處理器時(shí)鐘的頻率,默認(rèn)為200MHz。
13.2.5.9. 初始化ICU
14-17:初始化 ICU
/* Initialize ELC events that will be used to trigger NVIC interrupts. */
bsp_irq_cfg();
這個(gè)函數(shù)將初始化ICU(Interrupt Control Unit),即中斷控制器,以便配置的ELC事件觸發(fā)NVIC中斷。
-
mcu
+關(guān)注
關(guān)注
146文章
16899瀏覽量
349934 -
中斷
+關(guān)注
關(guān)注
5文章
895瀏覽量
41349 -
代碼
+關(guān)注
關(guān)注
30文章
4723瀏覽量
68237 -
FSP
+關(guān)注
關(guān)注
0文章
34瀏覽量
7100 -
啟動(dòng)文件
+關(guān)注
關(guān)注
0文章
15瀏覽量
2199
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論