剖析startup_stm32f407xx.s文件

剖析startup_stm32f407xx.s文件

感谢gcc编译环境下ARM汇编语法(伪指令)提供的相关ARM伪指令功能的介绍,本文据此详细介绍STM32F407芯片所使用的startup_stm32f407xx.s文件

文件头注释

文件开头是关于文件的描述,先粘过来后面解释:

/**
  ******************************************************************************
  * @file      startup_stm32f407xx.s
  * @author    MCD Application Team
  * @brief     STM32F407xx Devices vector table for GCC based toolchains. 
  *            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 Cortex-M4 processor is in Thread mode,
  *            priority is Privileged, and the Stack is set to Main.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2017 STMicroelectronics</center></h2>
  *
  * 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.
  *
  ******************************************************************************
  */

开头部分描述了文件的用途及版权声明:基于GCC编译链的STM32F407xx设备中断向量表,主要描述了

  • 初始SP,PC寄存器的初始值。
  • PC的初始值即Reset_Handler
  • 设置中断向量表入口地址,并用异常地址初始化向量表。 向量表里面保存的是异常响应的时候服务例程的入口地址。STM32把向量表放在0地址开始的code区。
  • 转到C库的__main(最后调用mian())
  • 复位CortexM4之后,处理器处于线程模式,特权优先级。

定义

继续往下看

  .syntax unified
  .cpu cortex-m4
  .fpu softvfp
  .thumb

.global  g_pfnVectors
.global  Default_Handler
  • .syntax unified 是一个指示,默认值divided(分裂的) 旧样式,下面的指令使用ARM和THUMB各自独立的语法。unified (统一的)新样式,下面的指令使用ARM和THUMB通用格式。
  • .cpu表示后面用到的CPU平台为cortex-M4
  • .fpu表示后面使用的是软浮点,软浮点即Soft-float,浮点单元即VFP,(vector floating-point),相关资料可据此查询
  • .thumb使用thumb模式等价于.code 16;gcc -mthumb
  • .global定义了全局符号(symbol),.global使该符号对.ld(连接文件)可见。g_pfnVectors即中断向量表,Default_Handler我们这里先不解释,后面再看

继续往下看:

/* start address for the initialization values of the .data section. 
defined in linker script */
.word  _sidata
/* start address for the .data section. defined in linker script */  
.word  _sdata
/* end address for the .data section. defined in linker script */
.word  _edata
/* start address for the .bss section. defined in linker script */
.word  _sbss
/* end address for the .bss section. defined in linker script */
.word  _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
  • .word表示了在当前位置放一个word型的值,可以理解为一个变量或者数据定义,这个变量同样对对.ld(连接文件)可见。
  • 可以看到定义的变量包括_sidata;_sdata;_edata;_sbss;_ebss,分别用于表示带初始化值的.data段起始地址,.data段的起始地址,结束地址,.bss段的起始地址,结束地址。注释中SystemInit_ExtMemCtl表示配置外部RAM

启动跳转

继续往下看:

/**
 * @brief  This is the code that gets called when the processor first
 *          starts execution following a reset event. Only the absolutely
 *          necessary set is performed, after which the application
 *          supplied main() routine is called. 
 * @param  None
 * @retval : None
*/

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */  
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */  
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
    bl __libc_init_array
/* Call the application's entry point.*/
  bl  main
  bx  lr    
.size  Reset_Handler, .-Reset_Handler

