ARM Cortex-M Startup Code
How does the ARM Cortex-M startup code work? What is the first thing that happens when an ARM Cortex-M processor boots up? Let’s understand the ARM Cortex-M startup sequence in this blog post.
Understanding how an ARM Cortex-M startup code works is useful in certain cases. If you are writing bootloaders, you need to know how the ARM Cortex-M processor starts executing. Second,it helps deepen your understanding about the Cortex-M architecture. Finally it helps in getting a feel of ARM Cortex-M assembly language programming.
After reading this post, you will get to know how the ARM Cortex-M processor starts executing on power up. Familiarity with ARM Cortex-M architecture is helpful to understand the details presented here. This is not a line by line explanation of the ARM Cortex-M startup code. It rather elaborates on flow of things in the ARM Cortex-M processor at startup.
This post was originally published in ARM Community.
Startup Code Organization
We can divide the ARM Corte-M startup code broadly in 5 different sections.
- Stack area declaration
- Heap area declaration
- Vector table
- Reset handler code
- Other exception handler code
Let’s explore these sections one by one. Remember that this startup code is completely in assembly language. This blog post contains brief explanation of the syntax and code involved. If you would like to go deeper in assembly programming, then the ARM assembler user guide is your best friend.
Stack Area Declaration
Stack is an area used for temporary storage of variables and register contents during code execution. The ARM Cortex-M startup code defines stack area with following code.
The first line
Stack_Size EQU 0x00000400 declares a constant called Stack_Size of value 0x00000400. EQU is an assembler directive. It is similar to the #define pre-processor directive in C.
Next is a declaration of area for Stack.
The AREA directive divides the assembly code in different sections. This directive denotes a separate section in the memory. STACK in this case is the name of the section. Followed by the section name are some attributes for this section.
NOINIT indicates that the data in this section is initialized to zero.
READWRITE as the name implies, allows the program code to read and write from this section of memory.
ALIGN=3 makes the starting of this section on an 8-byte boundary. (2^3 = 8).
This line allocates a space of 0x0400 bytes in the stack area. SPACE is an assembly directive. It reserves a space in memory for specified number of bytes.
__initial_sp is the declaration of a label. The vector table stores this value as the first entry. This label will equate to the next address after the stack space in this area. Since the stack grows downwards, this serves as the initial stack pointer.
Ignore the heap section for now. Let’s first look at the vector table.
Vector table is an important section in the ARM Cortex-M startup code. This is where the processor locates point of entry to your application code. The vector table is contained in a section named as RESET.
AREA directive denotes the RESET section. The attribute DATA indicates that this section will contain data and not instructions. This is true because the vector table contains only the addresses of the handlers and initial stack pointer value.
Another attribute is
READONLY. This makes sure that the application code is not able to overwrite this area.
The RESET area is located at start of the CODE section of the flash memory. This places the vector table at offset 0. Since the vector table offset register VTOR defaults to 0, the processor uses this vector table at startup. For STM32F4, this location is 0x08000000. You can get this detail from the memory map from the STM32F4 datasheet. This value is also specified in linker options - either in a scatter file or by command line linker options.
What does the Vector Table contain?
The Vector table in the ARM Cortex-M startup code contains the following things.
- Initial value of the Stack Pointer
- Starting address of the reset handler (Reset handler is the code executed on reset)
- Starting addresses of all other exceptions and interrupts including the NMI handler, Hard fault handler and so on.
Let’s look at the Vector table contents in detail.
This line stores the value of label
__initial_sp in the RESET area. DCD is an assembly directive which stores a word data (32-bit) in the memory.
Similarly the next word stored is the address of
Reset_Handler. This is a forward reference because the label
Reset_Handler is declared somewhere down the code. (The assembler processes the file in two passes which helps it to resolve such forward references).
Exception Handlers Declaration
Following the reset handler are the starting addresses of various exception handlers such as
HardFault_Handler and so on. ARM Cortex-M processor provides some default exceptions. These are the first 15 exceptions starting from
Reset_Handler up to
SysTick_Handler. Following these processor exceptions are the external interrupts. Here external refers to Arm processor and not the MCU STM32. These interrupts are connected to various peripherals in the MCU such as Watchdog, DMA, RTC etc. The list continues up to
FPU_IRQHandler (Flash point Unit IRQ).
The vector table and especially the first two entries in it are essential for the ARM Cortex-M startup code to start executing application program and handle stacking operations. During the startup operation, the ARM Cortex-M processor copies the first entry in the vector table to the stack pointer (which is the Main Stack Pointer or MSP). Then it copies the next entry into PC (Program counter) and the execution starts from this address. So we specify the address of our Reset Handler which is the first code it will execute.
As the name suggests, reset handler is the code to be executed on reset. The ARM Cortex-M startup code defines the code in a separate area. This section resides in a CODE region.
This line defines an area of memory containing code. The
READONLY attribute makes sure that the application code does not write to this area. The name of the section is
.text as a convention but could be anything you wish. Vertical bars around this name are necessary because the name does not start with an alphabet. This is a requirement of the assembly directives.
In this region the ARM Cortex-M startup code calls a function called SystemInit. The SystemInit function contains code for initial configuration such as clock speed etc.
After system initialization the startup code calls main function. The main function usually does not return.
Let’s look at the code for reset handler.
The implementation of SystemInit and __main is not part of ARM Cortex-M startup code. Usually SystemInit is part of your application code. To call it from the startup code, we need to import it. The symbol __main is actually in the C library which eventually calls the main function in your application.
If you are using plain assembly, you will need to place an ENTRY directive in the reset handler in absence of the __main. This allows the linker, debugger to locate the entry point of the program.
LDR R0, =SystemInit and
LDR R0, =__main are pseudo assembly instructions. The pseudo instruction loads address of the specified function in R0. Next instruction
BLX R0 jumps the code to execute from that address.
Once the code starts executing, there might be exceptions occurring and therefore you need exception handlers. In addition to the reset handler, the ARM Cortex-M startup code also provides weak definitions for all exception handlers. For e.g. look at the NMI handler.
The first line NMI_Handler is the label for this small function. PROC is an assembly directive which defines start of a procedure or a function.
Next line EXPORT makes this label NMI_Handler available to other parts of the program. The attribute [WEAK] specifies that the handler can be redefined elsewhere in the project. This helps you to have your own custom handler in your project and even different handlers for different projects but still keep the same startup file. This is something similar to the virtual functions in C++.
Of course if you want to have the same handler for all your projects, then you can straight away modify this startup file to add your own definitions of the handlers.
By default the exception handler in ARM Cortex-M startup code is an endless loop with the instruction
B . This instruction branches to the same address thus generating in an infinite loop. Also note that the same handler code is used for all processor exceptions. In real world, you need to define different implementations for different handlers as per your requirement.
ENDP denotes end of the procedure.
For the external interrupt handlers, the ARM Cortex-M startup code defines only one procedure (the same endless loop)
Default_Handler. This means that for any exception occurring from the MCU peripherals, the code will execute this
Default_Handler. Again, all these are weak definitions so you can redefine them in your project.
Note that even the
Reset_Handler is also a weak implementation so you can have your own reset handler if you wish.
Finally, the ARM Cortex-M startup code also defines heap section for dynamic memory allocation. The heap section definition is similar to the stack area. The two labels
__heap_limit indicate the starting of heap area and end of the heap area respectively.
You can see that the heap area attributes are same as those for the stack area attributes.
Some more directives in the ARM Cortex-M startup code are worth mentioning.
This directive instructs the linker to preserve 8-byte alignment of the stack. This is a requirement of the Arm Architecture Procedure Call Standard (AAPCS).
This indicates THUMB mode which is the only mode available on Cortex-M processors since it does not support the Arm mode.
ALIGN is an assembler directive which aligns the current memory location to the next word boundary. The assembler inserts
NOP instructions (or zero data) if the current location is already on the boundary.
I hope this information will be useful in understanding ARM Cortex-M startup code and how the ARM Cortex-M processor starts executing code. If you are interested to explore ARM assembly programming, refer to various other blog posts on this website. To get started, you can refer to the Hello World of assembly programming Blink LED with ARM assembly. If you like this post, you can share it on social media. Have fun learning!