栈空间
- 栈(Stack):栈是一种后进先出(LIFO)的数据结构,用于存储函数调用时的局部变量、返回地址和寄存器的值。启动文件会定义栈的大小,并将栈指针初始化为栈顶地址。在函数调用时,栈指针会自动调整,以分配和释放内存空间。例如,当一个函数被调用时,返回地址和局部变量会被压入栈中;函数返回时,这些数据会从栈中弹出。


Stack_Size EQU 0x00000400
EQU
属于汇编伪指令,其功能是定义一个常量。在这里,Stack_Size
被定义为**0x00000400
**,换算成十进制就是 1024。这意味着栈的大小为 1024 字节。
AREA STACK, NOINIT, READWRITE, ALIGN=3
- **
AREA
**也是汇编伪指令,用于定义一个存储区域。 - **
STACK
**是该存储区域的名称,表明这个区域是用来作为栈空间的。 - **
NOINIT
**表示此区域无需进行初始化操作,也就是在程序启动时,不会对这个区域的内存赋初值。 - **
READWRITE
**说明这个区域是可读写的,程序能够对其进行读写操作。 - **
ALIGN=3
**意味着该存储区域的起始地址要按照 2的3次方=8 字节对齐。地址对齐有助于提高内存访问的效率。
Stack_Mem SPACE Stack_Size
Stack_Mem
是这个栈空间的起始标签。SPACE
同样是汇编伪指令,其作用是分配一定大小的内存空间。这里分配的空间大小由Stack_Size
决定,也就是 1024 字节。
__initial_sp
__initial_sp
代表栈顶指针的初始值。因为栈是从高地址向低地址增长的,所以 **__initial_sp
**指向的是栈空间的末尾地址,也就是栈顶。
代码功能
这段代码的主要功能是定义一个大小为 1024 字节、起始地址按 8 字节对齐、可读写且无需初始化的栈空间,并且确定了栈顶指针的初始位置。在程序启动时,栈指针会被设置为 __initial_sp
,后续函数调用、局部变量存储等操作都会使用这个栈空间。


堆空间
堆(Heap) :堆用于动态内存分配,例如使用malloc()
和free()
函数。启动文件会定义堆的大小和起始地址,为动态内存分配提供空间。堆的管理通常由操作系统或运行时库负责。
cs
; <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
1. Heap_Size EQU 0x00000200
EQU
是汇编伪指令,用于定义常量。这里将Heap_Size
定义为0x00000200
,换算成十进制是 512。这表明堆的大小为 512 字节。
2. AREA HEAP, NOINIT, READWRITE, ALIGN=3
- **
AREA
**也是汇编伪指令,用于定义一个存储区域。 - **
HEAP
**是该存储区域的名称,意味着这个区域是用来作为堆空间的。 - **
NOINIT
**表示此区域无需进行初始化操作,即程序启动时,不会对该区域的内存赋初值。 - **
READWRITE
**说明这个区域是可读写的,程序能够对其进行读写操作。 - **
ALIGN=3
**表示该存储区域的起始地址要按照 2的3次方=8 字节对齐,地址对齐有助于提高内存访问效率。
3. __heap_base
- **
__heap_base
**是一个标签,代表堆空间的起始地址。
4. Heap_Mem SPACE Heap_Size
Heap_Mem
同样是一个标签,SPACE
是汇编伪指令,用于分配一定大小的内存空间。这里分配的空间大小由Heap_Size
决定,即 512 字节。
5. __heap_limit
- **
__heap_limit
**是一个标签,代表堆空间的结束地址。
6. PRESERVE8
- 这是一个汇编指令,其作用是指示编译器保证代码中的数据和栈的对齐方式为 8 字节对齐。在某些架构中,保持 8 字节对齐能够提高内存访问的性能。
THUMB
- 此指令表明后续的代码会使用 Thumb 指令集进行编译。Thumb 指令集是 ARM 指令集的一种精简版本,它具有更高的代码密度,能够节省存储空间。
代码整体功能
这段代码的主要功能是定义一个大小为 512 字节、起始地址按 8 字节对齐、可读写且无需初始化的堆空间,同时标记出堆空间的起始地址和结束地址。在程序运行过程中,动态内存分配函数(如 malloc
、calloc
等)会使用这个堆空间来分配内存。
初始化向量表
- 向量表的作用:向量表是一个固定地址的数组,存储着各个异常和中断处理函数的入口地址。当异常或中断发生时,处理器会根据异常或中断号从向量表中获取相应的处理函数地址,并跳转到该地址执行。
- 异常和中断处理:启动文件会将向量表初始化为默认的异常和中断处理函数地址。这些处理函数包括复位处理函数、非屏蔽中断(NMI)处理函数、硬故障处理函数等。用户可以根据需要修改这些处理函数的地址,以实现自定义的异常和中断处理逻辑。
cs
; 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
; 其他中断向量...
- 复位处理函数 :**
Reset_Handler
**是复位后执行的第一个函数,它会完成一些必要的初始化工作,然后跳转到主程序。
中断向量表结束标记
cs
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
- 向量表结束标记 :**
__Vectors_End
**表示向量表的结束地址。 - 向量表大小 :**
__Vectors_Size
**计算向量表的大小,用于后续的一些初始化操作。
复位处理函数
cs
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
系统初始化
- 硬件初始化 :启动文件通常会调用一个系统初始化函数,如**
SystemInit()
**,该函数会对芯片的硬件资源进行初始化,包括时钟系统、总线配置、外设等。例如,配置系统时钟源、设置时钟频率、使能外设时钟等。这些初始化操作确保芯片以正确的时钟频率和工作模式运行。 - 全局变量初始化:启动文件会将全局变量和静态变量初始化为其初始值。对于已初始化的全局变量,启动文件会将其从只读存储器(ROM)复制到随机存取存储器(RAM)中;对于未初始化的全局变量,启动文件会将其所在的 RAM 区域清零。
- 跳转到主程序 :调用**
__main
** 函数,__main
是 C/C++ 程序的入口点,它会完成全局变量的初始化、调用**main()
**函数等操作。程序的主要逻辑通常从这里开始执行。
中断处理函数
cs
; 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
; 其他中断处理函数...
默认处理函数 :这些是默认的中断处理函数,当相应的异常或中断发生时,如果用户没有提供自定义的处理函数,就会执行这些默认函数。默认函数通常是一个无限循环B .
,表示程序会一直停在这里,等待用户进一步处理。