求职笔记 . MCU . Cortex-M启动脚本分析

以下代代码是startup_gd32f3x0.s的text.Reset_Handler代码段

asm
    .section	.text.Reset_Handler
	.weak	Reset_Handler
	.type	Reset_Handler, %function
Reset_Handler:
  ldr   sp, =_estack    /* Atollic update: set stack pointer */
  
/* Call the clock system initialization function.*/
    bl  SystemInit

/* Copy the data segment initializers from flash to SRAM */
  ldr r0, =_sdata
  ldr r1, =_edata
  ldr r2, =_sidata
  movs r3, #0
  b LoopCopyDataInit

CopyDataInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4

LoopCopyDataInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc CopyDataInit
  
/* Zero fill the bss segment. */
  ldr r2, =_sbss
  ldr r4, =_ebss
  movs r3, #0
  b LoopFillZerobss

FillZerobss:
  str  r3, [r2]
  adds r2, r2, #4

LoopFillZerobss:
  cmp r2, r4
  bcc FillZerobss

/* Call static constructors */
    bl __libc_init_array
/* Call the application's entry point.*/
	bl	main

LoopForever:
    b LoopForever

这段代码是Cortex - M微控制器的启动脚本代码,用于完成系统启动时的一系列初始化工作,最终调用应用程序的入口点main函数。以下是对代码各部分的详细分析:

  1. 代码段和函数声明
asm
.section	.text.Reset_Handler 
.weak	Reset_Handler 
.type	Reset_Handler, %function 
Reset_Handler:
  • .section .text.Reset_Handler:指定接下来的代码属于.text.Reset_Handler代码段,通常用于存储复位处理程序。
  • .weak Reset_Handler:将Reset_Handler声明为弱符号。这意味着如果在其他地方定义了同名的强符号,链接器会使用强符号。
  • .type Reset_Handler, %function:声明Reset_Handler为一个函数。
  • Reset_Handler::定义复位处理程序的入口标签。
  1. 设置栈指针
asm
ldr   sp, =_estack    /* Atollic update: set stack pointer */
  • ldr sp, =_estack:将栈顶地址_estack加载到栈指针sp中。栈指针是微控制器运行时非常重要的寄存器,用于管理函数调用和局部变量的存储。
  1. 调用系统时钟初始化函数
asm
/* Call the clock system initialization function.*/ 
bl  SystemInit
  • bl SystemInit:调用SystemInit函数,该函数通常用于初始化系统时钟。正确的时钟配置对于微控制器的正常运行至关重要,它会影响到外设的工作频率和定时。
  1. 复制数据段初始化值到SRAM
asm
/* Copy the data segment initializers from flash to SRAM */ 
ldr r0, =_sdata 
ldr r1, =_edata 
ldr r2, =_sidata 
movs r3, #0 
b LoopCopyDataInit 
 
CopyDataInit: 
  ldr r4, [r2, r3] 
  str r4, [r0, r3] 
  adds r3, r3, #4 
 
LoopCopyDataInit: 
  adds r4, r0, r3 
  cmp r4, r1 
  bcc CopyDataInit
  • _sdata:数据段在SRAM中的起始地址。
  • _edata:数据段在SRAM中的结束地址。
  • _sidata:数据段在闪存中的起始地址。
  • 这段代码通过循环将数据段的初始化值从闪存复制到SRAM中。每次复制4个字节(一个字),直到复制完整个数据段。
  1. 清零bss段
asm
/* Zero fill the bss segment. */ 
ldr r2, =_sbss 
ldr r4, =_ebss 
movs r3, #0 
b LoopFillZerobss 
 
FillZerobss: 
  str  r3, [r2] 
  adds r2, r2, #4 
 
LoopFillZerobss: 
  cmp r2, r4 
  bcc FillZerobss
  • _sbss:bss段在SRAM中的起始地址。
  • _ebss:bss段在SRAM中的结束地址。
  • 这段代码通过循环将bss段的内存清零。bss段用于存储未初始化的全局变量和静态变量,清零操作确保这些变量在程序开始时的值为零。
  1. 调用静态构造函数
asm
/* Call static constructors */ 
bl __libc_init_array
  • bl __libc_init_array:调用__libc_init_array函数,该函数用于执行静态构造函数。静态构造函数通常用于初始化全局对象。
  1. 调用应用程序入口点
asm
/* Call the application's entry point.*/ 
bl	main
  • bl main:调用应用程序的入口点main函数,开始执行应用程序的主要逻辑。
  1. 无限循环
asm
LoopForever: 
    b LoopForever
  • 如果main函数返回,程序会进入这个无限循环,避免程序失控。

综上所述,这段启动脚本代码完成了栈指针设置、时钟初始化、数据段复制、bss段清零、静态构造函数调用等初始化工作,最终启动应用程序。

求职笔记 . MCU. 链接脚本
求职笔记 . Cortex-M 简述