ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)
內核規(guī)范
??ARM Cortex-M/R 內核的復位啟動過程也被稱為復位序列(Reset sequence)。ARM Cortex-M/R內核的復位啟動過程與其他大部分CPU不同,也與之前的ARM架構(ARM920T、ARM7TDMI等)不相同。大部分CPU復位后都是從 0x00000000 處取得第一條指令開始運行的,然而在ARM Cortex-M/R內核中并不是這樣的。其復位序列為:
ARM Cortex-M/R 內核的中斷向量表布局情況如下圖所示:
注意:中斷向量表的位置可以改變,此處是默認情況下的設置。
??在 ARM Cortex-M/R 內核中,發(fā)生異常后,并不是去執(zhí)行中斷向量表中對應位置處的代碼,而是將對應位置處的數(shù)據存入PC中,然后去此地址處進行取指。簡而言之,在ARM Cortex-M/R的中斷向量表中不應該放置跳轉指令,而是該放置 ISR 程序的入口地址。另外還有兩個細節(jié)問題需要注意:
??第一條是因為在ARM上編程,但凡涉及到調用,就需要遵循一套規(guī)約AAPCS ——《Procedure Call Standard for the ARM Architecture》。AAPCS 中對棧使用的約定是這樣的:
5.2.1.1
 Universal stack constraints
 At all times the following basic constraints must hold:
 Stack-limit < SP <= stack-base. The stack pointer must lie within the extent of the stack.
 SP mod 4 = 0. The stack must at all times be aligned to a word boundary.
 5.2.1.2
 Stack constraints at a public interface
 The stack must also conform to the following constraint at a public interface:
 SP mod 8 = 0. The stack must be double-word aligned.
簡而言之,規(guī)約規(guī)定棧任何時候都必須 4 字節(jié)對齊,在調用入口需8字節(jié)對齊,而且 SP 的最低兩位在硬件上就被置為 0 了。
 ??第二條與 ARM 模式與 Thumb 模式有關。ARM 中 PC 中的地址必須是 32 位對齊的,其最低兩位也被硬件上置 0 了,故寫入PC 中的數(shù)據最低兩位并不代表真實的取址地址。ARM中使用最低一位來判斷這條指令是 ARM 指令還是 Thumb 指令,若最低位為 0,代表 ARM 指令;若最低位為 1,代表 Thumb 指令。在 Cortex-M/R 內核中,并不支持 ARM 模式,若強行切換到 ARM 模式會引發(fā)一個 Hard Fault。
啟動流程
??嵌入式應用程序在用戶定義的 main() 函數(shù)啟動之前需要初始化序列。 這稱為啟動代碼或啟動代碼。 ARM C 庫包含啟動應用程序所必需的預編譯和預組裝代碼段。鏈接應用程序時,鏈接器會根據應用程序從 C 庫中包含必要的代碼,以便為應用程序創(chuàng)建自定義啟動代碼。但需要注意的是,ARM 自己的編譯套件提供了如下三種庫:
 
 根據ARM官方的文檔說明,以下分析描述的啟動代碼適用于標準 ARM C 庫。 它不適用于 ARM C 微庫。 啟動流程同樣適用于 ARMv4T 及更高版本的架構。如下圖清晰的表示了程序啟動到用戶的main() 之前的流程:
 
__main
??函數(shù)__main是C庫的入口點。 除非您更改它,否則__main是ARM鏈接器(armlink)在創(chuàng)建映像時使用的ELF映像的缺省入口點。 下圖顯示了C庫啟動期間__main調用的函數(shù)。
 
__scatterload
??Application code and data can be in a root region or a non-root region. Root regions have the same load-time and execution-time addresses. Non-root regions have different load-time and execution-time addresses. The root region contains a region table output by the ARM linker.應用程序代碼和數(shù)據可以位于根區(qū)域或非根區(qū)域中。 根區(qū)域具有相同的加載時間和執(zhí)行時間地址。 非根區(qū)域具有不同的加載時間和執(zhí)行時間地址。 根區(qū)域包含ARM鏈接器輸出的區(qū)域表。
 ??The region table contains the addresses of the non-root code and data regions that require initialization. The region table also contains a function pointer that indicates what initialization is needed for the region, for example a copying, zeroing, or decompressing function. region表包含需要初始化的非根代碼和數(shù)據區(qū)域的地址。 區(qū)域表還包含一個函數(shù)指針,指示區(qū)域需要初始化,例如復制,歸零或解壓縮功能。
 __scatterload遍歷區(qū)域表并初始化各種執(zhí)行時區(qū)域。 功能如下:
