小华半导体芯片启动文件详细讲解
本文的源码文件名为:startup_hcf460.S,是一个纯汇编文件。首先要掌握一些基础的汇编语法,主要用到的列表标出(参考stm32相关资料):
https://img-blog.csdnimg.cn/9d1227ccdabc415a90ad88a3d2dd2a98.jpeg因为和stm32不同,华大芯片的sram3区域有点特殊,参考华大芯片用户手册。
启动文件中使用STR指令,STR指令的格式为:
STR{条件}源寄存器,<存储器地址>
STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常
用,丏寻址方式灵活多样,使用方式可参考指令LDR。
指令示例:
STR R0,,#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0, ;将R0中的字数据写入以R1+8为地址的存储器中。”
str r1, ;将r1寄存器的值,传送到地址值为r0的(存储器)内存中
注意分号;在汇编里面是注释的标识,类似C++ 的//。 1. 栈区域
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp 开辟栈的大小为0x00000400(1k B),名字为STACK,NOINIT即不初始化,可读可写,8(23)字节对齐。栈是用于局部变量、函数调用、函数形参等的开销,栈的大小不能超过内部SRAM的大小。如果编写的程序比较大,定义的局部变量很多,那么就需要修改栈的大小。如果某一天,你写的程序出现了莫名奇怪的错误,并进入了硬fault,这时就要考虑下是不是栈不够大,溢出了。EQU:宏定义的伪指令,相当于等于,类似于C中的define。 AREA:告诉汇编器汇编一个新的代码段或者数据段。
STACK表示段名,这个可以任意命名;NOINIT表示不初始化;READWRITE表示可读可写,ALIGN=3,表示按照2^3对齐,即8字节对齐。
SPACE:用于分配一定大小的内存空间,单位为字节。这里指定大小等于Stack_Size。标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址(栈是由高向低生长的)。 2. 堆区域Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit 开辟堆的大小为0x00000200(512字节),名字为HEAP,NOINIT即不初始化,可读可写,8(2^3)字节对齐。__heap_base表示堆的起始地址,__heap_limit表示堆的结束地址(堆是由低向高生长的,与栈的生长方向相反)。堆主要用于动态内存的分配,像malloc()函数申请的内存就在堆上面。
PRESERVE8
THUMB PRESERVE8:指定当前文件的堆栈按照8字节对齐。
THUMB:表示后面指令兼容THUMB指令。
THUBM是ARM以前的指令集,16位。现在Cortex-M系列都使用THUMB-2指令集,THUMB-2是32位的,兼容16位和32位的指令,是THUMB的超集。 3. 向量表
AREA RESET, DATA, READONLY
EXPORT__Vectors
EXPORT__Vectors_End
EXPORT__Vectors_Size 定义一个数据段,名字为RESET,可读,并声明__Vectors、__Vectors_End和__Vectors_Size这3个标号具有全局属性,可供外部的文件调用。 EXPORT:声明一个标号可被外部的文件使用,使标号具有全局属性。如果是IAR编译器,则使用GLOBAL这个指令。当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定ESR的入口地址,内核使用了“向量表查表机制”。这里使用一张向量表,见下表。向量表其实是一个WORD(32位整数)数组,每个下标对应一种异常,该下标元素的值则是该ESR的入口地址。向量表在地址空间中的位置是可以设置的,通过NVIC中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为0。因此,在地址0(即Flash地址0)处必须包含一张向量表,用于初始时的异常分配。要注意的是,这里有个另类:0号类型并不是什么入口地址,而是给出了复位后MSP的初值。
中断向量表可以参考用户手册,注意的是,官网上面已经更新了driver库,用户手册也更新了。 向量表的代码比较长,占据了启动代码的大部分篇幅。__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
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; Peripheral Interrupts
DCD IRQ000_Handler ; IRQ000_Handler
DCD IRQ001_Handler ; IRQ001_Handler
DCD IRQ002_Handler ; IRQ002_Handler
DCD IRQ003_Handler ; IRQ003_Handler
DCD IRQ004_Handler ; IRQ004_Handler
DCD IRQ005_Handler ; IRQ005_Handler
DCD IRQ006_Handler ; IRQ006_Handler
DCD IRQ007_Handler ; IRQ007_Handler
DCD IRQ008_Handler ; IRQ008_Handler
DCD IRQ009_Handler ; IRQ009_Handler
DCD IRQ010_Handler ; IRQ010_Handler
DCD IRQ011_Handler ; IRQ011_Handler
DCD IRQ012_Handler ; IRQ012_Handler
DCD IRQ013_Handler ; IRQ013_Handler
DCD IRQ014_Handler ; IRQ014_Handler
DCD IRQ015_Handler ; IRQ015_Handler
DCD IRQ016_Handler ; IRQ016_Handler
DCD IRQ017_Handler ; IRQ017_Handler
DCD IRQ018_Handler ; IRQ018_Handler
DCD IRQ019_Handler ; IRQ019_Handler
DCD IRQ020_Handler ; IRQ020_Handler
DCD IRQ021_Handler ; IRQ021_Handler
DCD IRQ022_Handler ; IRQ022_Handler
DCD IRQ023_Handler ; IRQ023_Handler
DCD IRQ024_Handler ; IRQ024_Handler
DCD IRQ025_Handler ; IRQ025_Handler
DCD IRQ026_Handler ; IRQ026_Handler
DCD IRQ027_Handler ; IRQ027_Handler
DCD IRQ028_Handler ; IRQ028_Handler
DCD IRQ029_Handler ; IRQ029_Handler
DCD IRQ030_Handler ; IRQ030_Handler
DCD IRQ031_Handler ; IRQ031_Handler
DCD IRQ032_Handler ; IRQ032_Handler
DCD IRQ033_Handler ; IRQ033_Handler
DCD IRQ034_Handler ; IRQ034_Handler
DCD IRQ035_Handler ; IRQ035_Handler
DCD IRQ036_Handler ; IRQ036_Handler
DCD IRQ037_Handler ; IRQ037_Handler
DCD IRQ038_Handler ; IRQ038_Handler
DCD IRQ039_Handler ; IRQ039_Handler
DCD IRQ040_Handler ; IRQ040_Handler
DCD IRQ041_Handler ; IRQ041_Handler
DCD IRQ042_Handler ; IRQ042_Handler
DCD IRQ043_Handler ; IRQ043_Handler
DCD IRQ044_Handler ; IRQ044_Handler
DCD IRQ045_Handler ; IRQ045_Handler
DCD IRQ046_Handler ; IRQ046_Handler
DCD IRQ047_Handler ; IRQ047_Handler
DCD IRQ048_Handler ; IRQ048_Handler
DCD IRQ049_Handler ; IRQ049_Handler
DCD IRQ050_Handler ; IRQ050_Handler
DCD IRQ051_Handler ; IRQ051_Handler
DCD IRQ052_Handler ; IRQ052_Handler
DCD IRQ053_Handler ; IRQ053_Handler
DCD IRQ054_Handler ; IRQ054_Handler
DCD IRQ055_Handler ; IRQ055_Handler
DCD IRQ056_Handler ; IRQ056_Handler
DCD IRQ057_Handler ; IRQ057_Handler
DCD IRQ058_Handler ; IRQ058_Handler
DCD IRQ059_Handler ; IRQ059_Handler
DCD IRQ060_Handler ; IRQ060_Handler
DCD IRQ061_Handler ; IRQ061_Handler
DCD IRQ062_Handler ; IRQ062_Handler
DCD IRQ063_Handler ; IRQ063_Handler
DCD IRQ064_Handler ; IRQ064_Handler
DCD IRQ065_Handler ; IRQ065_Handler
DCD IRQ066_Handler ; IRQ066_Handler
DCD IRQ067_Handler ; IRQ067_Handler
DCD IRQ068_Handler ; IRQ068_Handler
DCD IRQ069_Handler ; IRQ069_Handler
DCD IRQ070_Handler ; IRQ070_Handler
DCD IRQ071_Handler ; IRQ071_Handler
DCD IRQ072_Handler ; IRQ072_Handler
DCD IRQ073_Handler ; IRQ073_Handler
DCD IRQ074_Handler ; IRQ074_Handler
DCD IRQ075_Handler ; IRQ075_Handler
DCD IRQ076_Handler ; IRQ076_Handler
DCD IRQ077_Handler ; IRQ077_Handler
DCD IRQ078_Handler ; IRQ078_Handler
DCD IRQ079_Handler ; IRQ079_Handler
DCD IRQ080_Handler ; IRQ080_Handler
DCD IRQ081_Handler ; IRQ081_Handler
DCD IRQ082_Handler ; IRQ082_Handler
DCD IRQ083_Handler ; IRQ083_Handler
DCD IRQ084_Handler ; IRQ084_Handler
DCD IRQ085_Handler ; IRQ085_Handler
DCD IRQ086_Handler ; IRQ086_Handler
DCD IRQ087_Handler ; IRQ087_Handler
DCD IRQ088_Handler ; IRQ088_Handler
DCD IRQ089_Handler ; IRQ089_Handler
DCD IRQ090_Handler ; IRQ090_Handler
DCD IRQ091_Handler ; IRQ091_Handler
DCD IRQ092_Handler ; IRQ092_Handler
DCD IRQ093_Handler ; IRQ093_Handler
DCD IRQ094_Handler ; IRQ094_Handler
DCD IRQ095_Handler ; IRQ095_Handler
DCD IRQ096_Handler ; IRQ096_Handler
DCD IRQ097_Handler ; IRQ097_Handler
DCD IRQ098_Handler ; IRQ098_Handler
DCD IRQ099_Handler ; IRQ099_Handler
DCD IRQ100_Handler ; IRQ100_Handler
DCD IRQ101_Handler ; IRQ101_Handler
DCD IRQ102_Handler ; IRQ102_Handler
DCD IRQ103_Handler ; IRQ103_Handler
DCD IRQ104_Handler ; IRQ104_Handler
DCD IRQ105_Handler ; IRQ105_Handler
DCD IRQ106_Handler ; IRQ106_Handler
DCD IRQ107_Handler ; IRQ107_Handler
DCD IRQ108_Handler ; IRQ108_Handler
DCD IRQ109_Handler ; IRQ109_Handler
DCD IRQ110_Handler ; IRQ110_Handler
DCD IRQ111_Handler ; IRQ111_Handler
DCD IRQ112_Handler ; IRQ112_Handler
DCD IRQ113_Handler ; IRQ113_Handler
DCD IRQ114_Handler ; IRQ114_Handler
DCD IRQ115_Handler ; IRQ115_Handler
DCD IRQ116_Handler ; IRQ116_Handler
DCD IRQ117_Handler ; IRQ117_Handler
DCD IRQ118_Handler ; IRQ118_Handler
DCD IRQ119_Handler ; IRQ119_Handler
DCD IRQ120_Handler ; IRQ120_Handler
DCD IRQ121_Handler ; IRQ121_Handler
DCD IRQ122_Handler ; IRQ122_Handler
DCD IRQ123_Handler ; IRQ123_Handler
DCD IRQ124_Handler ; IRQ124_Handler
DCD IRQ125_Handler ; IRQ125_Handler
DCD IRQ126_Handler ; IRQ126_Handler
DCD IRQ127_Handler ; IRQ127_Handler
DCD IRQ128_Handler ; IRQ128_Handler
DCD IRQ129_Handler ; IRQ129_Handler
DCD IRQ130_Handler ; IRQ130_Handler
DCD IRQ131_Handler ; IRQ131_Handler
DCD IRQ132_Handler ; IRQ132_Handler
DCD IRQ133_Handler ; IRQ133_Handler
DCD IRQ134_Handler ; IRQ134_Handler
DCD IRQ135_Handler ; IRQ135_Handler
DCD IRQ136_Handler ; IRQ136_Handler
DCD IRQ137_Handler ; IRQ137_Handler
DCD IRQ138_Handler ; IRQ138_Handler
DCD IRQ139_Handler ; IRQ139_Handler
DCD IRQ140_Handler ; IRQ140_Handler
DCD IRQ141_Handler ; IRQ141_Handler
DCD IRQ142_Handler ; IRQ142_Handler
DCD IRQ143_Handler ; IRQ143_Handler
__Vectors_End
__Vectors_SizeEQU __Vectors_End - __Vectors __Vectors为向量表起始地址,__Vectors_End为向量表结束地址,两个相减即可算出向量表大小。向量表从Flash的0地址开始放置,以4个字节为一个单位,地址0存放的是栈顶地址,0x04存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C语言中的函数名就是一个地址。DCD:分配一个或者多个以字为单位的内存,以4字节对齐,并要求初始化这些内存。在向量表中,DCD分配了一堆内存,并且以ESR的入口地址初始化它们。 4. 复位程序
AREA |.text|, CODE, READONLY 定义一个名称为.text的代码段,只读。 Reset_Handler PROC
EXPORTReset_Handler
IMPORTSystemInit
IMPORT__main
SET_SRAM3_WAIT
LDR R0, =0x40050804
MOV R1, #0x77
STR R1,
LDR R0, =0x4005080C
MOV R1, #0x77
STR R1,
LDR R0, =0x40050800
MOV R1, #0x1100
STR R1,
LDR R0, =0x40050804
MOV R1, #0x76
STR R1,
LDR R0, =0x4005080C
MOV R1, #0x76
STR R1,
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP 复位子程序是系统上电后第一个执行的程序,调用System Init函数初始化系统时钟,然后调用C库函数_mian,最终调用main函数进入C语言世界。 WEAK:表示弱定义,如果外部文件优先定义了该标号,则首先引用该标号,如果外部文件没有声明,也不会出错。这里表示复位子程序可以由用户在其他文件中重新实现,这里并不是唯一的。IMPORT:表示该标号来自外部文件,与C语言中的EXTERN关键字类似。这里表示System Init和__main这两个函数均来自外部的文件。