• 华大(现在改名小华半导体)芯片启动文件详细讲解


    本文的源码文件名为:startup_hcf460.S,是一个纯汇编文件。

    首先要掌握一些基础的汇编语法,主要用到的列表标出(参考stm32相关资料):

     因为和stm32不同,华大芯片的sram3区域有点特殊,参考华大芯片用户手册。

     启动文件中使用STR指令,STR指令的格式为:
    STR{条件}  源寄存器,<存储器地址>
    STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常
    用,丏寻址方式灵活多样,使用方式可参考指令LDR。

    指令示例:
    STR R0,[R1],#8             ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
    STR R0,[R1,#8]             ;将R0中的字数据写入以R1+8为地址的存储器中。”
    str     r1, [r0]                       ;将r1寄存器的值,传送到地址值为r0的(存储器)内存中

    注意分号;在汇编里面是注释的标识,类似C++ 的//。

    1. 栈区域

    1. Stack_Size EQU 0x00000400
    2. AREA STACK, NOINIT, READWRITE, ALIGN=3
    3. Stack_Mem SPACE Stack_Size
    4. __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. 堆区域

    1. Heap_Size EQU 0x00000200
    2. AREA HEAP, NOINIT, READWRITE, ALIGN=3
    3. __heap_base
    4. Heap_Mem SPACE Heap_Size
    5. __heap_limit

     开辟堆的大小为0x00000200(512字节),名字为HEAP,NOINIT即不初始化,可读可写,8(2^3)字节对齐。__heap_base表示堆的起始地址,__heap_limit表示堆的结束地址(堆是由低向高生长的,与栈的生长方向相反)。堆主要用于动态内存的分配,像malloc()函数申请的内存就在堆上面。

    1. PRESERVE8
    2. THUMB

    PRESERVE8:指定当前文件的堆栈按照8字节对齐。

    THUMB:表示后面指令兼容THUMB指令。

    THUBM是ARM以前的指令集,16位。现在Cortex-M系列都使用THUMB-2指令集,THUMB-2是32位的,兼容16位和32位的指令,是THUMB的超集。

    3. 向量表

    1. AREA RESET, DATA, READONLY
    2. EXPORT __Vectors
    3. EXPORT __Vectors_End
    4. 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库,用户手册也更新了。

     向量表的代码比较长,占据了启动代码的大部分篇幅。

    1. __Vectors DCD __initial_sp ; Top of Stack
    2. DCD Reset_Handler ; Reset Handler
    3. DCD NMI_Handler ; NMI Handler
    4. DCD HardFault_Handler ; Hard Fault Handler
    5. DCD MemManage_Handler ; MPU Fault Handler
    6. DCD BusFault_Handler ; Bus Fault Handler
    7. DCD UsageFault_Handler ; Usage Fault Handler
    8. DCD 0 ; Reserved
    9. DCD 0 ; Reserved
    10. DCD 0 ; Reserved
    11. DCD 0 ; Reserved
    12. DCD SVC_Handler ; SVCall Handler
    13. DCD DebugMon_Handler ; Debug Monitor Handler
    14. DCD 0 ; Reserved
    15. DCD PendSV_Handler ; PendSV Handler
    16. DCD SysTick_Handler ; SysTick Handler
    17. ; Peripheral Interrupts
    18. DCD IRQ000_Handler ; IRQ000_Handler
    19. DCD IRQ001_Handler ; IRQ001_Handler
    20. DCD IRQ002_Handler ; IRQ002_Handler
    21. DCD IRQ003_Handler ; IRQ003_Handler
    22. DCD IRQ004_Handler ; IRQ004_Handler
    23. DCD IRQ005_Handler ; IRQ005_Handler
    24. DCD IRQ006_Handler ; IRQ006_Handler
    25. DCD IRQ007_Handler ; IRQ007_Handler
    26. DCD IRQ008_Handler ; IRQ008_Handler
    27. DCD IRQ009_Handler ; IRQ009_Handler
    28. DCD IRQ010_Handler ; IRQ010_Handler
    29. DCD IRQ011_Handler ; IRQ011_Handler
    30. DCD IRQ012_Handler ; IRQ012_Handler
    31. DCD IRQ013_Handler ; IRQ013_Handler
    32. DCD IRQ014_Handler ; IRQ014_Handler
    33. DCD IRQ015_Handler ; IRQ015_Handler
    34. DCD IRQ016_Handler ; IRQ016_Handler
    35. DCD IRQ017_Handler ; IRQ017_Handler
    36. DCD IRQ018_Handler ; IRQ018_Handler
    37. DCD IRQ019_Handler ; IRQ019_Handler
    38. DCD IRQ020_Handler ; IRQ020_Handler
    39. DCD IRQ021_Handler ; IRQ021_Handler
    40. DCD IRQ022_Handler ; IRQ022_Handler
    41. DCD IRQ023_Handler ; IRQ023_Handler
    42. DCD IRQ024_Handler ; IRQ024_Handler
    43. DCD IRQ025_Handler ; IRQ025_Handler
    44. DCD IRQ026_Handler ; IRQ026_Handler
    45. DCD IRQ027_Handler ; IRQ027_Handler
    46. DCD IRQ028_Handler ; IRQ028_Handler
    47. DCD IRQ029_Handler ; IRQ029_Handler
    48. DCD IRQ030_Handler ; IRQ030_Handler
    49. DCD IRQ031_Handler ; IRQ031_Handler
    50. DCD IRQ032_Handler ; IRQ032_Handler
    51. DCD IRQ033_Handler ; IRQ033_Handler
    52. DCD IRQ034_Handler ; IRQ034_Handler
    53. DCD IRQ035_Handler ; IRQ035_Handler
    54. DCD IRQ036_Handler ; IRQ036_Handler
    55. DCD IRQ037_Handler ; IRQ037_Handler
    56. DCD IRQ038_Handler ; IRQ038_Handler
    57. DCD IRQ039_Handler ; IRQ039_Handler
    58. DCD IRQ040_Handler ; IRQ040_Handler
    59. DCD IRQ041_Handler ; IRQ041_Handler
    60. DCD IRQ042_Handler ; IRQ042_Handler
    61. DCD IRQ043_Handler ; IRQ043_Handler
    62. DCD IRQ044_Handler ; IRQ044_Handler
    63. DCD IRQ045_Handler ; IRQ045_Handler
    64. DCD IRQ046_Handler ; IRQ046_Handler
    65. DCD IRQ047_Handler ; IRQ047_Handler
    66. DCD IRQ048_Handler ; IRQ048_Handler
    67. DCD IRQ049_Handler ; IRQ049_Handler
    68. DCD IRQ050_Handler ; IRQ050_Handler
    69. DCD IRQ051_Handler ; IRQ051_Handler
    70. DCD IRQ052_Handler ; IRQ052_Handler
    71. DCD IRQ053_Handler ; IRQ053_Handler
    72. DCD IRQ054_Handler ; IRQ054_Handler
    73. DCD IRQ055_Handler ; IRQ055_Handler
    74. DCD IRQ056_Handler ; IRQ056_Handler
    75. DCD IRQ057_Handler ; IRQ057_Handler
    76. DCD IRQ058_Handler ; IRQ058_Handler
    77. DCD IRQ059_Handler ; IRQ059_Handler
    78. DCD IRQ060_Handler ; IRQ060_Handler
    79. DCD IRQ061_Handler ; IRQ061_Handler
    80. DCD IRQ062_Handler ; IRQ062_Handler
    81. DCD IRQ063_Handler ; IRQ063_Handler
    82. DCD IRQ064_Handler ; IRQ064_Handler
    83. DCD IRQ065_Handler ; IRQ065_Handler
    84. DCD IRQ066_Handler ; IRQ066_Handler
    85. DCD IRQ067_Handler ; IRQ067_Handler
    86. DCD IRQ068_Handler ; IRQ068_Handler
    87. DCD IRQ069_Handler ; IRQ069_Handler
    88. DCD IRQ070_Handler ; IRQ070_Handler
    89. DCD IRQ071_Handler ; IRQ071_Handler
    90. DCD IRQ072_Handler ; IRQ072_Handler
    91. DCD IRQ073_Handler ; IRQ073_Handler
    92. DCD IRQ074_Handler ; IRQ074_Handler
    93. DCD IRQ075_Handler ; IRQ075_Handler
    94. DCD IRQ076_Handler ; IRQ076_Handler
    95. DCD IRQ077_Handler ; IRQ077_Handler
    96. DCD IRQ078_Handler ; IRQ078_Handler
    97. DCD IRQ079_Handler ; IRQ079_Handler
    98. DCD IRQ080_Handler ; IRQ080_Handler
    99. DCD IRQ081_Handler ; IRQ081_Handler
    100. DCD IRQ082_Handler ; IRQ082_Handler
    101. DCD IRQ083_Handler ; IRQ083_Handler
    102. DCD IRQ084_Handler ; IRQ084_Handler
    103. DCD IRQ085_Handler ; IRQ085_Handler
    104. DCD IRQ086_Handler ; IRQ086_Handler
    105. DCD IRQ087_Handler ; IRQ087_Handler
    106. DCD IRQ088_Handler ; IRQ088_Handler
    107. DCD IRQ089_Handler ; IRQ089_Handler
    108. DCD IRQ090_Handler ; IRQ090_Handler
    109. DCD IRQ091_Handler ; IRQ091_Handler
    110. DCD IRQ092_Handler ; IRQ092_Handler
    111. DCD IRQ093_Handler ; IRQ093_Handler
    112. DCD IRQ094_Handler ; IRQ094_Handler
    113. DCD IRQ095_Handler ; IRQ095_Handler
    114. DCD IRQ096_Handler ; IRQ096_Handler
    115. DCD IRQ097_Handler ; IRQ097_Handler
    116. DCD IRQ098_Handler ; IRQ098_Handler
    117. DCD IRQ099_Handler ; IRQ099_Handler
    118. DCD IRQ100_Handler ; IRQ100_Handler
    119. DCD IRQ101_Handler ; IRQ101_Handler
    120. DCD IRQ102_Handler ; IRQ102_Handler
    121. DCD IRQ103_Handler ; IRQ103_Handler
    122. DCD IRQ104_Handler ; IRQ104_Handler
    123. DCD IRQ105_Handler ; IRQ105_Handler
    124. DCD IRQ106_Handler ; IRQ106_Handler
    125. DCD IRQ107_Handler ; IRQ107_Handler
    126. DCD IRQ108_Handler ; IRQ108_Handler
    127. DCD IRQ109_Handler ; IRQ109_Handler
    128. DCD IRQ110_Handler ; IRQ110_Handler
    129. DCD IRQ111_Handler ; IRQ111_Handler
    130. DCD IRQ112_Handler ; IRQ112_Handler
    131. DCD IRQ113_Handler ; IRQ113_Handler
    132. DCD IRQ114_Handler ; IRQ114_Handler
    133. DCD IRQ115_Handler ; IRQ115_Handler
    134. DCD IRQ116_Handler ; IRQ116_Handler
    135. DCD IRQ117_Handler ; IRQ117_Handler
    136. DCD IRQ118_Handler ; IRQ118_Handler
    137. DCD IRQ119_Handler ; IRQ119_Handler
    138. DCD IRQ120_Handler ; IRQ120_Handler
    139. DCD IRQ121_Handler ; IRQ121_Handler
    140. DCD IRQ122_Handler ; IRQ122_Handler
    141. DCD IRQ123_Handler ; IRQ123_Handler
    142. DCD IRQ124_Handler ; IRQ124_Handler
    143. DCD IRQ125_Handler ; IRQ125_Handler
    144. DCD IRQ126_Handler ; IRQ126_Handler
    145. DCD IRQ127_Handler ; IRQ127_Handler
    146. DCD IRQ128_Handler ; IRQ128_Handler
    147. DCD IRQ129_Handler ; IRQ129_Handler
    148. DCD IRQ130_Handler ; IRQ130_Handler
    149. DCD IRQ131_Handler ; IRQ131_Handler
    150. DCD IRQ132_Handler ; IRQ132_Handler
    151. DCD IRQ133_Handler ; IRQ133_Handler
    152. DCD IRQ134_Handler ; IRQ134_Handler
    153. DCD IRQ135_Handler ; IRQ135_Handler
    154. DCD IRQ136_Handler ; IRQ136_Handler
    155. DCD IRQ137_Handler ; IRQ137_Handler
    156. DCD IRQ138_Handler ; IRQ138_Handler
    157. DCD IRQ139_Handler ; IRQ139_Handler
    158. DCD IRQ140_Handler ; IRQ140_Handler
    159. DCD IRQ141_Handler ; IRQ141_Handler
    160. DCD IRQ142_Handler ; IRQ142_Handler
    161. DCD IRQ143_Handler ; IRQ143_Handler
    162. __Vectors_End
    163. __Vectors_Size EQU __Vectors_End - __Vectors

     __Vectors为向量表起始地址,__Vectors_End为向量表结束地址,两个相减即可算出向量表大小。向量表从Flash的0地址开始放置,以4个字节为一个单位,地址0存放的是栈顶地址,0x04存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C语言中的函数名就是一个地址。DCD:分配一个或者多个以字为单位的内存,以4字节对齐,并要求初始化这些内存。在向量表中,DCD分配了一堆内存,并且以ESR的入口地址初始化它们。

    4. 复位程序

    AREA    |.text|, CODE, READONLY

    定义一个名称为.text的代码段,只读。

    1. Reset_Handler PROC
    2. EXPORT Reset_Handler [WEAK]
    3. IMPORT SystemInit
    4. IMPORT __main
    5. SET_SRAM3_WAIT
    6. LDR R0, =0x40050804
    7. MOV R1, #0x77
    8. STR R1, [R0]
    9. LDR R0, =0x4005080C
    10. MOV R1, #0x77
    11. STR R1, [R0]
    12. LDR R0, =0x40050800
    13. MOV R1, #0x1100
    14. STR R1, [R0]
    15. LDR R0, =0x40050804
    16. MOV R1, #0x76
    17. STR R1, [R0]
    18. LDR R0, =0x4005080C
    19. MOV R1, #0x76
    20. STR R1, [R0]
    21. LDR R0, =SystemInit
    22. BLX R0
    23. LDR R0, =__main
    24. BX R0
    25. ENDP

     复位子程序是系统上电后第一个执行的程序,调用System Init函数初始化系统时钟,然后调用C库函数_mian,最终调用main函数进入C语言世界。

    WEAK:表示弱定义,如果外部文件优先定义了该标号,则首先引用该标号,如果外部文件没有声明,也不会出错。这里表示复位子程序可以由用户在其他文件中重新实现,这里并不是唯一的。IMPORT:表示该标号来自外部文件,与C语言中的EXTERN关键字类似。这里表示System Init和__main这两个函数均来自外部的文件。

    System Init():一个标准的库函数,在system_hc32f460.c这个库文件中定义。其主要作用是配置系统时钟,这里面的时钟是需要用户自己配置的,华大芯片最高主频支持200M。

    __main:一个标准的C库函数,主要作用是初始化用户堆栈,并在函数的最后调用main函数进入C语言世界。这就是为什么我们写的程序都有一个main函数的原因。LDR、BLX、BX是CM4内核的指令,可在《CM3权威指南Cn R2》第4章里面查询到,具体作用见下表。

     值得注意的是上面有一段操作R0和R1寄存器的代码,这段代码是什么意思呢?查询手册,找到这个0x40050804的地址,发现是对sram的控制寄存器进行操作,详细对比手册之后,功能就是给sram3这块区域设置读写等待周期为2周期。

     

     5. 中断服务程序

    再下面的代码就是中断服务程序,太长了就不粘贴了。启动文件已经帮我们写好所有中断的服务函数,但与我们平时写的中断服务函数不一样,这些函数都是空的,真正的中断服务程序需要在外部的C文件里面重新实现,这里只是提前占了一个位置而已。如果在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务程序或者函数名写错,那当中断来临时,程序就会跳转到启动文件预先写好的空的中断服务程序中,并且在这个空函数中无限循环,即程序就“死”在这里。

    6. 用户堆栈初始化

    1. ALIGN
    2. ; User Initial Stack & Heap
    3. IF :DEF:__MICROLIB
    4. EXPORT __initial_sp
    5. EXPORT __heap_base
    6. EXPORT __heap_limit
    7. ELSE
    8. IMPORT __use_two_region_memory
    9. EXPORT __user_initial_stackheap
    10. __user_initial_stackheap PROC
    11. LDR R0, = Heap_Mem
    12. LDR R1, =(Stack_Mem + Stack_Size)
    13. LDR R2, = (Heap_Mem + Heap_Size)
    14. LDR R3, = Stack_Mem
    15. BX LR
    16. ENDP
    17. ALIGN
    18. ENDIF
    19. END

     ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数,缺省表示4字节对齐。

    首先判断是否定义了__MICROLIB,如果定义了这个宏,则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。有关这个宏在KEIL里面配置,具体见下图。然后堆栈的初始化就由C库函数_main来完成。

     如果没有定义__MICROLIB,则采用双段存储器模式,且声明标号__user_initial_stackheap具有全局属性,让用户自己初始化堆栈。IF、ELSE、ENDIF:汇编的条件分支语句,与C语言中的if、else类似。END:文件结束。

    总结:

    启动文件由汇编语言编写,是系统上电复位后第一个执行的程序,主要做了以下工作:

    1)初始化堆栈指针:SP=_initial_sp。

    2)初始化程序指针:PC=Reset_Handler。

    3)初始化中断向量表。

    4)设定sram3的等待周期,配置系统时钟。

    5)调用C库函数_main初始化用户堆栈,最终调用main函数进入C语言世界。

  • 相关阅读:
    剑指offer 47:礼物的最大价值
    从零开始搭建SpringBoot项目,并且用Mybatis进行数据查询(包含所用数据库)
    常见的几种身份验证方法
    OneDiff 1.0发布!生产环境稳定加速SD/SVD模型
    RabbitMQ-简述
    tinyxml2开源库使用
    Xilinx MicroBlaze定时器中断无法返回主函数问题解决
    代码随想录算法训练营第五十九天| 647.回文子串 、516.最长回文子序列
    Rust-类型转换进阶
    深入理解计算机网络-10传输层2
  • 原文地址:https://blog.csdn.net/weixin_41579872/article/details/127653434