一、简介
ARM Cortex-M内核的复位启动过程也被称为复位序列(Reset sequence)。ARM Cortex-M内核的复位启动过程与其他大部分CPU不同,也与之前的ARM架构(ARM920T、ARM7TDMI等)不相同。大部分CPU复位后都是从0x00000000处取得第一条指令开始运行的,然而在ARM Cortex-M内核中并不是这样的。其复位序列为:
- 从地址0x0000_0000处取出MSP的初始值;
- 从地址0x0000_0004处取出PC的初始值,然后从这个值对应的地址处取指。事实上,地址0x00000004开始存放的就是默认中断向量表
ARM Cortex-M内核的中断向量表布局情况如下图所示:
注意:中断向量表的位置可以改变,此处是默认情况下的设置。
在ARM Cortex-M内核中,发生异常后,并不是去执行中断向量表中对应位置处的代码,而是将对应位置处的数据存入PC中,然后去此地址处进行取指。简而言之,在ARM Cortex-M的中断向量表中不应该放置跳转指令,而是该放置ISR程序的入口地址。另外还有两个细节问题需要注意:
- 0x00000000处存放的MSP初始值最低三位需要是0;
- 0x00000004处存放的地址最低位必须是1。
第一个问题是因为在ARM上编程,但凡涉及到调用,就需要遵循一套规约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.
简而言之,规约规定,栈任何时候都必须4字节对齐,在调用入口需8字节对齐,而且SP的最低两位在硬件上就被置为0了。
第二个问题与ARM模式与Thumb模式有关。ARM中PC中的地址必须是32位对齐的,其最低两位也被硬件上置0了,故写入PC中的数据最低两位并不代表真实的取址地址。ARM中使用最低一位来判断这条指令是ARM指令还是Thumb指令,若最低位为0,代表ARM指令;若最低位为1,代表Thumb指令。在Cortex-M内核中,并不支持ARM模式,若强行切换到ARM模式会引发一个Hard Fault。
二、启动文件
目前,多数MCU厂商都提供一个启动文件。当然,编程者也可以自己编写启动文件,具体编写要求ARM的网站上都有相关文档进行说明。下面分析一下STM32启动文件startup_stm32f407xx.s,具体看里面的注释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
|
|
;******************** (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 ; Stack Configuration ; Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ;
Stack_Size EQU 0x0800 ; 定义栈大小 ; AREA 命令指示汇编器汇编一个新的代码段或数据段。 AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 代码段名称为STACK,未初始化,允许读写,8字节对齐 Stack_Mem SPACE Stack_Size ; 分配Stack_Size的栈空间,首地址赋给Stack_Mem __initial_sp ; 栈顶指针,全局变量
; Heap Configuration ; Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ;
Heap_Size EQU 0x200
AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ; 堆末底部地址 Heap_Mem SPACE Heap_Size __heap_limit ; 堆界限地址
PRESERVE8 ; 指定当前文件保持堆栈八字节对齐 THUMB ; Thumb命令模式
; Vector Table Mapped to Address 0 at Reset ; 终端向量表 重启时程序从这里运行,必须将该地址映射到0x00000000 AREA RESET, DATA, READONLY ; 代码段名称为RESET,DATA类型,只读 EXPORT __Vectors ; 导出中断向量表地址(供外部可以使用) EXPORT __Vectors_End ; 导出中断向量表结束指针(供外部可以使用) EXPORT __Vectors_Size ; 中断向量表大小(供外部可以使用) __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler
; External Interrupts DCD 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 TIM11 DCD 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 TIM14 DCD 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 Rng DCD FPU_IRQHandler ; FPU __Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors ; 计算中断向量表的大小
AREA |.text|, CODE, READONLY ; 代码段,|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段。 CODE类型,只读 ; 以下开始定义各种中断,第一个便是复位中断,顺序与上面的终端向量表一致! ; Reset handler Reset_Handler PROC ; 代码开始,与ENDP成对出现 EXPORT Reset_Handler [WEAK] ; 复位中断,[WEAK]修饰代表其他文件有函数定义优先调用 IMPORT SystemInit ; 导入外部函数SystemInit IMPORT __main ; 导入外部函数__main
LDR R0, =SystemInit BLX R0 ; 无返回调用SystemInit LDR R0, =__main BX R0 ; 有返回调用__main ENDP ; 代码结束,与PROC成对出现
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] B . ENDP MemManage_Handler\ PROC EXPORT MemManage_Handler [WEAK] B . ENDP BusFault_Handler\ PROC EXPORT BusFault_Handler [WEAK] B . ENDP UsageFault_Handler\ PROC EXPORT UsageFault_Handler [WEAK] B . ENDP SVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDP DebugMon_Handler\ PROC EXPORT DebugMon_Handler [WEAK] B . ENDP PendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDP SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP ; 终端向量表的External Interrupts部分。 默认的外部中断,通常有外部实现。先导出各种符号以供外部使用,然后时默认的定义 Default_Handler PROC
EXPORT 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 .
ENDP
ALIGN
;******************************************************************************* ; User Stack and Heap initialization 编译器预处理命令,主要是用来初始化用户堆栈 ;******************************************************************************* IF :DEF:__MICROLIB ; "DEF"的用法为 :DEF:X 就是说X定义了则为真,否则为假。若定义了__MICROLIB,则将__initial_sp,__heap_base,__heap_limit亦即栈顶地址,堆始末地址赋予全局属性,使外部程序可以使用。 EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE ; 如果没定义__MICROLIB,则使用默认的C运行时库 IMPORT __use_two_region_memory ; 用于指定存储器模式为双段模式,即一部分储存区用于栈空间,其他的存储区用于堆空间,堆区空间可以为0,但是,这样就不能调用malloc()内存分配函数;堆区空间也可以由存储器分配,也可以从执行环境中继承。在汇编代码中,通过 IMPORT __use_two_region_memory 表明使用双段模式;在C语言中,通过 #pragma import(__use_two_region_memory)语句表明使用双段模式。 EXPORT __user_initial_stackheap __user_initial_stackheap ; 此处是初始化两区的堆栈空间,堆是从由低到高的增长,栈是由高向低生长的,两个是互相独立的数据段,并不能交叉使用。
LDR R0, = Heap_Mem ; 保存堆始地址 LDR R1, =(Stack_Mem + Stack_Size) ; 保存栈的大小 LDR R2, = (Heap_Mem + Heap_Size) ; 保存堆的大小 LDR R3, = Stack_Mem ; 保存栈顶指针 BX LR
ALIGN ; 填充字节使地址对齐
ENDIF
END ; END 命令指示汇编器,已到达一个源文件的末尾。
;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE***** |
接下来,看看实际程序运行图如下
其中,SystemInit为ST提供的时钟初始化函数(如果使用了外部RAM,可能还包含外部RAM的配置)
三、系统函数(__main)
ARM自己的编译套件提供了如下三种库(如果使用的是IAR或者GNU则可能有区别)
以下分析,均是基于Cstandardlib来说的,并不是适用于其他两种库。
嵌入式应用程序在调用用户定义的main()函数之前,需要一个被称为"初始化序列"的流程。如下图:
这个初始化序列程序被称为startup code 或者boot code。对于ARM编译套件,这些代码就在ARM编译套件的库中。下面我们以STM32F407VG片子为例,看看其调试时的汇编代码(Keil5中)。直接进调试模式,注意:最好将汇编窗口右键改为assembly mode
接着,就会有如下汇编代码:
0x08000180 0243 DCW 0x0243 ; 小端模式,地址:0x08000243
0x08000182 0800 DCW 0x0800
0x08000184 0243 DCW 0x0243 ; 小端模式,地址:0x08000243
0x08000186 0800 DCW 0x0800
; 从此网上即为中断向量表,与上一节的启动文件中的中断向量表相对应,32位地址,小端模式
; 与初始化序列相同,
__main:
0x08000188 F000F802 BL.W __scatterload (0x08000190) ; 负责把RW/RO输出段从装载域地址复制到运行域地址,并完成了ZI运行域的初始化工作。
0x0800018C F000F83C BL.W __rt_entry (0x08000208) ; 负责初始化堆、栈,完成C库函数的初始化。其会调用一系列初始化函数,最后自动跳转向main()函数。
; 执行完后,R10和R11就被赋给成了下面两个值,Map文件中的symbol:
; Region
TableTable
Table)
; !!!-----------注意:以下注释中括号中的数据为我在调试时的值,自行调试时值可能有变化-----------!!!
; !!!-----------注意:以下注释中非括号中的数据一般为左侧地址或者立即数-----------!!!
__scatterload:
0x08000190 A00A ADR r0,{pc}+0x2C ; 将基于PC相对偏移的地址值(0x080001BC)读取到寄存器r0中。
0x08000192 E8900C00 LDM r0,{r10-r11} ; 将R0对应地址存放的的2个字copy到R10~R11中。即:r10 = 0x000006DC; r11 = 0x000006FC
0x08000196 4482 ADD r10,r10,r0 ; r10 = r10 + r0 = 0x000006DC + 0x080001BC = 0x08000898
0x08000198 4483 ADD r11,r11,r0 ; r11 = r11 + r0 = 0x000006FC + 0x080001BC = 0x080008B8
0x0800019A F1AA0701 SUB r7,r10,#0x01 ; r7 = r10 - 0x01 = 0x08000898 - 0x01 = 0x08000897
__scatterload_null:
0x0800019E 45DA CMP r10,r11 ; 比较r10和r11。实际做r10-r11操作,根据结果修改CPSR中条件标志位的值
0x080001A0 D101 BNE 0x080001A6 ; Z 标志位不等于零时, 跳转到0x080001A6
0x080001A2 F000F831 BL.W __rt_entry (0x08000208) ; 最后一步:在执行完__scatterload_copy和__scatterload_zeroinit后,r10 == r11 ,上面的跳转不成立,执行该句
0x080001A6 F2AF0E09 ADR.W lr,{pc}-0x07 ; 将基于PC相对偏移的地址值(0x0800019F)读取到寄存器lr中。lr = 0x0800019F
0x080001AA E8BA000F LDM r10!,{r0-r3} ; 将R10对应地址存放的的4个双字copy到R0~R3中,!表示执行语句后将地址赋值给r10。r10 = r10 + 4*4 = 0x08000898 + 0x10 = 0x080008A8.
; R0:表示的是程序加载视图的RW区的起始地址(0x080008B8)
; R1:要复制到的运行域地址(RAM中0x20000000)
; R2:要复制的RW数据的个数(0x00000020)
; R3:是__scatterload_copy函数的起始地址0x080001C4
0x080001AE F0130F01 TST r3,#0x01 ;
0x080001B2 BF18 IT NE
0x080001B4 1AFB SUBNE r3,r7,r3
0x080001B6 F0430301 ORR r3,r3,#0x01
0x080001BA 4718 BX r3 ; 跳转到r3,也就是__scatterload_copy函数,开始复制数据
0x080001BC 06DC DCW 0x06DC
0x080001BE 0000 DCW 0x0000
0x080001C0 06FC DCW 0x06FC
0x080001C2 0000 DCW 0x0000
__scatterload_copy: ; 该函数负责将镜像中的RW数据复制到芯片的ARM中
0x080001C4 3A10 SUBS r2,r2,#0x10 ; 循环:R2=R2-0x10,SUBS中S表示把进位结果写入CPSR。
0x080001C6 BF24 ITT CS
0x080001C8 C878 LDMCS r0!,{r3-r6}
0x080001CA C178 STMCS r1!,{r3-r6}
0x080001CC D8FA BHI __scatterload_copy (0x080001C4) ; 循环:条件成立跳转到回去
0x080001CE 0752 LSLS r2,r2,#29
0x080001D0 BF24 ITT CS
0x080001D2 C830 LDMCS r0!,{r4-r5}
0x080001D4 C130 STMCS r1!,{r4-r5}
0x080001D6 BF44 ITT MI
0x080001D8 6804 LDRMI r4,[r0,#0x00]
0x080001DA 600C STRMI r4,[r1,#0x00]
0x080001DC 4770 BX lr ; 这里返回到lr = 0x0800019F,即在此调用__scatterload_null。会继续将R10对应地址存放的的4个双字copy到R0~R3中,但是,此时的R10由于之前的一次调用,其值已经增加
; R0:是程序加载视图的RW区的起始地址(0x080008D8 = 0x080008B8 + 0x20 上面执行后面的部分)
; R1:是要输出的执行视图的RW区的地址(RAM中 0x20000020)
; R2:要复制的RW数据的个数(0x00000660)
; R3:是__scatterload_zeroinit函数的起始地址0x080001E0
; 接下来就会跳转到__scatterload_zeroinit
0x080001DE 0000 MOVS r0,r0
__scatterload_zeroinit:
0x080001E0 2300 MOVS r3,#0x00
0x080001E2 2400 MOVS r4,#0x00
0x080001E4 2500 MOVS r5,#0x00
0x080001E6 2600 MOVS r6,#0x00
0x080001E8 3A10 SUBS r2,r2,#0x10 ; 循环:
0x080001EA BF28 IT CS
0x080001EC C178 STMCS r1!,{r3-r6}
0x080001EE D8FB BHI 0x080001E8 ; 循环:条件成立跳转到回去
0x080001F0 0752 LSLS r2,r2,#29
0x080001F2 BF28 IT CS
0x080001F4 C130 STMCS r1!,{r4-r5}
0x080001F6 BF48 IT MI
0x080001F8 600B STRMI r3,[r1,#0x00]
0x080001FA 4770 BX lr ; 这里返回到lr = 0x0800019F,即在此调用__scatterload_null。这次回去后r10 == r11.回去后会跳转到__rt_entry
__rt_lib_init:
0x080001FC B51F PUSH {r0-r4,lr}
__rt_lib_init_fp_1:
0x080001FE F000FB45 BL.W _fp_init (0x0800088C)
__rt_lib_init_alloca_1:
0x08000202 BD1F POP {r0-r4,pc}
__rt_lib_shutdown:
0x08000204 B510 PUSH {r4,lr}
__rt_lib_shutdown_cpp_1:
0x08000206 BD10 POP {r4,pc}
__rt_entry: ; 在执行完__scatterload后,会紧接着执行该函数
0x08000208 F000F831 BL.W __user_setup_stackheap (0x0800026E) ; 设置堆栈的函数,该函数中会调用由用户实现的__user_initial_stackheap函数,如第二节的启动文件中最后的代码即为用户实现的堆栈初始化该函数
0x0800020C 4611 MOV r1,r2
__rt_entry_li:
0x0800020E F7FFFFF5 BL.W __rt_lib_init (0x080001FC) ; 初始化C库
__rt_entry_main:
0x08000212 F000FA3B BL.W main (0x0800068C) ; 跳转到C的main函数
0x08000216 F000F84F BL.W exit (0x080002B8) ; 如果main返回,则执行该函数,结束程序运行
__rt_exit:
0x0800021A B403 PUSH {r0-r1}
__rt_exit_ls:
0x0800021C F7FFFFF2 BL.W __rt_lib_shutdown (0x08000204)
__rt_exit_exit:
0x08000220 BC03 POP {r0-r1}
0x08000222 F000F857 BL.W _sys_exit (0x080002D4)
0x08000226 0000 MOVS r0,r0
0x08000226 0000 MOVS r0,r0
; 此处往下,就是定义的各个中断向量的实现代码,第一个就是复位中断,程序就是从复位中断开始执行的
Reset_Handler:
0x08000228 4809 LDR r0,[pc,#36] ; @0x08000250
0x0800022A 4780 BLX r0
0x0800022C 4809 LDR r0,[pc,#36] ; @0x08000254
0x0800022E 4700 BX r0
NMI_Handler:
0x08000230 E7FE B NMI_Handler (0x08000230)
HardFault_Handler:
0x08000232 E7FE B HardFault_Handler (0x08000232)
MemManage_Handler:
0x08000234 E7FE B MemManage_Handler (0x08000234)
BusFault_Handler:
0x08000236 E7FE B BusFault_Handler (0x08000236)
UsageFault_Handler:
0x08000238 E7FE B UsageFault_Handler (0x08000238)
SVC_Handler:
0x0800023A E7FE B SVC_Handler (0x0800023A)
DebugMon_Handler:
0x0800023C E7FE B DebugMon_Handler (0x0800023C)
PendSV_Handler:
0x0800023E E7FE B PendSV_Handler (0x0800023E)
SysTick_Handler:
0x08000240 E7FE B SysTick_Handler (0x08000240)
Default_Handler:
0x08000242 E7FE B Default_Handler (0x08000242)
__user_initial_stackheap:
0x08000244 4804 LDR r0,[pc,#16] ; @0x08000258
0x08000246 4905 LDR r1,[pc,#20] ; @0x0800025C
0x08000248 4A05 LDR r2,[pc,#20] ; @0x08000260
0x0800024A 4B06 LDR r3,[pc,#24] ; @0x08000264
0x0800024C 4770 BX lr
0x0800024E 0000 DCW 0x0000
0x08000250 0621 DCW 0x0621
0x08000252 0800 DCW 0x0800
0x08000254 0189 DCW 0x0189
0x08000256 0800 DCW 0x0800
0x08000258 0080 DCW 0x0080
0x0800025A 2000 DCW 0x2000
0x0800025C 0680 DCW 0x0680
0x0800025E 2000 DCW 0x2000
0x08000260 0280 DCW 0x0280
0x08000262 2000 DCW 0x2000
0x08000264 0280 DCW 0x0280
0x08000266 2000 DCW 0x2000
__use_two_region_memory:
0x08000268 4770 BX lr
__rt_heap_escrow$2region:
0x0800026A 4770 BX lr
__rt_heap_expand$2region:
0x0800026C 4770 BX lr
__user_setup_stackheap:
0x0800026E 4675 MOV r5,lr
0x08000270 F000F82C BL.W __user_libspace (0x080002CC)
0x08000274 46AE MOV lr,r5
0x08000276 0005 MOVS r5,r0
0x08000278 4669 MOV r1,sp
0x0800027A 4653 MOV r3,r10
0x0800027C F0200007 BIC r0,r0,#0x07
0x08000280 4685 MOV sp,r0
0x08000282 B018 ADD sp,sp,#0x60
0x08000284 B520 PUSH {r5,lr}
0x08000286 F7FFFFDD BL.W __user_initial_stackheap (0x08000244)
0x0800028A E8BD4020 POP {r5,lr}
0x0800028E F04F0600 MOV r6,#0x00
0x08000292 F04F0700 MOV r7,#0x00
0x08000296 F04F0800 MOV r8,#0x00
0x0800029A F04F0B00 MOV r11,#0x00
0x0800029E F0210107 BIC r1,r1,#0x07
0x080002A2 46AC MOV r12,r5
0x080002A4 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002A8 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002AC E8AC09C0 STM r12!,{r6-r8,r11}
0x080002B0 E8AC09C0 STM r12!,{r6-r8,r11}
0x080002B4 468D MOV sp,r1
0x080002B6 4770 BX lr
exit:
0x080002B8 B510 PUSH {r4,lr}
0x080002BA 4604 MOV r4,r0
0x080002BC F3AF8000 NOP.W
0x080002C0 4620 MOV r0,r4
0x080002C2 E8BD4010 POP {r4,lr}
0x080002C6 F7FFBFA8 B.W __rt_exit (0x0800021A)
0x080002CA 0000 MOVS r0,r0
__user_libspace:
0x080002CC 4800 LDR r0,[pc,#0] ; @0x080002D0
0x080002CE 4770 BX lr
0x080002D0 0020 DCW 0x0020
0x080002D2 2000 DCW 0x2000
_sys_exit:
0x080002D4 4901 LDR r1,[pc,#4] ; @0x080002DC
0x080002D6 2018 MOVS r0,#0x18
0x080002D8 BEAB BKPT 0xAB
0x080002DA E7FE B 0x080002DA
0x080002DC 0026 DCW 0x0026
0x080002DE 0002 DCW 0x0002