淺談嵌入式MCU軟件開發之S32K1xx系列MCU啓動過程及重映射代碼到RAM中運行方法詳解

內容提要

注:本文摘自NXP工程師胡恩偉的微信公衆號"汽車電子expert成長之路",大家感興趣可以關注一下。

引言

1. S32K1xx系列MCU啓動過程詳解(startup_S32K144.S)

① 關閉CPU全局中斷

②清零CPU內核寄存器R1~R12

③初始化SRAM的ECC

④初始化堆棧

⑤系統初始化

⑥RAM初始化

⑦打開CPU全局中斷

⑧跳轉到應用程序main()函數

2. 重映射代碼/函數到RAM中運行的方法和步驟

①分析應用工程鏈接文件(S32K144_64_flash.ld)

②將想要重映射的代碼/函數通過__attribute__ ((section(".code_ram")))指定到.code_ram段

③在應用工程編譯結果map文件中查看重映射結果

Tips:需要注意是關鍵詞-- __attribute__ ((section(".code_ram"))) 添加的位置,每個需要指定的函數都要添加這個關鍵詞,因此,可以將其定義爲一個宏比如CODE_RAM使用:

總結

 

    引言

 

之前,在我的公衆號文章中已經介紹瞭如何在CodeWarrior 5.x IDE(方法適用於S08、S12(X)以及MagniV S12Z系列MCU)以及S32DS for Power IDE(方法適用於Qorivva MPC57xx和S32R系列MCU)中如何實現用戶代碼重映射(remap)到RAM中運行,具體請參考如下文章(直接點擊文章標題即可跳轉閱讀):

淺談嵌入式MCU軟件開發之startup過程詳解(在CodeWarrior 5.1 中實現RAM自定義初始化)》;

淺談嵌入式MCU軟件開發之startup過程詳解(從復位向量到main函數之前的準備工作)》;

S32DS使用Tips--S32DS for Power V1.2 鏈接文件和啓動過程詳解》;

CodeWarrior IDE使用Tips之如何通過prm文件指定彙編代碼函數、全局變量和常量的儲存地址

 

最近很多讀者朋友問我在S32DS for ARM IDE中如何才能把S32K1xx系列MCU的應用工程的用戶代碼重映射到RAM中運行?

 

爲此,本文將以S32DS for ARM v2018.R1中基於S32K14x SDK v1.9的S32K144應用工程爲例,詳細介紹S32K1xx系列MCU啓動過程及重映射代碼到RAM中運行的具體方法和詳細步驟。

 

 

Tips:本文介紹使用的SDK版本爲S32 SDK S32K14x BETA 1.9.0,請大家到NXP官網下載安裝:

 

Tips:在S32DS for ARM v2018.R1 IDE中,新建工程時,需要選擇Toolchain爲ARM Bare-Metal 32-bit Target Binary Toolchain(其使用的編譯器爲GCC v.6.3),才能選擇和使用S32 SDK S32K14x BETA 1.9.0:

 

 

默認的Toolchain爲Standard S32DS toolchain for ARM,其使用的編譯器爲GCC v.4.9:

 

 

1. S32K1xx系列MCU啓動過程詳解(startup_S32K144.S)

 

由於S32K1xx系列MCU都是使用的ARM Cortex M系列內核,其SRAM和Flash使用同樣的IP,它們的啓動過程都是相同的,只是SRAM和Flash的memory大小不同。所以這裏僅以S32K144爲例進行介紹。

 

在S32DS for ARM v2018.R1 IDE中新建的S32K144應用工程如下,在其工程瀏覽器(Project Explorer)窗口中Project_Settings-->Startup_Code目錄下的存放着整個工程的啓動文件----startup_S32K144.S:

 

 

通過閱讀這個啓動文件,可以知道每次MCU復位後,從其復位向量--Reset_Handler開始運行到用戶程序的main()函數之前,CPU啓動過程還做了如下準備工作:

 

① 關閉CPU全局中斷

 

通過彙編指令“cpsid   i”,關閉CPU全局中斷的目的是避免啓動過程中中斷的影響;因爲此時中斷向量表還未建立好,無法響應外設中斷;

 

②清零CPU內核寄存器R1~R12

 

每次復位後,CPU內核寄存器的值是隨機不確定的,所以需要將其清零;

 

③初始化SRAM的ECC

 

由於S32K1xx系列MCU除4KB FlexRAM外的SRAM帶有ECC功能,所以必須在使用之前對其進入任意寫操作以產生正確的ECC結果,從而完成ECC初始化;

 

④初始化堆棧

 

ARM Cortex M系列CPU內核有MSP和PSP兩個32-bit的堆棧,由於中斷和異常處理時使用MSP所以必須在發生中斷/異常之前將其初始化,其初始化值來自默認向量表的0地址偏移,即0x0000地址存放的4個字節;

 

⑤系統初始化

 

在完成了以上堆棧初始化之後,CPU就可以運行C代碼了,所以此時通過調用定義在工程SDK-->platform-->device-->S32K144-->startup目錄下的system_S32K144.c中的系統初始化函數--SystemInit():

 

