stm32 启动过程 以及中段重定位

本文深入解析ARM处理器的启动过程,包括启动文件结构、段定义、内存分配、堆栈初始化及中断向量表设置等关键步骤。揭示SPACE与DCD指令的区别,及如何通过伪指令AREA定义代码段和数据段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

启动文件:
startup_stm32f40_41xxx.s

Stack_Size      EQU     0x00000400

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

SPACE和DCD的区别在于:
SPACE和DCD的功能类似,SPACE申请一片内存空间,DCD申请一个字(32bit)的内存空间。
SPACE和DCD的区别在于,SPACE申请空间但不赋初值,DCD申请一个字的空间,并赋初值。

AREA

语法格式:
AREA 段名 属性1 ,属性2 ,……
AREA伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“|”括起来,如:|1_test| 。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:
— CODE 属性:用于定义代码段,默认为READONLY 。
— DATA 属性:用于定义数据段,默认为READWRITE 。
— READONLY 属性:指定本段为只读,代码段默认为READONLY 。
— READWRITE 属性:指定本段为可读可写,数据段的默认属性为READWRITE 。
— ALIGN 属性:使用方式为ALIGN表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。
— COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:
AREA Init ,CODE ,READONLY ; 该伪指令定义了一个代码段,段名为Init ,属性为只读。

 NOINIT

指示数据节未初始化,或初始化为零。 它只包含空间保留指令 SPACE 或初始化值为零的DCB、DCD、DCDU、DCQ、DCQU、DCW 或 DCWU。 您可以在链接时决定某区域是未初始化还是初始化为零(请参阅《RealView 编译工具链接器和实用程序指南》中的第 3 章 使用基本链接器功能)

设置初始SP
设置初始PC=Reset_Handler
设置向量表入口地址,并初始化向量表
调用SystemInit,把系统时钟配制成72M,SystemInit在库文件system_stm32f10.c定义
调转到标号_main,最终来到C程序文件


; Reset handler
Reset_Handler    PROC    // reset_hander子程序
                 EXPORT  Reset_Handler             [WEAK]   //供其他模块调用
        IMPORT  SystemInit      //其他模块供自己调用
        IMPORT  __main       //同上

                 LDR     R0, =SystemInit   //把SystemInit 的地址加载到寄存器R0。
                 BLX     R0      //程序跳转到R0 中的地址执行程序,之后系统的时钟就被设置成72MHZ
                 LDR     R0, =__main   //把_main 的地址加载到寄存器R0
                 BX      R0    //程序跳转到R0 中的地址执行程序,执行完毕之后就去到我们熟知的C 世界
                 ENDP   //表示子程序的结束
  PROC 是子程序定义伪指令。一般用法为: 
1 子程序名PROC NEAR ( 或FAR ) 
2 …… 
3 ret 
4子程序名ENDP 
其中NEAR 和FAR 是属性词。 NEAR属性(段内近调用): 
调用程序和子程序在同一代码段中,只能被相同代码段的其他程序调用。 
FAR属性(段间远调用):调用程序和子程序不在同一代码段中,可以被相同或不同代码段的程序调用。

arm跳转指令详细请参考:https://blog.csdn.net/bytxl/article/details/49883103

上面是对基本的arm指令找到一些解释 可能并不能具体说明启动过程下面在总结下

.S 文件中基本定义了各个段的定义 但是没有具体的实际地址,汇编中各个标号的偏移地址跟编译链接后的地址是两个不同的概念 但是经过编译器的重定向后才编程我们需要的地址

Stack_Size      EQU     0x00000400

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


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

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

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                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

据上图可以看到,_initial_sp栈顶指针和向量表起始地址均未设置。
默认情况下,cortex-m0+内核认为该表位于零地址处,且各向量占用4 字节,因此每个表项占用4 字节
在这里插入图片描述
重定向后:

在这里插入图片描述

中断向量表里的中断跳转地址在编译后就定下来了,SCB->VTOR向量
可动态调整就是让我们的程序运行后还能改变向量的跳转地址。方法就是:在RAM
重建一个中断向量表,在想改变的位置重新赋值新的跳转地址
在这里插入图片描述

要想实现Flash程序跳转和SRAM程序跳转,第一步先设置SCB->VTOR向量,重置中断向量表的起始地址(不是默认的0x00000000了),其中_DSB()为数据同步隔离、_ISB()为指令同步隔离(确保接下来的所有指令都使用新配置)。

在这里插入图片描述
以上为改变中断向量表起始地址,经过_DSB()和_ISB()指令后接下来要给这些地址指针重新赋值,也就是在FlashLoader_ASM()和SRAMLoader_ASM()被调函数中重新配置MSP和PC的值。

以SJ_FLASH_BASE=0x10000000,SJ_SRAM_BASE=0x20000000为例:
在这里插入图片描述

以上两个函数均是将SJ_FLASH_BASE或SJ_SRAM_BASE地址处存储的值赋给SP
,再将地址值偏移4字节后得到的地址里存储的值给PC

上面过程类似启动文件中中断向量表里提到的Reset_Handler,也是先给_initial_sp
栈顶指针赋值中断向量表起始地址,然后地址偏移4
字节程序跳转到启动文件后面定义的Reset_Handler函数执行如下:
在这里插入图片描述

区别只不过是_main和SystemInit代表的是入口地址常量,而前者是直接操作地址值

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值