;/*!
; \file startup_gd32e10x.s
; \brief start up file
;
; \version 2017-12-26, V1.0.0, firmware for GD32E10x
;*/
;
;/*
; Copyright (c) 2017, GigaDevice Semiconductor Inc.
;
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without modification,
;are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
; list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright notice,
; this list of conditions and the following disclaimer in the documentation
; and/or other materials provided with the distribution.
; 3. Neither the name of the copyright holder nor the names of its contributors
; may be used to endorse or promote products derived from this software without
; specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
;PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
;OF SUCH DAMAGE.
;*/
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
; 定义栈的大小
Stack_Size EQU 0x00000400
;定义名字为STACK的独立区域,其实就是栈区,栈区没有初始化,可读可写,并且以2^3=8字节对齐
AREA STACK, NOINIT, READWRITE, ALIGN=3
;申请了Stack_Size的空间,也就是0x400 大概 1024的栈空间。(SPACE申请一片内存空间但是不赋值)
Stack_Mem SPACE Stack_Size
;这里的__initial_sp定了一个标签(label),代码当前指令的地址,后面会用DCD赋值,但是赋值什么呢?
;初始化栈顶指针,指向堆栈顶,因为栈是向下增长的,所以栈顶在最下面,堆是向上增长的,所以这里也是堆顶?
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
;定义堆的大小
Heap_Size EQU 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN=3
;定义_heap_base标签,后面会赋值?
__heap_base
Heap_Mem SPACE Heap_Size
;定义__heap_limit标签,后面会赋值?
__heap_limit
;Preserve意思是保留维持,这里的PRESERVE8是字节对齐的关键字,指定当前文件保持堆栈8字节对齐
PRESERVE8
; 告诉编译器使用THUMB指令集,THUMB 必须位于使用新语法的任何Thumb代码之前
THUMB
; /* reset Vector Mapped to at Address 0 */
;复位段,只包含数据,只读,没搞明白这个东西,这个确实是复位段,但是复位段有什么作用,还是只是起了名字而已
AREA RESET, DATA, READONLY
; EXPORT 命令声明一个符号,可由链接器用于解释各个目标和库文件中的符号引用,相当于声明了一个全局变量。 GLOBAL 于 EXPORT相同
;为什么引号EXPORT __Vectors ???
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
;关键字DCD代表申请一个字的空间,后面的函数名即为中断服务函数入口地址。另外中断向量表一般存放在Flash 0地址
;第一行,用DCD申请一个字的空间,然后用后面的表达式(__initial_sp,这其实是一个地址->栈顶指针)初始化
__Vectors DCD __initial_sp ; Top of Stack
;下面是Reset_Handler,复位函数,关于这两个地址,__initial_sp和Reset_Handler,程序上电后,首先加载SP和PC,
;ARM规定从0地址处加载SP,从偏移为4的地址(0x00000004)处加载PC。然后将程序控制权交给程序。我们知道0地址处存放
;__initial_sp???0x00000004地址处存放Reset_Handler,加载PC后,程序跳转到Reset_Handler开始运行。
;后面看下Reset_Handler函数
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
;uC/OS-II中任务切换的时候会调用到,其中做了现场保存,并且在遇到其他中断和SysTick中断冲突时候其缓冲作用
DCD PendSV_Handler ; PendSV Handler
;系统tick中断,想起来有人又疑问Systick中断和Timer中断的区别,喏,这里就是区别,SysTick中断是ARM核心内部中断,
;你那些Timer啥的都是外部中断,Timer当然作用更丰富一些
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_IRQHandler ; 18:Tamper through EXTI Line detect
DCD RTC_IRQHandler ; 19:RTC through EXTI Line
DCD FMC_IRQHandler ; 20:FMC
DCD RCU_CTC_IRQHandler ; 21:RCU and CTC
DCD EXTI0_IRQHandler ; 22:EXTI Line 0
DCD EXTI1_IRQHandler ; 23:EXTI Line 1
DCD EXTI2_IRQHandler ; 24:EXTI Line 2
DCD EXTI3_IRQHandler ; 25:EXTI Line 3
DCD EXTI4_IRQHandler ; 26:EXTI Line 4
DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel0
DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel1
DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel2
DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel3
DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel4
DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel5
DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel6
DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1
DCD CAN0_TX_IRQHandler ; 35:CAN0 TX
DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0
DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1
DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC
DCD EXTI5_9_IRQHandler ; 39:EXTI5 to EXTI9
DCD TIMER0_BRK_TIMER8_IRQHandler ; 40:TIMER0 Break and TIMER8
DCD TIMER0_UP_TIMER9_IRQHandler ; 41:TIMER0 Update and TIMER9
DCD TIMER0_TRG_CMT_TIMER10_IRQHandler ; 42:TIMER0 Trigger and Commutation and TIMER10
DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare
DCD TIMER1_IRQHandler ; 44:TIMER1
DCD TIMER2_IRQHandler ; 45:TIMER2
DCD TIMER3_IRQHandler ; 46:TIMER3
DCD I2C0_EV_IRQHandler ; 47:I2C0 Event
DCD I2C0_ER_IRQHandler ; 48:I2C0 Error
DCD I2C1_EV_IRQHandler ; 49:I2C1 Event
DCD I2C1_ER_IRQHandler ; 50:I2C1 Error
DCD SPI0_IRQHandler ; 51:SPI0
DCD SPI1_IRQHandler ; 52:SPI1
DCD USART0_IRQHandler ; 53:USART0
DCD USART1_IRQHandler ; 54:USART1
DCD USART2_IRQHandler ; 55:USART2
DCD EXTI10_15_IRQHandler ; 56:EXTI10 to EXTI15
DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm
DCD USBFS_WKUP_IRQHandler ; 58:USBFS Wakeup
DCD TIMER7_BRK_TIMER11_IRQHandler ; 59:TIMER7 Break and TIMER11
DCD TIMER7_UP_TIMER12_IRQHandler ; 60:TIMER7 Update and TIMER12
DCD TIMER7_TRG_CMT_TIMER13_IRQHandler ; 61:TIMER7 Trigger and Commutation and TIMER13
DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare
DCD 0 ; 63:Reserved
DCD EXMC_IRQHandler ; 64:EXMC
DCD 0 ; 65:Reserved
DCD TIMER4_IRQHandler ; 66:TIMER4
DCD SPI2_IRQHandler ; 67:SPI2
DCD UART3_IRQHandler ; 68:UART3
DCD UART4_IRQHandler ; 69:UART4
DCD TIMER5_IRQHandler ; 70:TIMER5
DCD TIMER6_IRQHandler ; 71:TIMER6
DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0
DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1
DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2
DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3
DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4
DCD 0 ; 77:Reserved
DCD 0 ; 78:Reserved
DCD CAN1_TX_IRQHandler ; 79:CAN1 TX
DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0
DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1
DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC
DCD USBFS_IRQHandler ; 83:USBFS
;__Vectors_End就是当前指令的地址,可以用来计算这个向量的size,当然这个size你也可以数一数有多少个DCD,然后乘以4得到
__Vectors_End
;EQU就不说了,这个理解为赋值语句就可以了,__Vectors_Size的大小
__Vectors_Size EQU __Vectors_End - __Vectors
;这里用AREA指示汇编程序汇编新的代码节,指定的节名是“.text",注意这里语法上如果节名中首位是数字的话必须在首尾加入'|'
;这里没有数字而是习惯性的表示节名的方法 ,表示由C编译器生成的代码节。CODE属性表示定义的是代码节,READONLY只读
AREA |.text|, CODE, READONLY
;/* reset Handler */
;proc是定义子程序的伪指令
Reset_Handler PROC
;如果EXPORT带有WEAK的话,其他文件中有定义Reset_Handler有同名函数的话就用其他文件中的函数
;如果没有,则就用本地的Reset_Handler函数,这个好像是C++中的继承,不同的是,一旦其他文件中有
;同名的函数,则本地的函数就彻底无用了
EXPORT Reset_Handler [WEAK]
;IMPORT表示这是一个外部变量的标号,不是本程序中定义的 ,这两个函数都在外面有定义
;可以看出在main函数调用之前调用了systeminit
IMPORT SystemInit
IMPORT __main
;这里说一下LDR,说的多一点,ARM是RISC(精简指令集计算机)结构,只有载入(loader)和存储(store)指令可以访问存储器,
;数据处理指令只对寄存器的内容进行操作。这里利用LDR伪指令(注意不是LDR指令)用“="表示地址,把SystemInit的地址写入到R0中
;需要把内存中的地址读到寄存器中只能使用LDR,不能用mov,mov只能在寄存器之间移动数据
LDR R0, =SystemInit
;BLX 带链接和带状态切换的跳转,首先将当前指令的下一条指令地址保存在LR(R14)寄存器中,然后跳转,这体现了BLX的带链接性,
;BLX同时又是带状态切换的跳转,从BLX R0;跳转到R0中的地址(注意我说的不是R0地址),如果R0[0]=1,则进入Thumb状态
;BLX 同时兼容了BL和BX的功能,为什么这里用带链接功能的跳转呢,下面跳转到main函数的时候为什么用的BX,因为main函数后面就
;没有要执行的指令了,不必保存PC到LR中了,PC中保存的时候当前指令的下一条指令,在执行函数的时候执行完返回的时候要知道进入函数
;之前的下一条指令地址
BLX R0
;下面LDR是将内存中的__main函数的地址存储到LDR中,再啰嗦一句,ARM指令集是精简指令集,从内存搬运数据到寄存器只能用load(LDR)的方法
LDR R0, =__main
;BX R0,带状态切换的跳转,R0[0](R0的最低为如果是1的话)=1的话,则状态切换为Thumb状态 ,为什么切换为Thumb状态,因为Thumb是十六位指令
;AMR指令是32位指令,更低位数的指令代表更高的代码密度,更高的代码密度意味着更低的功耗,所以能用thumb的情况下就用thumb,这是我的理解,
;欢迎斧正
BX R0
;ENDP表示这个子程序结束了,这个很容易理解。最后总结下,相当于这行了Reset_Handler之后再执行SystemInit和main
ENDP
;/* dummy Exception Handlers */
;EXPORT func [WEAK],其他文件中有此fun函数的话就用其他文件中的,当前文件中的fun不起作用
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
; B . 这里有个'.'指的是当前行地址,就跳到当前行
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 ,这个首先是个子程序,在什么情况下会调用到此函数
Default_Handler PROC
; /* external interrupts handler */
EXPORT WWDGT_IRQHandler [WEAK]
EXPORT LVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_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 ADC0_1_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 EXMC_IRQHandler [WEAK]
EXPORT TIMER4_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT UART3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT TIMER5_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 CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_EWMC_IRQHandler [WEAK]
EXPORT USBFS_IRQHandler [WEAK]
;/* external interrupts handler */
WWDGT_IRQHandler
LVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FMC_IRQHandler
RCU_CTC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA0_Channel0_IRQHandler
DMA0_Channel1_IRQHandler
DMA0_Channel2_IRQHandler
DMA0_Channel3_IRQHandler
DMA0_Channel4_IRQHandler
DMA0_Channel5_IRQHandler
DMA0_Channel6_IRQHandler
ADC0_1_IRQHandler
CAN0_TX_IRQHandler
CAN0_RX0_IRQHandler
CAN0_RX1_IRQHandler
CAN0_EWMC_IRQHandler
EXTI5_9_IRQHandler
TIMER0_BRK_TIMER8_IRQHandler
TIMER0_UP_TIMER9_IRQHandler
TIMER0_TRG_CMT_TIMER10_IRQHandler
TIMER0_Channel_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
I2C0_EV_IRQHandler
I2C0_ER_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
SPI0_IRQHandler
SPI1_IRQHandler
USART0_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
EXTI10_15_IRQHandler
RTC_Alarm_IRQHandler
USBFS_WKUP_IRQHandler
TIMER7_BRK_TIMER11_IRQHandler
TIMER7_UP_TIMER12_IRQHandler
TIMER7_TRG_CMT_TIMER13_IRQHandler
TIMER7_Channel_IRQHandler
EXMC_IRQHandler
TIMER4_IRQHandler
SPI2_IRQHandler
UART3_IRQHandler
UART4_IRQHandler
TIMER5_IRQHandler
TIMER6_IRQHandler
DMA1_Channel0_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_EWMC_IRQHandler
USBFS_IRQHandler
;B .跳转到当前位置,网上有人说是跳到这里无限循环,我自己理解是跳到这里补再返回了,不存在什么无限循环,只是不能往下跳转了而已
B .
ENDP
;这里ALIGN表示自己对齐,ALIGN 后面什么都没有?默认是4字节对齐?8字节对齐?网上有网友给出答案
;ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示4字节对齐
ALIGN
; user Initial Stack & Heap
;IF ELSE ENDIF汇编的这些指令已经和C语言差不多了,^_^
;如何才能确定有没有定义__MICORLIB,在编译器中有选项Use MicroLIB,如果有勾选则__MICROLIB会被定义,外面
;就可以用__initial_sp,__heap_base,__heap_limit这些在本文件中声明的label
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
;__use_two_region_memory 指定栈和堆是分两个区域,双区模式,也有单区模式的
IMPORT __use_two_region_memory
;EXPORT表示可以导入到外面,被其他文件所调用,类似于C语言中的extern
EXPORT __user_initial_stackheap
;看下这个PROC子程序
__user_initial_stackheap PROC
;将Heap_Mem地址也就是堆的基地址放入到R0寄存器中,R0~R15的寄存器要看下功能
LDR R0, = Heap_Mem
;R1中放的是栈的最高地址
LDR R1, =(Stack_Mem + Stack_Size)
;R2中放的是堆的最高地址
LDR R2, = (Heap_Mem + Heap_Size)
;R3中放的是栈区的基地址
LDR R3, = Stack_Mem
;BX LR的作用等同于mov PC,LR,即跳转到LR中存放的地址处。
;LR是链接寄存器(Link Register LR),在ARM体系结构体中LR的特殊用途有两种:一种是用来保存子程序的返回地址,这里在外面调用这个子程序的时候
;子程序的返回地址已经自动保存到LR(R14)寄存器中了;二是当异常发生时候,LR中保存的值等于异常发生时PC的值减去4(或者减去2),因此在各种模式下可以
;根据LR的值返回到异常发生前的响应位置继续执行。
;当前通过BL或者BLX指令(注意这里是带L(链接)的)调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时候,把LR的值复制到程序
计数器PC即可实现子程序返回。
BX LR
;ENDP 子程序结束
ENDP
;模式4字节对齐,如果是ALIGN 3,则是2^3字节对齐
ALIGN
;ENDIF 和前面IF ELSE 对应
ENDIF
;END 表示汇编程序结束,后面再有也不会汇编了
END