起始和STM32的启动过程是一样的
EQU:给数字常量取一个符号名,相当于C语言中的define
AREA:指示汇编程序汇编新的代码或数据段。
SPACE:分配内存空间
PRESERVE8:当前文件堆栈需按照8字节对齐
EXPORT:声明一个标号具有全局属性,可被外部的文件使用
DCD:以字为单位分配内存,要求4字节对齐,并要求初始化这些内存
PROC:定义子程序,与ENDP成对使用,表示子程序结束
MSR:Move to Special register from Register. 恢复到特殊寄存器
WEAK:弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
IMPORT:声明标号来自外部文件,跟C语言中的EXTERN关键字类似
B:跳转到一个标号ALIGN编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4字节对齐。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
END:到达文件的末尾,文件结束
IF,ELSE,ENDIF:汇编条件分支语句,跟C语言的if else类似
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
EQU 类似C语言的define
Stack_Size EQU 0x800,表示Stack_Size 为0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
用AREA指令定义一个段,代码节或数据节。说明所定义段的相关属性。(说明段的名字,段的属性)
NOINIT 不初始化
READWRITE 可读写
ALIGN=3 按照8*2^3 =64字节对齐
Stack_Mem SPACE Stack_Size
Stack_Mem 分配内存空间大小为 0x00000400
__initial_sp 表示Stack_Mem 空间的结束地址。栈顶地址,栈是由高向低生长的
Heap_Size EQU 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
同理heap空间的申请也是一样的,__heap_base表示起始地址,__heap_limit表示结束地址
在编译后mmp文件中有记录内存地址
HEAP 0x20012e10 Section 1024 startup_gd32f450_470.o(HEAP)
Heap_Mem 0x20012e10 Data 1024 startup_gd32f450_470.o(HEAP)
STACK 0x20013210 Section 1024 startup_gd32f450_470.o(STACK)
Stack_Mem 0x20013210 Data 1024 startup_gd32f450_470.o(STACK)
__initial_sp 0x20013610 Data 0 startup_gd32f450_470.o(STACK)
0x20013210 - 0x20012e10 = 0x400
0x20013610 - 0x20013210 = 0x400
/* reset Vector Mapped to at Address 0 */
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
一样的做法申请一块区域 RESET 数据段,只读。内存是可读可写的,所以只读指的是FLASH上的空间,原本指的是ROM。
EXPORT:声明一个标号具有全局属性,可被外部的文件使用
三个声明部分不占用空间
下面接着是 RESET 空间向量,按照顺序排列
DCD:以字为单位分配内存,要求4字节对齐,并要求初始化这些内存
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
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
; /* external interrupts handler */
DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer
DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect
DCD TAMPER_STAMP_IRQHandler ; 18:Tamper and TimeStamp through EXTI Line detect
DCD RTC_WKUP_IRQHandler ; 19:RTC Wakeup through EXTI Line
DCD FMC_IRQHandler ; 20:FMC
DCD RCU_CTC_IRQHandler ; 21:RCU and CTC
DCD EXTI0_IRQHandler ; 22:EXTI Line 0
DCD __initial_sp ; Top of Stack 排在第一位
DCD Reset_Handler ; Reset Handler 排在第二位
一次排列成一张表
__initial_sp ,Reset_Handler 这些标号都是地址。
__Vectors 开头 以__Vectors_End 结尾
__Vectors_Size EQU __Vectors_End - __Vectors
接着是 申请一块空间
AREA |.text|, CODE, READONLY
;/* reset Handler */
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
PROC:定义子程序,与ENDP成对使用,表示子程序结束
同样是定义了只读FLASH空间,标号为Reset_Handler ,并且可以被外部引用
IMPORT:声明标号来自外部文件,跟C语言中的EXTERN关键字类似
然后依次执行 外部函数 SystemInit,__main(由MDK环境自动生成)
SystemInit 一般做时钟配置
__main 一般是配置C语言运行环境,堆栈的初始化
;/* dummy Exception Handlers */
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
上面的意思是定义Vectors对应处理函数地址。[WEAK]表示如果外部有定义则优先链接外部的函数。
B .是循环的意思,如果没有定义就会无限循环。
可以看到在Default_Handler PROC 之前都是会无限循环的,也就是说如果没写对应的函数就会卡在循环中。
;/* reset Handler */
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
;/* dummy Exception Handlers */
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler\
PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler\
PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
; /* external interrupts handler */
EXPORT WWDGT_IRQHandler [WEAK]
EXPORT LVD_IRQHandler [WEAK]
EXPORT TAMPER_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT RCU_CTC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA0_Channel0_IRQHandler [WEAK]
EXPORT DMA0_Channel1_IRQHandler [WEAK]
EXPORT DMA0_Channel2_IRQHandler [WEAK]
EXPORT DMA0_Channel3_IRQHandler [WEAK]
EXPORT DMA0_Channel4_IRQHandler [WEAK]
EXPORT DMA0_Channel5_IRQHandler [WEAK]
EXPORT DMA0_Channel6_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT CAN0_TX_IRQHandler [WEAK]
EXPORT CAN0_RX0_IRQHandler [WEAK]
EXPORT CAN0_RX1_IRQHandler [WEAK]
EXPORT CAN0_EWMC_IRQHandler [WEAK]
EXPORT EXTI5_9_IRQHandler [WEAK]
EXPORT TIMER0_BRK_TIMER8_IRQHandler [WEAK]
EXPORT TIMER0_UP_TIMER9_IRQHandler [WEAK]
EXPORT TIMER0_TRG_CMT_TIMER10_IRQHandler [WEAK]
EXPORT TIMER0_Channel_IRQHandler [WEAK]
EXPORT TIMER1_IRQHandler [WEAK]
EXPORT TIMER2_IRQHandler [WEAK]
EXPORT TIMER3_IRQHandler [WEAK]
EXPORT I2C0_EV_IRQHandler [WEAK]
EXPORT I2C0_ER_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT SPI0_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT USART0_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT EXTI10_15_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT USBFS_WKUP_IRQHandler [WEAK]
EXPORT TIMER7_BRK_TIMER11_IRQHandler [WEAK]
EXPORT TIMER7_UP_TIMER12_IRQHandler [WEAK]
EXPORT TIMER7_TRG_CMT_TIMER13_IRQHandler [WEAK]
EXPORT TIMER7_Channel_IRQHandler [WEAK]
EXPORT DMA0_Channel7_IRQHandler [WEAK]
EXPORT EXMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIMER4_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT UART3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT TIMER5_DAC_IRQHandler [WEAK]
EXPORT TIMER6_IRQHandler [WEAK]
EXPORT DMA1_Channel0_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT ENET_IRQHandler [WEAK]
EXPORT ENET_WKUP_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_EWMC_IRQHandler [WEAK]
EXPORT USBFS_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT USART5_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT USBHS_EP1_Out_IRQHandler [WEAK]
EXPORT USBHS_EP1_In_IRQHandler [WEAK]
EXPORT USBHS_WKUP_IRQHandler [WEAK]
EXPORT USBHS_IRQHandler [WEAK]
EXPORT DCI_IRQHandler [WEAK]
EXPORT TRNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
EXPORT UART6_IRQHandler [WEAK]
EXPORT UART7_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT SPI4_IRQHandler [WEAK]
EXPORT SPI5_IRQHandler [WEAK]
EXPORT TLI_IRQHandler [WEAK]
EXPORT TLI_ER_IRQHandler [WEAK]
EXPORT IPA_IRQHandler [WEAK]
最后初始化堆栈
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap PROC
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ENDP
ALIGN
ENDIF
END
GD32F4xx_Firmware_Library\CMSIS\GD\GD32F4xx\Source\system_gd32f4xx.c
一下是GD32标准库中包含的SystemInit函数
void SystemInit (void)
{
/* FPU settings */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCU clock configuration to the default reset state */
/* Set IRC16MEN bit */
RCU_CTL |= RCU_CTL_IRC16MEN;
while(0U == (RCU_CTL & RCU_CTL_IRC16MSTB)){
}
RCU_MODIFY(0x50);
RCU_CFG0 &= ~RCU_CFG0_SCS;
/* Reset HXTALEN, CKMEN and PLLEN bits */
RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);
/* Reset HSEBYP bit */
RCU_CTL &= ~(RCU_CTL_HXTALBPS);
/* Reset CFG0 register */
RCU_CFG0 = 0x00000000U;
/* wait until IRC16M is selected as system clock */
while(0 != (RCU_CFG0 & RCU_SCSS_IRC16M)){
}
/* Reset PLLCFGR register */
RCU_PLL = 0x24003010U;
/* Disable all interrupts */
RCU_INT = 0x00000000U;
/* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings */
system_clock_config();
}
执行外SystemInit 后跳转到__main(),然后跳转到用户执行的主函数中。