程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

ARM Cortex-M0的程序映像和启动加载过程

balukai 2025-05-23 15:22:42 文章精选 3 ℃

ARM Cortex-M的程序映像从地址0开始,起始处是向量表,其中的每个向量占用4个字节,除了第一个向量保存主栈针MSP初始地址外,其他向量保存的是各种异常处理的跳转地址,例如外部中断IRQ#0的编号是16(从0开始),故IRQ#0的向量地址为16*4=0x40处。Cortex-M0的向量表如下图所示:

程序映像与启动过程密切相关,当处理器复位后,首先将向量表中的第一个字,加载到MSP寄存器中。MSP寄存器的数值是处理器的主栈存储的位置;栈空间用于临时数据的存储,相当于与一个先入先出的缓存,每次执行PUSH(压栈)和POP(出栈)操作时,自动调整MSP寄存器的值。PUSH和POP操作一般用在函数的开始和结尾处,在函数开始时,调用程序使用的寄存器的值被PUSH到栈中,在函数结尾处,栈中的数据被POP恢复回来。Cortex-M处理器的栈操作是基于“满递减”的栈模型:栈指针总是指向最后一个填充到栈空间中的数据,每次PUSH 新的数据后,栈指针的数据会减小,如下图所示:

MSP寄存器地址加载完成后,处理器取出第二个字(复位向量)的数值加载到PC寄存器,即跳转到复位程序执行。程序映像和启动加载过程如下图所示:

对应STM32F030芯片,这一过程是在startup_stm32f030x8.s里面实现,使用的是汇编语言,从代码里面了解整个过程更清晰:

1 分配栈和堆的空间:

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

Heap_Size       EQU     0x00000000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

代码中,Stack_Size和Heap_Size分别指定了栈和堆的空间大小,当程序的栈/堆不够用时,可以修改这里的值,STM32F0默认设置堆空间为0,故malloc调用不能成功;另外将初始栈顶地址命名为__initial_sp,将在向量表中使用。

2 建立向量表

__Vectors       DCD     __initial_sp                   ; Top of Stack
                DCD     Reset_Handler                  ; Reset Handler
                DCD     NMI_Handler                    ; NMI Handler
                DCD     HardFault_Handler              ; Hard Fault Handler
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     SVC_Handler                    ; SVCall Handler
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     PendSV_Handler                 ; PendSV Handler
                DCD     SysTick_Handler                ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler                ; Window Watchdog
                DCD     0                              ; Reserved
                DCD     RTC_IRQHandler                 ; RTC through EXTI Line
                DCD     FLASH_IRQHandler               ; FLASH
                DCD     RCC_IRQHandler                 ; RCC
                DCD     EXTI0_1_IRQHandler             ; EXTI Line 0 and 1
                DCD     EXTI2_3_IRQHandler             ; EXTI Line 2 and 3
                DCD     EXTI4_15_IRQHandler            ; EXTI Line 4 to 15
                DCD     0                              ; Reserved
                DCD     DMA1_Channel1_IRQHandler       ; DMA1 Channel 1
                DCD     DMA1_Channel2_3_IRQHandler     ; DMA1 Channel 2 and Channel 3
                DCD     DMA1_Channel4_5_IRQHandler     ; DMA1 Channel 4 and Channel 5
                DCD     ADC1_IRQHandler                ; ADC1 
                DCD     TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation
                DCD     TIM1_CC_IRQHandler             ; TIM1 Capture Compare
                DCD     0                              ; Reserved
                DCD     TIM3_IRQHandler                ; TIM3
                DCD     TIM6_IRQHandler                ; TIM6
                DCD     0                              ; Reserved
                DCD     TIM14_IRQHandler               ; TIM14
                DCD     TIM15_IRQHandler               ; TIM15
                DCD     TIM16_IRQHandler               ; TIM16
                DCD     TIM17_IRQHandler               ; TIM17
                DCD     I2C1_IRQHandler                ; I2C1
                DCD     I2C2_IRQHandler                ; I2C2
                DCD     SPI1_IRQHandler                ; SPI1
                DCD     SPI2_IRQHandler                ; SPI2
                DCD     USART1_IRQHandler              ; USART1
                DCD     USART2_IRQHandler              ; USART2

__Vectors_End

正如前面的介绍,向量表第1个向量保存的是初始栈顶地址__initial_sp, 芯片复位后将该值加载到MSP寄存器;第2~16个向量是ARM Cortex-M定义异常处理函数地址,第17个向量开始是外部中断处理函数地址,由微控制器厂商定义,上述代码可以看到STM32F0支持的外部中断。

3 将复位向量地址加载到PC寄存器,跳转到程序起始地址(Reset_Handler)开始执行

; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler                 [WEAK]
            		 IMPORT  __main
                 IMPORT  SystemInit  
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

Reset_Handler函数先跳转到SystemInit(位于system_stm32f0xx.c)执行,然后在跳转到__main函数。

4 __main函数由C编译器生成,负责初始化C程序执行所需的运行环境,其中一个工作就是初始化栈/堆空间,调用startup_stm32f030x8.s定义的__user_initial_stackheap函数获取栈/堆空间的地址和大小:

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB

                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit

                 ELSE

                 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

完成C程序执行环境初始化后,就进入到程序员熟悉的main函数执行了。

最近发表
标签列表