1. S32K144為NXP公司采用ARM內(nèi)核(cortex-M4)IP和ARMv7-M架構(gòu)集成的SOC。

2. 內(nèi)存映射圖
2.1 S32K144芯片內(nèi)存映射圖(memory map)

2.2 Cortex-M4內(nèi)存映射圖


Code,SRAM,RAM區(qū)域都能保存程序。ARM系統(tǒng)推薦使用Code段來保存執(zhí)行程序。
3. 編程模型
3.1) Cortex-M4兩種工作模式:
線程模式(Thread Model),應(yīng)用程序正常執(zhí)行的時(shí)候所在的模式,處理器每次reset重啟后進(jìn)入這個(gè)模式;異常處理模式(Handler Model),CPU異常處理的時(shí)候進(jìn)入這個(gè)模式,當(dāng)CPU執(zhí)行完異常處理程序后會(huì)退回到Thread Model。
3.2) Cortex-M4兩種特權(quán)級(jí):
非特權(quán)級(jí)狀態(tài)(Unprivileged),軟件限制使用MSR和MRS指令,不能使用CPS指令。不能訪問系統(tǒng)定時(shí)器,NVIC嵌套向量中斷控制器和系統(tǒng)控制塊。特權(quán)級(jí)狀態(tài)(Privileged),能訪問所有資源,使用所有指令。
3.3) Thread模式下,CONTROL寄存器控制軟件執(zhí)行在privileged或者unprivileged狀態(tài)。unpriveleged 軟件執(zhí)行時(shí)可以通過 SVC 指令進(jìn)行supervisor call進(jìn)入privilieged software。
3.4) 處理器使用降棧。Thread mode下,CONTROL寄存器控制處理器使用main stack還是進(jìn)程棧process stack。在Handler模式下,處理器只使用main stack。
3.5) 核寄存器(Core registers)

R0-R12為通用寄存器
特殊寄存器:
R13是堆棧寄存器(stack Pointer),在Thread mode下,COTROL寄存器的bit[1]控制stack pointer作為Main Stack Pointer(MSP 這個(gè)是reset 值)還是Process Stack Pointer(PSP)
note:reset后,處理器裝載0x00000000地址處的四字節(jié)值到MSP寄存器。也就是說系統(tǒng)啟動(dòng)的時(shí)候,0地址處存放的是MSP寄存器的值。
R14是連接寄存器(Link Register),他存儲(chǔ)子程序、函數(shù)調(diào)用、異常處理程序時(shí)的返回信息。reset重啟時(shí),處理器設(shè)置LR寄存器的默認(rèn)值為0xFFFFFFFF。
R15是程序計(jì)數(shù)器(Program Counter PC)。存儲(chǔ)當(dāng)前程序地址。reset重啟的時(shí)候,處理器裝載0x00000004地址處的值到PC指針。
程序狀態(tài)寄存器(Program Status Register),包括應(yīng)用程序狀態(tài)寄存器(Application Program Status Register APSR)、中斷程序狀態(tài)寄存器(IPSR)、異常程序狀態(tài)寄存器(EPSR)。
4. startup_S32K144.S源代碼分析:
4.1 上電啟動(dòng)
根據(jù)上面的分析,reset后處理器從0x00000000地址處取四字節(jié)值到MSP寄存器,也就是取 __StackTop標(biāo)號(hào)的值到MSP寄存器。然后處理器裝載0x00000004地址處的值(Reset_Handler標(biāo)號(hào)代表的值)到PC指針,也就是程序跳轉(zhuǎn)到Reset_Handler標(biāo)號(hào)處開始運(yùn)行。
__isr_vector:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
4.2 關(guān)閉CPU全局中斷
通過匯編指令“cpsid i”,關(guān)閉 CPU 全局中斷的目的是避免啟動(dòng)過程中中斷的影響;因?yàn)榇藭r(shí)中斷向量表還未建立好,無法響應(yīng)外設(shè)中斷
Reset_Handler:
cpsid i /* Mask interrupts */

