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í)行的時候所在的模式,處理器每次reset重啟后進入這個模式;異常處理模式(Handler Model),CPU異常處理的時候進入這個模式,當(dāng)CPU執(zhí)行完異常處理程序后會退回到Thread Model。
3.2) Cortex-M4兩種特權(quán)級:
非特權(quán)級狀態(tài)(Unprivileged),軟件限制使用MSR和MRS指令,不能使用CPS指令。不能訪問系統(tǒng)定時器,NVIC嵌套向量中斷控制器和系統(tǒng)控制塊。特權(quán)級狀態(tài)(Privileged),能訪問所有資源,使用所有指令。
3.3) Thread模式下,CONTROL寄存器控制軟件執(zhí)行在privileged或者unprivileged狀態(tài)。unpriveleged 軟件執(zhí)行時可以通過 SVC 指令進行supervisor call進入privilieged software。
3.4) 處理器使用降棧。Thread mode下,CONTROL寄存器控制處理器使用main stack還是進程棧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 這個是reset 值)還是Process Stack Pointer(PSP)
note:reset后,處理器裝載0x00000000地址處的四字節(jié)值到MSP寄存器。也就是說系統(tǒng)啟動的時候,0地址處存放的是MSP寄存器的值。
R14是連接寄存器(Link Register),他存儲子程序、函數(shù)調(diào)用、異常處理程序時的返回信息。reset重啟時,處理器設(shè)置LR寄存器的默認(rèn)值為0xFFFFFFFF。
R15是程序計數(shù)器(Program Counter PC)。存儲當(dāng)前程序地址。reset重啟的時候,處理器裝載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 上電啟動
根據(jù)上面的分析,reset后處理器從0x00000000地址處取四字節(jié)值到MSP寄存器,也就是取 __StackTop標(biāo)號的值到MSP寄存器。然后處理器裝載0x00000004地址處的值(Reset_Handler標(biāo)號代表的值)到PC指針,也就是程序跳轉(zhuǎn)到Reset_Handler標(biāo)號處開始運行。
__isr_vector: .long __StackTop /* Top of Stack */ .long Reset_Handler /* Reset Handler */
4.2 關(guān)閉CPU全局中斷
通過匯編指令“cpsid i”,關(guān)閉 CPU 全局中斷的目的是避免啟動過程中中斷的影響;因為此時中斷向量表還未建立好,無法響應(yīng)外設(shè)中斷
Reset_Handler: cpsid i /* Mask interrupts */
4.3 清零R1-R12通用寄存器
每次復(fù)位后, CPU 內(nèi)核寄存器的值是隨機不確定的,所以需要將其清零。
note 1: 為什么清零r1-r7寄存器用ldr偽指令,而清除r8-r12寄存器是要用mov指令 ?
r1-r7是low registers,r8-r12是hight registers,參考ARMv7-M Architecture手冊:
大多數(shù)16位指令指令只能訪問R0-R7這8個通用寄存器(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 是一條偽指令。編譯器會根據(jù) 立即數(shù)的大小,決定用 ldr 指令或者是mov或mvn指令。當(dāng)imd能用mov或者mvn操作時,就將它翻譯成一條mov或mvn指令。當(dāng)imd大于mov或mvn能夠操作的數(shù)時,編譯器會將imd存在一個內(nèi)存單元中,然后再用一條ldr指令加載這個內(nèi)存單元的的值到寄存器中。
LDR r, label 和 LDR r, =label的區(qū)別:
LDR r, =label 會把label表示的值加載到寄存器中,而LDR r, label會把label當(dāng)做地址,把label指向的地址中的值加載到寄存器中。譬如 label的值是 0x8000, LDR r, =label會將 0x8000加載到寄存器中,而LDR r, label則會將內(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 兩個 32-bit 的堆棧,由于中斷和異常處理時使用 MSP 所以必須在發(fā)生中斷/異常之前將其初始化,其初始化值來自默認(rèn)向量表的 0 地址偏移,即 0x0000 地址存放的 4 個字節(jié)。
note: __StackTop是S32K144_64_flash.ld鏈接器腳本中定義標(biā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 就可以運行 C 代碼了,所以此時通過調(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ù)運算使用硬件 FPU)
2)關(guān)閉看門狗(默認(rèn)配置)
3)使能 CPU 內(nèi)核指令緩沖(I-Cache)等 MCU 硬件平臺配置。
#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)。為什么要寫入這個值?
查看S32芯片手冊:
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寄存器的四個功能
4.5.3 配置看門狗TOVAL寄存器
TOVAL是一個16位的超時寄存器(65535),也就是說是timeout為65535個時鐘周期,到點沒有喂狗則產(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初始化
接下來,啟動文件會調(diào)用定義在 SDK->platform->devices 目錄下 startup.c 中的init_data_bss()函數(shù)完成應(yīng)用工程運行所需的 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 符號,獲得工程鏈接結(jié)果中.data 段(有初始化值)、 .bss 段(未初始化和初始化值為 0)的全局變量以及重定向到 RAM 中運行的.code 段代碼/函數(shù)在 Flash 和RAM 中的起始地址和長度(結(jié)束地址-開始地址)。
然后再通過數(shù)據(jù)指針的方式實現(xiàn)全局變量初始化值和重映射代碼從 Flash 到 RAM 中的拷貝以及.bss 段的清零:具體包括:
1)初始化.data 段
2)初始化.code 段
3)初始化.bss 段
4)將中斷向量表從 Flash 拷貝到 RAM 中并
5)初始化 CPU 系統(tǒng)中斷向量偏移地址,使其指向 RAM 中新的中斷向量表(如果編譯目標(biāo)為 debug,編譯結(jié)果存儲在 Flash 中)。
note 1: .code段不是代碼段,.code段屬于m_data域(RAM)用來存放重映射到RAM中的代碼。
note 2: .text段才是代碼段,.text段屬于m_text域(ROM),存放的就是代碼。
note 3: 為什么要將.data .bss 中斷向量表拷貝到m_data域(RAM),因為.data中保存的是初始化過后的全局變量/靜態(tài)局部變量.bss段中保存的是未初始化(初始化為0)的全局變量/靜態(tài)局部變量,在系統(tǒng)運行的時候是需要改變的,而ROM是只讀的,中斷向量表同理。
note 4: 代碼是不需要改變的,為什么也有部分的代碼需要重映射到RAM中執(zhí)行?-- 因為效率,因為相對于ROM來說,RAM的數(shù)據(jù)寬度較大,速度較快。
note 5: 怎么將代碼重映射到RAM中?
通過上面的分析可知,在 S32K1xx 系列 MCU 的啟動過程,會自動將定義在.code 段中的代碼/函數(shù)從其 Flash 儲存地址拷貝到 RAM 中的運行時地址。
只有將用戶代碼分配到 Flash 中的編譯目標(biāo),即使用 S32K1xx_xx_flash.ld 鏈接文件的編譯目標(biāo)才存在代碼重映射。若是將應(yīng)用工程編譯結(jié)果代碼分配到 RAM 的編譯目標(biāo)(使用 S32K1xx_xx_ram.ld 鏈接文件),其編譯的函數(shù)/代碼本身就是儲存在 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 段的具體實現(xiàn):int __attribute__ ((section(".code_ram"))) main(void)。
在 S32DS IDE 應(yīng)用工程中,一個函數(shù)若沒有特別指定,其將分配到.text 代碼段。
需要注意是關(guān)鍵詞-- __attribute__ ((section(".code_ram"))) 添加的位置,每個需要指定的函數(shù)都要添加這個關(guān)鍵詞,因此,可以將其定義為一個宏比如 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)備工作之后,啟動過程的最后一步是跳轉(zhuǎn)到應(yīng)用程序main()函數(shù).
bl main
5. 相關(guān)應(yīng)用
從bootloader跳轉(zhuǎn)到application的時候,要設(shè)置應(yīng)用中R13寄存器中的堆棧地址,然后直接跳轉(zhuǎn)到app中的Reset_Handler執(zhí)行:bootup_application(0x5000, 0x5004)
同時需要修改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文章
360瀏覽量
41632 -
Cortex-M4
+關(guān)注
關(guān)注
6文章
89瀏覽量
46494 -
S32k144
+關(guān)注
關(guān)注
1文章
7瀏覽量
1876
原文標(biāo)題:S32K144啟動流程分析
文章出處:【微信號:eng2mot,微信公眾號:汽車ECU開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論