逐一解释:

  • 开头注释表明:这部分代码在芯片第一次启动或复位后,需要执行的一些必要的操作,在此之后启动main()函数执行
  • .weak Reset_Handler该伪指令在符号名称的逗号分隔列表上设置弱属性。 如果符号不存在,将创建它们。(弱定义,如果有其他强定义则用强定义替代),即当我们定义一个Reset_Handler函数时,这部分将不起作用
  • .type Reset_Handler, %function(ELF格式下隐含的标识一个段的开始)此伪指令用于设置符号的类型。%function表示符号是函数名
  • 后面就是汇编函数的定义了,一步步看:
    • Reset_Handler:LDR指令用于从内存中将一个32位的字读取到指令中的目标寄存器中,即将_estack栈底赋值给SP,SP寄存器是指的是堆栈指针寄存器,将R1寄存器赋值为0,B指令跳转至CopyDataInit函数,通过函数名可以看出,这里是对数据进行初始化。
    • LoopCopyDataInit:将_sdata赋值给R0,将_edata赋值给R3,将R0和R1的相加结果给R2,注意R1这里是0,即flash的0起始位置?比较R2与R3,如果R2>=R3,则bcc指令执行,进入CopyDataInit函数,否则则将R2置为_sbss并跳转至LoopFillZerobss,之后跳转至SystemInit函数和__libc_init_array中,多说一句__libc_init_array指用了C++代码,所以需要__libc_init_array 来初始化一些东西, 在C++中,全局变量和静态变量的构造函数需要在main函数执行前执行,这些构造函数会放在init_array表中,__libc_init_array函数有调用这些函数的代码
    • 跳转main函数,当main函数执行退出后执行BX LR跳转回LR寄存器,这时候就从main函数跳出来了。
  • .size Reset_Handler, .-Reset_Handler(ELF格式下隐含标识一个段的结束)该指令设置与符号名称关联的大小。字节大小由可使用标签算术的表达式计算得出。 该伪指令通常用于设置功能符号的大小。

默认中断服务函数

继续往下看

/**
 * @brief  This is the code that gets called when the processor receives an 
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None     
 * @retval None       
*/
    .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler
  • 注释说明如果处理器收到一个未预料的中断,将会进入这个死循环中,即Default_Handler
  • .section .text.Reset_Handler这里表示定义的是.text段中的Reset_Handler段,ax表示权限,ax是 allocation execute的缩写,表示该节区可分配并且可执行,progbits是type,详细定义为.section section_name [, “flags”[, %type[,flag_specific_arguments]]] 这里不具体解释。

中断向量表和服务函数

这部分主要是中断向量表的定义,摘抄部分如下:

/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
* 
*******************************************************************************/
   .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors
    
    
g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  /*省略*/
  
  /* External Interrupts */
  .word     WWDG_IRQHandler                   /* Window WatchDog              */                                        
  .word     PVD_IRQHandler                    /* PVD through EXTI Line detection */                        
  .word     TAMP_STAMP_IRQHandler             /* Tamper and TimeStamps through the EXTI line */            
  .word     RTC_WKUP_IRQHandler               /* RTC Wakeup through the EXTI line */                      
  .word     FLASH_IRQHandler                  /* FLASH                        */                                          
  .word     RCC_IRQHandler                    /* RCC                          */                                            
  /*省略*/                
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler. 
* As they are weak aliases, any function with the same name will override 
* this definition.
* 
*******************************************************************************/
   .weak      NMI_Handler
   .thumb_set NMI_Handler,Default_Handler
  
   .weak      HardFault_Handler
   .thumb_set HardFault_Handler,Default_Handler
  
   /*省略*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

  • 注释部分,表明中断向量表需要在物理地址0x00000000的位置上,如果是IAP当然可以通过程序后续配置调整地址大小,但是第一次启动必然要从0开始
  • .section .isr_vector,"a",%progbits定义中断向量段和它的类型,a表示可分配,%progbits表示段内包含数据。
  • .type g_pfnVectors, %object段符号名为g_pfnVectors,%object表示符号为数据对象。
  • .size g_pfnVectors, .-g_pfnVectors表示g_pfnVectors的大小是从当前位置-定义位置。
  • .word _estack在当前位置放置一个word型的值,这个值为_estack;后面同理。
  • .thumb_set NMI_Handler,Default_Handler等效于.set指令,因为它创建了一个符号,该符号是另一个符号的别名(可能尚未定义)。 该指令还具有添加的属性,因为它以.thumb_func指令相同的方式将别名符号标记为thumb函数入口点。即NMI_Handler默认用Default_Handler替代,但同时NMI_Handler还是个若引用,因此当我们在程序里定义了这个中断服务函数的时候,就会替代这里。

总结

以上即整个.S文件的全部解析,总结一下即定义了中断向量表及相关DATA段,BSS段的地址位置定义。之后定义了启动函数(类似__main)和默认中断函数Default_Handler,而在启动函数中只对相应地址值进行了赋值,并未有更复杂的处理。

posted @ 2021-05-27 21:22  回归的世界线  阅读(2714)  评论(0编辑  收藏  举报