4.3 清零R1-R12通用寄存器
每次復(fù)位后, CPU 內(nèi)核寄存器的值是隨機(jī)不確定的,所以需要將其清零。
note 1: 為什么清零r1-r7寄存器用ldr偽指令,而清除r8-r12寄存器是要用mov指令 ?
r1-r7是low registers,r8-r12是hight registers,參考ARMv7-M Architecture手冊(cè):
大多數(shù)16位指令指令只能訪問R0-R7這8個(gè)通用寄存器(low registers);只有小部分的指令能訪問R8-R15寄存器(high registers)
note2:LDR R,label 和 LDR R,=label的區(qū)別
LDR 是ARM中的指令,也是偽指令。當(dāng)用 LDR r, =imd ;r 為寄存器, imd為立即數(shù)LDR 是一條偽指令。編譯器會(huì)根據(jù) 立即數(shù)的大小,決定用 ldr 指令或者是mov或mvn指令。當(dāng)imd能用mov或者mvn操作時(shí),就將它翻譯成一條mov或mvn指令。當(dāng)imd大于mov或mvn能夠操作的數(shù)時(shí),編譯器會(huì)將imd存在一個(gè)內(nèi)存單元中,然后再用一條ldr指令加載這個(gè)內(nèi)存單元的的值到寄存器中。
LDR r, label 和 LDR r, =label的區(qū)別:
LDR r, =label 會(huì)把label表示的值加載到寄存器中,而LDR r, label會(huì)把label當(dāng)做地址,把label指向的地址中的值加載到寄存器中。譬如 label的值是 0x8000, LDR r, =label會(huì)將 0x8000加載到寄存器中,而LDR r, label則會(huì)將內(nèi)存0x8000處的值加載到寄存器中。
/* Init the rest of the registers */
ldr r1,=0
ldr r2,=0
ldr r3,=0
ldr r4,=0
ldr r5,=0
ldr r6,=0
ldr r7,=0
mov r8,r7
mov r9,r7
mov r10,r7
mov r11,r7
mov r12,r7
4.4 初始化堆棧
ARM Cortex M 系列 CPU 內(nèi)核有 MSP 和 PSP 兩個(gè) 32-bit 的堆棧,由于中斷和異常處理時(shí)使用 MSP 所以必須在發(fā)生中斷/異常之前將其初始化,其初始化值來自默認(rèn)向量表的 0 地址偏移,即 0x0000 地址存放的 4 個(gè)字節(jié)。
note: __StackTop是S32K144_64_flash.ld鏈接器腳本中定義標(biāo)號(hào),也就是0x20007000地址處,在SRAM中
/* Initialize the stack pointer */
ldr r0,=__StackTop
mov r13,r0
/* S32K144_64_flash.ld */
/* Specify the memory areas */
MEMORY
{
/* Flash */
m_interrupts (RX) : ORIGIN = 0x00005000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00005400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00005410, LENGTH = 0x0007FBF0
/* SRAM_L */
m_data (RW) : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
/* SRAM_U */
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00007000
}
//...
/* Initializes stack on the end of block */
__StackTop = ORIGIN(m_data_2) + LENGTH(m_data_2);
__StackLimit = __StackTop - STACK_SIZE;
PROVIDE(__stack = __StackTop);
4.5 系統(tǒng)初始化
在完成了以上堆棧初始化之后, CPU 就可以運(yùn)行 C 代碼了,所以此時(shí)通過調(diào)用定義在工程 SDK->platform->device->S32K144->startup 目錄下的 system_S32K144.c 中的系統(tǒng)初始化函數(shù)--SystemInit():根據(jù)工程配置完成:1)CPU 內(nèi)核 FPU 配置和使能(如果創(chuàng)建應(yīng)用工程時(shí)選擇浮點(diǎn)數(shù)運(yùn)算使用硬件 FPU)
2)關(guān)閉看門狗(默認(rèn)配置)
3)使能 CPU 內(nèi)核指令緩沖(I-Cache)等 MCU 硬件平臺(tái)配置。
#ifndef __NO_SYSTEM_INIT
/* Call the system init routine */
ldr r0,=SystemInit
blx r0
#endif
我們么有選擇使用FPU和CPU內(nèi)核指令緩沖,所以主要介紹關(guān)閉看門狗操作:
4.5.1 配置CNT(Watchdog Counter Register)寄存器
往看門狗模塊的CNT(Watchdog Counter Register)寄存器寫入0xD928C520(FEATURE_WDOG_UNLOCK_VALUE)。為什么要寫入這個(gè)值?
查看S32芯片手冊(cè):
Unlock sequence of writing 0xC520 and then 0xD928 for allowing updates towrite-once configuration bits.
意思就是看門狗的默認(rèn)配置是lock的,如果要改變看門狗的配置首先需要解鎖:往CNT寄存器中寫入

