The source code file name of this article is: startup_hcf460.S, which is a pure assembly file.
First of all, you need to master some basic assembly syntax, and the main used lists are marked (refer to stm32 related information):
Because it is different from stm32, the sram3 area of the Huada chip is a bit special. Please refer to the Huada chip user manual.
The STR instruction is used in the startup file. The format of the STR instruction is:
STR{condition} source register, <memory address>
The STR instruction is used to transfer a 32-bit word data from the source register to the memory. This instruction is commonly used in programming
. The addressing methods are flexible and diverse. For usage, please refer to the instruction LDR.
Instruction example:
STR R0, [R1], #8; Write the word data in R0 into the memory with R1 as the address, and write the new address R1+8 into R1.
STR R0, [R1, #8]; Write the word data in R0 into the memory with the address R1+8. ”
str r1, [r0]; transfer the value of the r1 register to the (memory) memory with the address value r0
Pay attention to the semicolon; it is a comment mark in assembly, similar to C++'s //.
1. Stack area
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
The size of the opened stack is 0x00000400 (1k B), the name is STACK, NOINIT means not initialized, readable and writable, 8 (23) byte aligned. The stack is used for overhead of local variables, function calls, function parameters, etc. The size of the stack cannot exceed the size of the internal SRAM. If the program you write is relatively large and has many local variables defined, you need to modify the stack size. If one day, the program you wrote has an inexplicable error and enters a hard fault, then you need to consider whether the stack is not large enough and has overflowed. EQU: Macro definition pseudo-instruction, equivalent to equal, similar to define in C.
AREA: Tells the assembler to assemble a new code segment or data segment.
STACK represents the segment name, which can be named arbitrarily; NOINIT means not initialized; READWRITE means readable and writable, ALIGN=3 means aligned according to 2^3, that is, 8-byte alignment.
SPACE: used to allocate a certain size of memory space, in bytes. The size specified here is equal to Stack_Size. The label __initial_sp is placed next to the SPACE statement, indicating the end address of the stack, that is, the top address of the stack (the stack grows from high to low).
2. Heap area
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
The size of the allocated heap is 0x00000200 (512 bytes), the name is HEAP, NOINIT means not initialized, readable and writable, and 8 (2^3) byte aligned. __heap_base represents the starting address of the heap, and __heap_limit represents the end address of the heap (the heap grows from low to high, which is opposite to the growth direction of the stack). The heap is mainly used for dynamic memory allocation. The memory requested by the malloc() function is on the heap.
PRESERVE8
THUMB
PRESERVE8: Specifies that the stack of the current file is aligned to 8 bytes.
THUMB: Indicates that the following instructions are compatible with the THUMB instruction.
THUBM is ARM's previous instruction set, 16 bits. Now the Cortex-M series uses the THUMB-2 instruction set. THUMB-2 is 32-bit, compatible with 16-bit and 32-bit instructions, and is a superset of THUMB.
3. Vector table
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
Define a data segment with the name RESET, which is readable, and declare that the three labels __Vectors, __Vectors_End and __Vectors_Size have global attributes and can be called by external files.
EXPORT: declares that a label can be used by external files, giving the label global attributes. If it is an IAR compiler, use the GLOBAL directive. When the kernel responds to an exception that occurs, the corresponding exception service routine (ESR) is executed. In order to determine the entry address of the ESR, the kernel uses the "vector lookup table mechanism". A vector table is used here, see the table below. The vector table is actually a WORD (32-bit integer) array, each subscript corresponds to an exception, and the value of the subscript element is the entry address of the ESR. The location of the vector table in the address space can be set, and the address of the vector table is pointed out through a relocation register in the NVIC. After reset, the value of this register is 0. Therefore, a vector table must be included at address 0 (ie Flash address 0) for initial exception allocation. It should be noted that there is something different here: type 0 is not an entry address, but gives the initial value of MSP after reset.
For the interrupt vector table, please refer to the user manual. Note that the driver library has been updated on the official website, and the user manual has also been updated.
The vector table code is relatively long and takes up most of the startup code.
__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_Size EQU __Vectors_End - __Vectors
__Vectors is the starting address of the vector table, __Vectors_End is the ending address of the vector table, and the size of the vector table can be calculated by subtracting the two. The vector table is placed starting from address 0 of Flash, with 4 bytes as a unit. Address 0 stores the top address of the stack, 0x04 stores the address of the reset program, and so on. From the code point of view, the vector table stores the function names of the interrupt service functions, but we know that the function name in C language is an address. DCD: Allocate one or more words of memory, aligned with 4 bytes, and require initialization of these memories. In the vector table, DCD allocates a bunch of memory and initializes them with the entry address of the ESR.
4. Reset procedure
AREA |.text|, CODE, READONLY
Define a code segment named .text, read-only.
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
SET_SRAM3_WAIT
LDR R0, =0x40050804
MOV R1, #0x77
STR R1, [R0]
LDR R0, =0x4005080C
MOV R1, #0x77
STR R1, [R0]
LDR R0, =0x40050800
MOV R1, #0x1100
STR R1, [R0]
LDR R0, =0x40050804
MOV R1, #0x76
STR R1, [R0]
LDR R0, =0x4005080C
MOV R1, #0x76
STR R1, [R0]
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
The reset subroutine is the first program executed after the system is powered on. It calls the System Init function to initialize the system clock, then calls the C library function _mian, and finally calls the main function to enter the C language world.
WEAK: Indicates a weak definition. If the external file defines the label first, the label will be referenced first. If the external file does not declare it, no error will occur. This means that the reset subroutine can be re-implemented by the user in other files, and this is not the only one. IMPORT: Indicates that the label comes from an external file, similar to the EXTERN keyword in C language. This means that the two functions System Init and __main come from external files.
System Init(): A standard library function, defined in the system_hc32f460.c library file. Its main function is to configure the system clock. The clock here needs to be configured by the user. The maximum frequency of the Huada chip supports 200M.
__main: A standard C library function whose main function is to initialize the user stack and call the main function at the end of the function to enter the C language world. This is why every program we write has a main function. LDR, BLX, and BX are instructions of the CM4 core, which can be found in Chapter 4 of "CM3 Authoritative Guide Cn R2". The specific functions are shown in the table below.
It is worth noting that there is a piece of code above that operates the R0 and R1 registers. What does this code mean? Check the manual and find the address of 0x40050804. It is found that the control register of sram is operated. After comparing the manual in detail, the function is to set the read and write waiting period for the sram3 area to 2 cycles.
5. Interrupt service routine
The code below is the interrupt service routine. It is too long and will not be pasted. The startup file has helped us write all the interrupt service functions, but unlike the interrupt service functions we usually write, these functions are empty. The real interrupt service program needs to be re-implemented in the external C file. Here is just the advance Just occupied a position. If you enable an interrupt when using a certain peripheral, but forget to write the supporting interrupt service routine or write the wrong function name, then when the interrupt comes, the program will jump to the space pre-written in the startup file. in the interrupt service routine, and loops infinitely in this empty function, that is, the program "dies" here.
6. User stack initialization
ALIGN
; User Initial Stack & Heap
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
ALIGN: Align the address where the instruction or data is stored, followed by an immediate number. The default indicates 4-byte alignment.
First, determine whether __MICROLIB is defined. If this macro is defined, the labels __initial_sp (top address of stack), __heap_base (starting address of heap), and __heap_limit (end address of heap) are given global attributes, which can be called by external files. This macro is configured in KEIL, as shown in the figure below. Then the initialization of the stack is completed by the C library function _main.
If __MICROLIB is not defined, dual-segment memory mode is used, and the label __user_initial_stackheap is declared to have global attributes, allowing users to initialize the stack themselves. IF, ELSE, ENDIF: Assembly conditional branch statements, similar to if and else in C language. END: End of file.
Summarize:
The startup file is written in assembly language and is the first program executed after the system is powered on and reset. It mainly does the following work:
1) Initialize the stack pointer: SP=_initial_sp.
2) Initialization program pointer: PC=Reset_Handler.
3) Initialize the interrupt vector table.
4) Set the waiting period of sram3 and configure the system clock.
5) Call the C library function _main to initialize the user stack, and finally call the main function to enter the C language world.