根據工程配置完成:①CPU內核FPU配置和使能(如果創建應用工程時選擇浮點數運算使用硬件FPU)、②關閉看門狗(默認配置)和③使能CPU內核指令緩衝(I-Cache)等MCU硬件平臺配置:

 

 

Tips:system_S32K144.c是按照ARM Cortex M系列MCU的軟件接口標準--CMSIS實現的,在任意基於ARM Cortex M系列內核的MCU軟件SDK中都有相同的函數,除了以上介紹的MCU硬件平臺初始化函數SystemInit()外,還包含MCU系統時鐘更新API函數--SystemCoreClockUpdate()和MCU軟件復位API函數--SystemSoftwareReset():

 

 

 

⑥RAM初始化

 

接下來,啓動文件會調用定義在SDK-->platform-->devices目錄下startup.c中的init_data_bss()函數完成應用工程運行所需的RAM初始化:

 

 

在startup.c中通過申明外部變量(extern)的方式,可以引用定義在工程鏈接文件中的__DATA_ROM、__DATA_RAM、__DATA_END、__CODE_RAM、__CODE_ROM、__CODE_END、__BSS_START和__BSS_END符號,獲得工程鏈接結果中.data段(有初始化值)、.bss段(未初始化和初始化值爲0)的全局變量以及重定向到RAM中運行的.code段代碼/函數在Flash和RAM中的起始地址和長度(結束地址-開始地址):

 

 

然後再通過數據指針的方式實現全局變量初始化值和重映射代碼從Flash到RAM中的拷貝以及.bss段的清零:

 

具體包括:①初始化.data段、②初始化.code段、③初始化.bss段,以及④將中斷向量表從Flash拷貝到RAM中並⑤初始化CPU系統中斷向量偏移地址,使其指向RAM中新的中斷向量表(如果編譯目標爲debug,編譯結果存儲在Flash中):

 

 

⑦打開CPU全局中斷

 

在完成RAM初始化和中斷向量表初始化後,就可以打開CPU全局中斷,響應外設中斷了;

 

打開ARM Cortex M系列CPU內核的全局中斷通過彙編語句--“cpsie   i”完成。

 

⑧跳轉到應用程序main()函數

 

在完成以上準備工作之後,啓動過程的最後一步是跳轉到應用程序main()函數;

 

2. 重映射代碼/函數到RAM中運行的方法和步驟

 

通過上面的分析可知,在S32K1xx系列MCU的啓動過程,會自動將定義在.code段中的代碼/函數從其Flash儲存地址拷貝到RAM中的運行時地址。

 

①分析應用工程鏈接文件(S32K144_64_flash.ld)

 

具體來看在應用工程的連接文件中,對.code段的定義如下 :

 

 

Tips:只有將用戶代碼分配到Flash中的編譯目標,即使用S32K1xx_xx_flash.ld鏈接文件的編譯目標才存在代碼重映射。若是將應用工程編譯結果代碼分配到RAM的編譯目標(使用S32K1xx_xx_ram.ld鏈接文件),其編譯的函數/代碼本身就是儲存在RAM中的,所以無需重映射。

 

②將想要重映射的代碼/函數通過__attribute__ ((section(".code_ram")))指定到.code_ram段

 

由於在應用工程鏈接文件中已經將用戶段.code_ram放置在了.code段中,所以,我們只需要在C代碼中,將想要重映射的代碼/函數通過__attribute__ ((section(".code_ram")))指定到.code_ram段即可。

 

比如下面就是將main()函數指定到.code_ram段的具體實現:

 

int __attribute__ ((section(".code_ram"))) main(void)

 

 

 

③在應用工程編譯結果map文件中查看重映射結果

 

完成以上指定後,重新編譯應用工程即可在應用工程的編譯結果map文件中看到main()函數已經被成功重定向了,其長度爲0x20字節,在Flash中的存儲地址(也稱作加載地址--load address)爲0x000005bc,而在RAM中的重映射運行時地址爲0x1fff8404:

 

 

Tips:在S32DS IDE應用工程中,一個函數若沒有特別指定,其將分配到.text代碼段,如果沒特別指定(默認情況下)的main()函數編譯結果如下:

 

 

 

Tips:需要注意是關鍵詞-- __attribute__ ((section(".code_ram"))) 添加的位置,每個需要指定的函數都要添加這個關鍵詞,因此,可以將其定義爲一個宏比如CODE_RAM使用:

 

#define  CODE_RAM  __attribute__ ((section(".code_ram")))

 

然後,再將CODE_RAM放在定義的函數名前即可;

 

比如demo工程中,將CodeRemapToRAM.c中的delay_ms()、Toggle_Onboard_LED()和System_Clock_and_Pinmux_Config()三個函數通過指定到.code_ram段:

 

 

這樣重新編譯demo工程即可看到重映射的結果如下:

 

 

總結

 

本文詳細介紹了S32K1xx系列MCU的啓動過程和重映射代碼/函數到RAM的方法和步驟,希望對大家有所幫助。

 

爲了方便大家學習,現將S32K1xx系列MCU的啓動過程流程圖整理如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章