0xD928C520,然后再通過CS(Watchdog Control and Status Register)來配置看門狗模塊。
4.5.2 配置CS(Watchdog Control and Status Register)
主要配置CS寄存器的四個(gè)功能




4.5.3 配置看門狗TOVAL寄存器
TOVAL是一個(gè)16位的超時(shí)寄存器(65535),也就是說是timeout為65535個(gè)時(shí)鐘周期,到點(diǎn)沒有喂狗則產(chǎn)生看門狗復(fù)位。
/* system_S32K144.c */ /*FUNCTION********************************************************************** * * Function Name : SystemInit * Description : This function disables the watchdog, enables FPU * and the power mode protection if the corresponding feature macro * is enabled. SystemInit is called from startup_device file. * * Implements : SystemInit_Activity *END**************************************************************************/ void SystemInit(void) { /**************************************************************************/ /* FPU ENABLE*/ /**************************************************************************/ #ifdef ENABLE_FPU /* Enable CP10 and CP11 coprocessors */ S32_SCB->CPACR |= (S32_SCB_CPACR_CP10_MASK | S32_SCB_CPACR_CP11_MASK); #ifdef ERRATA_E6940 /* Disable lazy context save of floating point state by clearing LSPEN bit * Workaround for errata e6940 */ S32_SCB->FPCCR &= ~(S32_SCB_FPCCR_LSPEN_MASK); #endif #endif /* ENABLE_FPU */ /**************************************************************************/ /* WDOG DISABLE*/ /**************************************************************************/ #if (DISABLE_WDOG) /* Write of the WDOG unlock key to CNT register, must be done in order to allow any modifications*/ WDOG->CNT = (uint32_t ) FEATURE_WDOG_UNLOCK_VALUE; /* The dummy read is used in order to make sure that the WDOG registers will be configured only * after the write of the unlock value was completed. */ (void)WDOG->CNT; /* Initial write of WDOG configuration register: * enables support for 32-bit refresh/unlock command write words, * clock select from LPO, update enable, watchdog disabled */ WDOG->CS = (uint32_t ) ( (1UL << WDOG_CS_CMD32EN_SHIFT) | (FEATURE_WDOG_CLK_FROM_LPO << WDOG_CS_CLK_SHIFT) | (0U << WDOG_CS_EN_SHIFT) | (1U << WDOG_CS_UPDATE_SHIFT) ); /* Configure timeout */ WDOG->TOVAL = (uint32_t )0xFFFF; #endif /* (DISABLE_WDOG) */ /**************************************************************************/ /* Power mode protection */ /**************************************************************************/ #ifdef SYSTEM_SMC_PMPROT_VALUE /* Power mode protection initialization */ SMC->PMPROT = SYSTEM_SMC_PMPROT_VALUE; #endif }
4.6 RAM初始化
接下來,啟動(dòng)文件會(huì)調(diào)用定義在 SDK->platform->devices 目錄下 startup.c 中的init_data_bss()函數(shù)完成應(yīng)用工程運(yùn)行所需的 RAM 初始化。
/* Init .data and .bss sections */
ldr r0,=init_data_bss
blx r0
在 startup.c 中通過申明外部變量(extern)的方式,可以引用定義在工程鏈接文件中的__DATA_ROM、__DATA_RAM、__DATA_END、__CODE_RAM、__CODE_ROM、__CODE_END、__BSS_START 和__BSS_END 符號(hào),獲得工程鏈接結(jié)果中.data 段(有初始化值)、 .bss 段(未初始化和初始化值為 0)的全局變量以及重定向到 RAM 中運(yùn)行的.code 段代碼/函數(shù)在 Flash 和RAM 中的起始地址和長度(結(jié)束地址-開始地址)。
然后再通過數(shù)據(jù)指針的方式實(shí)現(xiàn)全局變量初始化值和重映射代碼從 Flash 到 RAM 中的拷貝以及.bss 段的清零:具體包括:
1)初始化.data 段
2)初始化.code 段
3)初始化.bss 段
4)將中斷向量表從 Flash 拷貝到 RAM 中并
5)初始化 CPU 系統(tǒng)中斷向量偏移地址,使其指向 RAM 中新的中斷向量表(如果編譯目標(biāo)為 debug,編譯結(jié)果存儲(chǔ)在 Flash 中)。
note 1: .code段不是代碼段,.code段屬于m_data域(RAM)用來存放重映射到RAM中的代碼。
note 2: .text段才是代碼段,.text段屬于m_text域(ROM),存放的就是代碼。
note 3: 為什么要將.data .bss 中斷向量表拷貝到m_data域(RAM),因?yàn)?data中保存的是初始化過后的全局變量/靜態(tài)局部變量.bss段中保存的是未初始化(初始化為0)的全局變量/靜態(tài)局部變量,在系統(tǒng)運(yùn)行的時(shí)候是需要改變的,而ROM是只讀的,中斷向量表同理。
note 4: 代碼是不需要改變的,為什么也有部分的代碼需要重映射到RAM中執(zhí)行?-- 因?yàn)樾?,因?yàn)橄鄬?duì)于ROM來說,RAM的數(shù)據(jù)寬度較大,速度較快。
note 5: 怎么將代碼重映射到RAM中?
通過上面的分析可知,在 S32K1xx 系列 MCU 的啟動(dòng)過程,會(huì)自動(dòng)將定義在.code 段中的代碼/函數(shù)從其 Flash 儲(chǔ)存地址拷貝到 RAM 中的運(yùn)行時(shí)地址。
只有將用戶代碼分配到 Flash 中的編譯目標(biāo),即使用 S32K1xx_xx_flash.ld 鏈接文件的編譯目標(biāo)才存在代碼重映射。若是將應(yīng)用工程編譯結(jié)果代碼分配到 RAM 的編譯目標(biāo)(使用 S32K1xx_xx_ram.ld 鏈接文件),其編譯的函數(shù)/代碼本身就是儲(chǔ)存在 RAM 中的,所以無需重映射。
將 想 要 重 映 射 的 代 碼 / 函 數(shù) 通 過 __attribute__((section(".code_ram")))指定到.code_ram 段由于在應(yīng)用工程鏈接文件中已經(jīng)將用戶段.code_ram 放置在了.code 段中,所以,我們只需要在 C 代碼中,將想要重映射的代碼/函數(shù)通過__attribute__ ((section(".code_ram")))指定到.code_ram 段即可。比如下面就是將 main()函數(shù)指定到.code_ram 段的具體實(shí)現(xiàn):int __attribute__ ((section(".code_ram"))) main(void)。
在 S32DS IDE 應(yīng)用工程中,一個(gè)函數(shù)若沒有特別指定,其將分配到.text 代碼段。
需要注意是關(guān)鍵詞-- __attribute__ ((section(".code_ram"))) 添加的位置,每個(gè)需要指定的函數(shù)都要添加這個(gè)關(guān)鍵詞,因此,可以將其定義為一個(gè)宏比如 CODE_RAM 使用:#define CODE_RAM __attribute__ ((section(".code_ram")))然后,再將 CODE_RAM 放在定義的函數(shù)名前即可。
/*startup.c*/
void init_data_bss(void)
{
uint32_t n;
/* Declare pointers for various data sections. These pointers
* are initialized using values pulled in from the linker file */
uint8_t * data_ram;
uint8_t * code_ram;
uint8_t * bss_start;
const uint8_t * data_rom, * data_rom_end;
const uint8_t * code_rom, * code_rom_end;
const uint8_t * bss_end;
/* Addresses for VECTOR_TABLE and VECTOR_RAM come from the linker file */
extern uint32_t __RAM_VECTOR_TABLE_SIZE[];
extern uint32_t __VECTOR_TABLE[];
extern uint32_t __VECTOR_RAM[];
/* Get section information from linker files */
#if defined(__ICCARM__)
/* Data */
data_ram = __section_begin(".data");
data_rom = __section_begin(".data_init");
data_rom_end = __section_end(".data_init");
/* CODE RAM */
#pragma section = "__CODE_ROM"
#pragma section = "__CODE_RAM"
code_ram = __section_begin("__CODE_RAM");
code_rom = __section_begin("__CODE_ROM");
code_rom_end = __section_end("__CODE_ROM");
/* BSS */
bss_start = __section_begin(".bss");
bss_end = __section_end(".bss");
#else
extern uint32_t __DATA_ROM[];
extern uint32_t __DATA_RAM[];
extern uint32_t __DATA_END[];
extern uint32_t __CODE_RAM[];
extern uint32_t __CODE_ROM[];
extern uint32_t __CODE_END[];
extern uint32_t __BSS_START[];
extern uint32_t __BSS_END[];
/* Data */
data_ram = (uint8_t *)__DATA_RAM;
data_rom = (uint8_t *)__DATA_ROM;
data_rom_end = (uint8_t *)__DATA_END;
/* CODE RAM */
code_ram = (uint8_t *)__CODE_RAM;
code_rom = (uint8_t *)__CODE_ROM;
code_rom_end = (uint8_t *)__CODE_END;
/* BSS */
bss_start = (uint8_t *)__BSS_START;
bss_end = (uint8_t *)__BSS_END;
#endif
/* Check if VECTOR_TABLE copy is needed */
if (__VECTOR_RAM != __VECTOR_TABLE)
{
/* Copy the vector table from ROM to RAM */
for (n = 0; n < (((uint32_t)__RAM_VECTOR_TABLE_SIZE)/sizeof(uint32_t)); n++)
{
__VECTOR_RAM[n] = __VECTOR_TABLE[n];
}
/* Point the VTOR to the position of vector table */
S32_SCB->VTOR = (uint32_t)__VECTOR_RAM;
}
else
{
/* Point the VTOR to the position of vector table */
S32_SCB->VTOR = (uint32_t)__VECTOR_TABLE;
}
/* Copy initialized data from ROM to RAM */
while (data_rom_end != data_rom)
{
*data_ram = *data_rom;
data_ram++;
data_rom++;
}
/* Copy functions from ROM to RAM */
while (code_rom_end != code_rom)
{
*code_ram = *code_rom;
code_ram++;
code_rom++;
}
/* Clear the zero-initialized data section */
while(bss_end != bss_start)
{
*bss_start = 0;
bss_start++;
}
}
4.7 打開CPU全局中斷
在完成 RAM 初始化和中斷向量表初始化后,就可以打開 CPU 全局中斷,響應(yīng)外設(shè)中斷了;打開 ARM Cortex M 系列 CPU 內(nèi)核的全局中斷通過匯編語句--“cpsie i”完成。
cpsie i /* Unmask interrupts */
4.8跳轉(zhuǎn)到應(yīng)用程序 main()函數(shù)在完成以上準(zhǔn)備工作之后,啟動(dòng)過程的最后一步是跳轉(zhuǎn)到應(yīng)用程序main()函數(shù).
bl main
5. 相關(guān)應(yīng)用
從bootloader跳轉(zhuǎn)到application的時(shí)候,要設(shè)置應(yīng)用中R13寄存器中的堆棧地址,然后直接跳轉(zhuǎn)到app中的Reset_Handler執(zhí)行:bootup_application(0x5000, 0x5004)
同時(shí)需要修改app程序中鏈接器腳本中的連接地址
MEMORY
{
/* Flash */
m_interrupts (RX) : ORIGIN = 0x00005000, LENGTH = 0x00000400
m_flash_config (RX) : ORIGIN = 0x00005400, LENGTH = 0x00000010
m_text (RX) : ORIGIN = 0x00005410, LENGTH = 0x0007FBF0
/* SRAM_L */
m_data (RW) : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
/* SRAM_U */
m_data_2 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00007000
}
void bootup_application(uint32_t appEntry, uint32_t appStack)
{
static void (*jump_to_application)(void);
static uint32_t stack_pointer;
//shutdown_drivers();
jump_to_application = (void (*)(void))appEntry;
stack_pointer = appStack;
S32_SCB->VTOR = APP_IMAGE_START;
//__set_MSP(stack_pointer);
__asm volatile ("MSR msp, %0
" : : "r" (stack_pointer) : "sp");
//__set_PSP(stack_pointer);
__asm volatile ("MSR psp, %0
" : : "r" (stack_pointer) : "sp");
jump_to_application();
}

審核編輯:劉清
-
ARM處理器
+關(guān)注
關(guān)注
6文章
361瀏覽量
43077 -
Cortex-M4
+關(guān)注
關(guān)注
6文章
100瀏覽量
47703 -
S32k144
+關(guān)注
關(guān)注
1文章
9瀏覽量
2149
原文標(biāo)題:S32K144啟動(dòng)流程分析
文章出處:【微信號(hào):eng2mot,微信公眾號(hào):汽車ECU開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄

S32K144芯片內(nèi)存映射圖與啟動(dòng)流程分析
評(píng)論