- Initializes the Zero Initialized (ZI) regions to zero
- Copies or decompresses the non-root code and data region from their load-time locations to the execute-time regions.
__main always calls this function during startup before calling __rt_entry .
__rt_entry
??__main調用__rt_entry來初始化堆棧,堆和其他C庫子系統(tǒng)。__rt_entry調用各種初始化函數(shù),然后調用用戶級main()。以下列出了_rt_entry可以調用的函數(shù)。 這些函數(shù)按它們被調用的順序列出:
platform *函數(shù)不是標準C庫的一部分。 如果你定義它們,則鏈接器會在__rt_entry中對它們進行調用。
 main()是用戶級應用程序的入口點。 寄存器r0和r1包含main()的參數(shù)。 如果main()返回,則將其返回值傳遞給exit()并退出應用程序。
 __rt_entry還負責設置堆棧和堆。 但是,設置堆棧和堆取決于用戶指定的方法。 可以通過以下任何方法設置堆棧和堆:
- 調用__user_setup_stackheap。 這也獲得了堆使用的內存邊界(堆頂部和堆基)。
- 使用符號__initial_sp的值加載 SP。
- Using the top of the ARM_LIB_STACK or ARM_LIB_STACKHEAP region specified in the linker scatter file.
??__rt_entry and __rt_lib_init do not exist as complete functions in the C library. Small sections of these functions are present in several internal objects that are part of the C library. Not all of these code sections are useful for a given user application. The linker decides which subset of those code sections are needed for a given application, and includes just those sections in the startup code. The linker places these sections in the correct order to create custom __rt_entry and __rt_lib_init functions as required by the user application.__rt_entry和__rt_lib_init在C庫中不作為完整函數(shù)存在。這些函數(shù)的小部分存在于作為類庫一部分的幾個內部對象中。并非所有這些代碼段都對給定的用戶應用程序有用。鏈接器決定給定應用程序需要這些代碼段的哪個子集,并且只在啟動代碼中包含這些部分。鏈接器按照正確的順序放置這些部分,以便根據用戶應用程序的要求創(chuàng)建自定義的__rt_entry和__rt_lib_init函數(shù)。
_platform_pre_stackheap_init
??The standard C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, before the code that initializes the stack and heap. 標準C庫不提供此函數(shù),但您可以根據需要定義它。 例如,您可以使用此函數(shù)設置硬件。如果您定義了該函數(shù),那么`__rt_entry會在初始化堆棧和堆的代碼之前調用此函數(shù)。
__user_setup_stackheap
??This function enables you to setup and return the location of the initial stack and heap. The C library does not provide this function but you can define it if you require it. __rt_entry calls this function if you define it or if you define the legacy function __user_initial_stackheap . If you define __user_initial_stackheap , then the C library provides a default __user_setup_stackheap as a wrapper around your __user_initial_stackheap function. 此函數(shù)使您可以設置并返回初始堆棧和堆的位置。C庫不提供此函數(shù),但您可以根據需要定義它。 如果你定義了該函數(shù)或者定義了老版本的函數(shù)__user_initial_stackheap,那么__rt_entry 會調用此函數(shù)。 如果定義了__user_initial_stackheap,則C庫提供默認的__user_setup_stackheap作為__user_initial_stackheap函數(shù)的包裝器。
_platform_post_stackheap_init
??The C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, after the code that initializes the stack and heap. C庫不提供此功能,但您可以根據需要定義它。 例如,您可以使用此功能設置硬件。 如果您定義了該函數(shù),那么__rt_entry會在初始化堆棧和堆的代碼之后調用此函數(shù)。
__rt_lib_init
??This function initializes the various C library subsystems. It initializes the referenced library functions, initializes the locale and, if necessary, sets up argc and argv for main() . __rt_entry calls this function always during startup.此函數(shù)初始化各種C庫子系統(tǒng)。 它初始化引用的庫函數(shù),初始化語言環(huán)境,并在必要時為main()設置 argc 和 argv。 __rt_entry在啟動期間始終調用此函數(shù)。
 ??If you use the __user_setup_stackheap or __user_initial_stackheap functions to setup the stack pointer and heap, then the start and end address of the heap memory block are passed as arguments to __rt_lib_init in registers r0 and r1 respectively.如果使用了函數(shù)__user_setup_stackheap 或函數(shù) __user_initial_stackheap來設置堆棧指針和堆,那么堆內存塊的起始和結束地址將作為參數(shù)通過寄存器 r0 和 r1傳遞給__rt_lib_init。
 ??The function returns argc and argv in registers r0 and r1 respectively if the user-level main() requires them. 如果用戶級main()需要,該函數(shù)分別在寄存器 r0 和 r1 中返回argc和argv。
The linker includes various initialization code sections from the internal object files to create a custom __rt_lib_int function. The linker places a function in __rt_lib_init only if it is needed by the application. This lists the functions that _rt_lib_init can call. The functions are listed in the order they get called:鏈接器包括內部對象文件中的各種初始化代碼部分,以創(chuàng)建自定義__rt_lib_int函數(shù)。 只有在應用程序需要時,鏈接器才會在__rt_lib_init中放置一個函數(shù)。 以下列出了_rt_lib_init可以調用的函數(shù)。 這些函數(shù)按它們被調用的順序列出:
這里就不一一介紹各函數(shù)了,想進一步了解的去查看官方文檔即可!
_platform_post_lib_init
??The C library does not provide this function but you can define it if you require it. You can use this function to setup hardware for example. __rt_entry calls this function, if you define it, after the call to __rt_lib_init and before the call to the user-level main() function. C庫不提供此功能,但您可以根據需要定義它。 例如,您可以使用此功能設置硬件。 如果定義了該函數(shù),那么__rt_entry在調用__rt_lib_init之后和調用用戶級main()函數(shù)之前,調用此函數(shù)。
ARM 庫分析
??上面介紹了各函數(shù),這些函數(shù)全部位于 ARM 提供的 C 庫中。我們可以參看任意項目的map文件,來看看都用了哪些庫,如下:
 
 其中,左側的圖為 Image Symbol Table 部分;右側圖為 Image component sizes 部分。從中可以看到:
其他函數(shù)同理,這里暫不說明。在右側可以看到,對應的 C 庫文件為c_w.l和fz_wm.l 。我們可以在 ARM 編譯套件的目錄下找到這兩個文件,路徑如下圖所示:
 
 下面我們使用 ARM 編譯套件中相應的工具來看看具體文件。關于編譯套件的詳細使用說明可以參考博文《ARM 之 主流編譯器(armcc、iar、gcc for arm)詳細介紹》。具體使用的工具就是armar.exe,這是 ARM 的庫文件管理工具。
從中我們可以看到有__main.o等文件,接下來我們可以使用armar -x命令將c_w.l解壓出以上全部文件,然后使用fromelf來查看__main.o的詳細信息,這里就不一一嘗試了!
啟動實例分析
??下面我們以STM32F407VG片子為例,看看其調試時的匯編代碼(Keil5中)。直接進調試模式,注意:最好將匯編窗口右鍵改為assembly mode。
 
 首先看看定義的中斷向量表部分,如下圖:
 
 其中,SystemInit為 ST 提供的時鐘初始化函數(shù)(如果使用了外部RAM,可能還包含外部RAM的配置)。接著,就會有如下匯編代碼(具體看里面的注釋即可):
啟動文件
??目前,多數(shù) MCU 廠商都提供一個啟動文件。當然,編程者也可以自己編寫啟動文件,具體編寫要求ARM的網站上都有相關文檔進行說明。下面分析一下 STM32 啟動文件startup_stm32f407xx.s,具體看里面的注釋。
;******************** (C) COPYRIGHT 2017 STMicroelectronics ******************** ;* File Name : startup_stm32f407xx.s ;* Author : MCD Application Team ;* Version : V2.6.1 ;* Date : 14-February-2017 ;* Description : STM32F407xx devices vector table for MDK-ARM toolchain. ;* This module performs: ;* - Set the initial SP ;* - Set the initial PC == Reset_Handler ;* - Set the vector table entries with the exceptions ISR address ;* - Branches to __main in the C library (which eventually ;* calls main()). ;* After Reset the CortexM4 processor is in Thread mode, ;* priority is Privileged, and the Stack is set to Main. ;* <<< Use Configuration Wizard in Context Menu >>> ;******************************************************************************* ; ;* Redistribution and use in source and binary forms, with or without modification, ;* are permitted provided that the following conditions are met: ;* 1. Redistributions of source code must retain the above copyright notice, ;* this list of conditions and the following disclaimer. ;* 2. Redistributions in binary form must reproduce the above copyright notice, ;* this list of conditions and the following disclaimer in the documentation ;* and/or other materials provided with the distribution. ;* 3. Neither the name of STMicroelectronics nor the names of its contributors ;* may be used to endorse or promote products derived from this software ;* without specific prior written permission. ;* ;* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ;* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ;* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ;* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE ;* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ;* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ;* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ;* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ;* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ;* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;*******************************************************************************; Amount of memory (in bytes) allocated for Stack ; Tailor this value to your application needs ; <h> Stack Configuration ; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h>Stack_Size EQU 0x0800 ; 定義棧大小; AREA 命令指示匯編器匯編一個新的代碼段或數(shù)據段。 AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 代碼段名稱為STACK,未初始化,允許讀寫,8字節(jié)對齊 Stack_Mem SPACE Stack_Size ; 分配Stack_Size的棧空間,首地址賦給Stack_Mem __initial_sp ; 棧頂指針,全局變量; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h>Heap_Size EQU 0x200AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ; 堆末底部地址 Heap_Mem SPACE Heap_Size __heap_limit ; 堆界限地址PRESERVE8 ; 指定當前文件保持堆棧八字節(jié)對齊THUMB ; Thumb命令模式; Vector Table Mapped to Address 0 at Reset ; 終端向量表 重啟時程序從這里運行,必須將該地址映射到0x00000000AREA RESET, DATA, READONLY ; 代碼段名稱為RESET,DATA類型,只讀EXPORT __Vectors ; 導出中斷向量表地址(供外部可以使用)EXPORT __Vectors_End ; 導出中斷向量表結束指針(供外部可以使用)EXPORT __Vectors_Size ; 中斷向量表大小(供外部可以使用) __Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler; External InterruptsDCD WWDG_IRQHandler ; Window WatchDog DCD PVD_IRQHandler ; PVD through EXTI Line detection DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line DCD FLASH_IRQHandler ; FLASH DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line0 DCD EXTI1_IRQHandler ; EXTI Line1 DCD EXTI2_IRQHandler ; EXTI Line2 DCD EXTI3_IRQHandler ; EXTI Line3 DCD EXTI4_IRQHandler ; EXTI Line4 DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0 DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1 DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2 DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3 DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4 DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5 DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6 DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s DCD CAN1_TX_IRQHandler ; CAN1 TX DCD CAN1_RX0_IRQHandler ; CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; External Line[9:5]s DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9 DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10 DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD TIM4_IRQHandler ; TIM4 DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD I2C2_EV_IRQHandler ; I2C2 Event DCD I2C2_ER_IRQHandler ; I2C2 Error DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD USART3_IRQHandler ; USART3 DCD EXTI15_10_IRQHandler ; External Line[15:10]s DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12 DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13 DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7 DCD FMC_IRQHandler ; FMC DCD SDIO_IRQHandler ; SDIO DCD TIM5_IRQHandler ; TIM5 DCD SPI3_IRQHandler ; SPI3 DCD UART4_IRQHandler ; UART4 DCD UART5_IRQHandler ; UART5 DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors DCD TIM7_IRQHandler ; TIM7 DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0 DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1 DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2 DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3 DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4 DCD ETH_IRQHandler ; Ethernet DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line DCD CAN2_TX_IRQHandler ; CAN2 TX DCD CAN2_RX0_IRQHandler ; CAN2 RX0 DCD CAN2_RX1_IRQHandler ; CAN2 RX1 DCD CAN2_SCE_IRQHandler ; CAN2 SCE DCD OTG_FS_IRQHandler ; USB OTG FS DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5 DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6 DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7 DCD USART6_IRQHandler ; USART6 DCD I2C3_EV_IRQHandler ; I2C3 event DCD I2C3_ER_IRQHandler ; I2C3 error DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI DCD OTG_HS_IRQHandler ; USB OTG HS DCD DCMI_IRQHandler ; DCMI DCD 0 ; Reserved DCD HASH_RNG_IRQHandler ; Hash and RngDCD FPU_IRQHandler ; FPU__Vectors_End__Vectors_Size EQU __Vectors_End - __Vectors ; 計算中斷向量表的大小AREA |.text|, CODE, READONLY ; 代碼段,|.text| 用于表示由 C 編譯程序產生的代碼段,或用于以某種方式與 C 庫關聯(lián)的代碼段。 CODE類型,只讀 ; 以下開始定義各種中斷,第一個便是復位中斷,順序與上面的終端向量表一致! ; Reset handler Reset_Handler PROC ; 代碼開始,與ENDP成對出現(xiàn) EXPORT Reset_Handler [WEAK] ; 復位中斷,[WEAK]修飾代表其他文件有函數(shù)定義優(yōu)先調用IMPORT SystemInit ; 導入外部函數(shù)SystemInitIMPORT __main ; 導入外部函數(shù)__mainLDR R0, =SystemInitBLX R0 ; 無返回調用SystemInitLDR R0, =__mainBX R0 ; 有返回調用__mainENDP ; 代碼結束,與PROC成對出現(xiàn); Dummy Exception Handlers (infinite loops which can be modified)NMI_Handler PROCEXPORT NMI_Handler [WEAK]B .ENDP HardFault_Handler\PROCEXPORT HardFault_Handler [WEAK]B .ENDP MemManage_Handler\PROCEXPORT MemManage_Handler [WEAK]B .ENDP BusFault_Handler\PROCEXPORT BusFault_Handler [WEAK]B .ENDP UsageFault_Handler\PROCEXPORT UsageFault_Handler [WEAK]B .ENDP SVC_Handler PROCEXPORT SVC_Handler [WEAK]B .ENDP DebugMon_Handler\PROCEXPORT DebugMon_Handler [WEAK]B .ENDP PendSV_Handler PROCEXPORT PendSV_Handler [WEAK]B .ENDP SysTick_Handler PROCEXPORT SysTick_Handler [WEAK]B .ENDP ; 終端向量表的External Interrupts部分。 默認的外部中斷,通常有外部實現(xiàn)。先導出各種符號以供外部使用,然后時默認的定義 Default_Handler PROCEXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] EXPORT TAMP_STAMP_IRQHandler [WEAK] EXPORT RTC_WKUP_IRQHandler [WEAK] EXPORT FLASH_IRQHandler [WEAK] EXPORT RCC_IRQHandler [WEAK] EXPORT EXTI0_IRQHandler [WEAK] EXPORT EXTI1_IRQHandler [WEAK] EXPORT EXTI2_IRQHandler [WEAK] EXPORT EXTI3_IRQHandler [WEAK] EXPORT EXTI4_IRQHandler [WEAK] EXPORT DMA1_Stream0_IRQHandler [WEAK] EXPORT DMA1_Stream1_IRQHandler [WEAK] EXPORT DMA1_Stream2_IRQHandler [WEAK] EXPORT DMA1_Stream3_IRQHandler [WEAK] EXPORT DMA1_Stream4_IRQHandler [WEAK] EXPORT DMA1_Stream5_IRQHandler [WEAK] EXPORT DMA1_Stream6_IRQHandler [WEAK] EXPORT ADC_IRQHandler [WEAK] EXPORT CAN1_TX_IRQHandler [WEAK] EXPORT CAN1_RX0_IRQHandler [WEAK] EXPORT CAN1_RX1_IRQHandler [WEAK] EXPORT CAN1_SCE_IRQHandler [WEAK] EXPORT EXTI9_5_IRQHandler [WEAK] EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK] EXPORT TIM1_UP_TIM10_IRQHandler [WEAK] EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK] EXPORT TIM1_CC_IRQHandler [WEAK] EXPORT TIM2_IRQHandler [WEAK] EXPORT TIM3_IRQHandler [WEAK] EXPORT TIM4_IRQHandler [WEAK] EXPORT I2C1_EV_IRQHandler [WEAK] EXPORT I2C1_ER_IRQHandler [WEAK] EXPORT I2C2_EV_IRQHandler [WEAK] EXPORT I2C2_ER_IRQHandler [WEAK] EXPORT SPI1_IRQHandler [WEAK] EXPORT SPI2_IRQHandler [WEAK] EXPORT USART1_IRQHandler [WEAK] EXPORT USART2_IRQHandler [WEAK] EXPORT USART3_IRQHandler [WEAK] EXPORT EXTI15_10_IRQHandler [WEAK] EXPORT RTC_Alarm_IRQHandler [WEAK] EXPORT OTG_FS_WKUP_IRQHandler [WEAK] EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK] EXPORT TIM8_UP_TIM13_IRQHandler [WEAK] EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK] EXPORT TIM8_CC_IRQHandler [WEAK] EXPORT DMA1_Stream7_IRQHandler [WEAK] EXPORT FMC_IRQHandler [WEAK] EXPORT SDIO_IRQHandler [WEAK] EXPORT TIM5_IRQHandler [WEAK] EXPORT SPI3_IRQHandler [WEAK] EXPORT UART4_IRQHandler [WEAK] EXPORT UART5_IRQHandler [WEAK] EXPORT TIM6_DAC_IRQHandler [WEAK] EXPORT TIM7_IRQHandler [WEAK] EXPORT DMA2_Stream0_IRQHandler [WEAK] EXPORT DMA2_Stream1_IRQHandler [WEAK] EXPORT DMA2_Stream2_IRQHandler [WEAK] EXPORT DMA2_Stream3_IRQHandler [WEAK] EXPORT DMA2_Stream4_IRQHandler [WEAK] EXPORT ETH_IRQHandler [WEAK] EXPORT ETH_WKUP_IRQHandler [WEAK] EXPORT CAN2_TX_IRQHandler [WEAK] EXPORT CAN2_RX0_IRQHandler [WEAK] EXPORT CAN2_RX1_IRQHandler [WEAK] EXPORT CAN2_SCE_IRQHandler [WEAK] EXPORT OTG_FS_IRQHandler [WEAK] EXPORT DMA2_Stream5_IRQHandler [WEAK] EXPORT DMA2_Stream6_IRQHandler [WEAK] EXPORT DMA2_Stream7_IRQHandler [WEAK] EXPORT USART6_IRQHandler [WEAK] EXPORT I2C3_EV_IRQHandler [WEAK] EXPORT I2C3_ER_IRQHandler [WEAK] EXPORT OTG_HS_EP1_OUT_IRQHandler [WEAK] EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK] EXPORT OTG_HS_WKUP_IRQHandler [WEAK] EXPORT OTG_HS_IRQHandler [WEAK] EXPORT DCMI_IRQHandler [WEAK] EXPORT HASH_RNG_IRQHandler [WEAK]EXPORT FPU_IRQHandler [WEAK]WWDG_IRQHandler PVD_IRQHandler TAMP_STAMP_IRQHandler RTC_WKUP_IRQHandler FLASH_IRQHandler RCC_IRQHandler EXTI0_IRQHandler EXTI1_IRQHandler EXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler DMA1_Stream0_IRQHandler DMA1_Stream1_IRQHandler DMA1_Stream2_IRQHandler DMA1_Stream3_IRQHandler DMA1_Stream4_IRQHandler DMA1_Stream5_IRQHandler DMA1_Stream6_IRQHandler ADC_IRQHandler CAN1_TX_IRQHandler CAN1_RX0_IRQHandler CAN1_RX1_IRQHandler CAN1_SCE_IRQHandler EXTI9_5_IRQHandler TIM1_BRK_TIM9_IRQHandler TIM1_UP_TIM10_IRQHandler TIM1_TRG_COM_TIM11_IRQHandler TIM1_CC_IRQHandler TIM2_IRQHandler TIM3_IRQHandler TIM4_IRQHandler I2C1_EV_IRQHandler I2C1_ER_IRQHandler I2C2_EV_IRQHandler I2C2_ER_IRQHandler SPI1_IRQHandler SPI2_IRQHandler USART1_IRQHandler USART2_IRQHandler USART3_IRQHandler EXTI15_10_IRQHandler RTC_Alarm_IRQHandler OTG_FS_WKUP_IRQHandler TIM8_BRK_TIM12_IRQHandler TIM8_UP_TIM13_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler TIM8_CC_IRQHandler DMA1_Stream7_IRQHandler FMC_IRQHandler SDIO_IRQHandler TIM5_IRQHandler SPI3_IRQHandler UART4_IRQHandler UART5_IRQHandler TIM6_DAC_IRQHandler TIM7_IRQHandler DMA2_Stream0_IRQHandler DMA2_Stream1_IRQHandler DMA2_Stream2_IRQHandler DMA2_Stream3_IRQHandler DMA2_Stream4_IRQHandler ETH_IRQHandler ETH_WKUP_IRQHandler CAN2_TX_IRQHandler CAN2_RX0_IRQHandler CAN2_RX1_IRQHandler CAN2_SCE_IRQHandler OTG_FS_IRQHandler DMA2_Stream5_IRQHandler DMA2_Stream6_IRQHandler DMA2_Stream7_IRQHandler USART6_IRQHandler I2C3_EV_IRQHandler I2C3_ER_IRQHandler OTG_HS_EP1_OUT_IRQHandler OTG_HS_EP1_IN_IRQHandler OTG_HS_WKUP_IRQHandler OTG_HS_IRQHandler DCMI_IRQHandler HASH_RNG_IRQHandler FPU_IRQHandler B .ENDPALIGN;******************************************************************************* ; User Stack and Heap initialization 編譯器預處理命令,主要是用來初始化用戶堆棧 ;*******************************************************************************IF :DEF:__MICROLIB ; "DEF"的用法為 :DEF:X 就是說X定義了則為真,否則為假。若定義了__MICROLIB,則將__initial_sp,__heap_base,__heap_limit亦即棧頂?shù)刂?#xff0c;堆始末地址賦予全局屬性,使外部程序可以使用。EXPORT __initial_spEXPORT __heap_baseEXPORT __heap_limitELSE ; 如果沒定義__MICROLIB,則使用默認的C運行時庫IMPORT __use_two_region_memory ; 用于指定存儲器模式為雙段模式,即一部分儲存區(qū)用于棧空間,其他的存儲區(qū)用于堆空間,堆區(qū)空間可以為0,但是,這樣就不能調用malloc()內存分配函數(shù);堆區(qū)空間也可以由存儲器分配,也可以從執(zhí)行環(huán)境中繼承。在匯編代碼中,通過 IMPORT __use_two_region_memory 表明使用雙段模式;在C語言中,通過 #pragma import(__use_two_region_memory)語句表明使用雙段模式。EXPORT __user_initial_stackheap__user_initial_stackheap ; 此處是初始化兩區(qū)的堆棧空間,堆是從由低到高的增長,棧是由高向低生長的,兩個是互相獨立的數(shù)據段,并不能交叉使用。LDR R0, = Heap_Mem ; 保存堆始地址LDR R1, =(Stack_Mem + Stack_Size) ; 保存棧的大小LDR R2, = (Heap_Mem + Heap_Size) ; 保存堆的大小LDR R3, = Stack_Mem ; 保存棧頂指針BX LRALIGN ; 填充字節(jié)使地址對齊ENDIFEND ; END 命令指示匯編器,已到達一個源文件的末尾。;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****參考
總結
以上是生活随笔為你收集整理的ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: ARM 之八 Cortex-M/R 内核
- 下一篇: 排序算法 之四 分类、时间/空间复杂度、
