When a Cortex-M processor first powers on or resets, it goes through a detailed start-up sequence before it begins executing the user’s program code. This start-up sequence configures the processor and prepares the system for program execution. The key steps are:
1. Processor Reset
The processor initiates a reset sequence. This puts all core registers including the program counter and processor status registers back to their reset values. The interrupt system is also reset with all exceptions disabled. Reset also initializes the processor’s internal state machines and clocks.
2. Boot Code Execution
After reset completes, the processor starts executing boot code located in internal ROM or external boot memory. This boot code is hardcoded by the manufacturer and cannot be modified by the user. The boot code sets up the clocking system, enables memory controllers, and configures IO ports and peripherals to put the system in a known initial state.
3. Vector Table Initialization
The boot code initializes the vector table, which contains the memory addresses for exception and interrupt handlers. On Cortex-M, the vector table is located at the start of code memory. The boot code copies the vector table entries to the Vector Table Offset Register so that exceptions and interrupts can be handled.
4. Stack Pointer Initialization
The boot code also initializes the main stack pointer for thread mode operation. This sets up the stack for the user application code that will execute after startup. The stack pointer is normally set to the top of the available RAM memory.
5. System and Memory Initialization
The processor initialization code now runs to set up the system for program execution. This includes:
- Initializing system clocks
- Configuring memory controllers
- Enabling caches and MPUs if present
- Setting up chip-selects for external memories
- Enabling Floating Point Unit (FPU) if present
- Initializing debug system
- Configuring I/O ports and onboard peripherals
The goal is to put the system into a default initialized state before passing control to the user program.
6. BSS Initialization
The BSS section contains uninitialized global and static variables used by the C program. The boot code clears the BSS region, initializing all variables to 0 as per the C standard.
7. C Runtime Initialization
The boot code now calls the C runtime initialization code provided by the compiler. This initializes language features, sets up the heap, initializes global objects, and copies ROM based constants like string literals into RAM. The C runtime ensures a proper execution environment for the user C/C++ code.
8. Main Stack Setup
After C runtime init, the boot code switches from using the temporary system stack to the main user stack. This is done by loading the main stack pointer set earlier. The user stack provides memory for thread mode execution.
9. Global Constructors Call
Before entering the user program, the boot code calls global C++ constructor functions to initialize any global C++ objects. This ensures all globals are in a proper initialized state.
10. Jump to User Program
With all initialization complete, the processor is now ready to jump to the user program entry point, usually main(). The boot code loads the entry point address into the program counter, and execution begins from the user code. The startup sequence is now complete and the system runs the user application until a reset occurs.
In summary, the Cortex-M startup sequence brings the system out of reset into a default initialized state ready for program execution. The user does not need to write this complex boot code – it is provided as part of the C runtime libraries.
Boot Sequence Customization
While the boot sequence is predefined, there are ways to customize it:
- The vector table can be modified to point handlers to user functions.
- The boot code can execute additional initialization functions provided by the user application.
- The linker script allows boot memory layout modification.
- User code can override weak C runtime functions like __initialize_hardware().
This allows some degree of customization to the start-up sequence. However, the fundamental sequence from reset to user program remains unchanged.
Boot Sequence Importance
Understanding the boot sequence is important for several reasons:
- It reveals the critical hardware and software steps needed to transition out of reset into normal operation.
- Knowing the sequence flow helps debug boot issues more quickly.
- It shows what C runtime facilities are available and active at various points.
- Modifying the boot process like the stack or boot memory requires sequence understanding.
- User code can hook into the sequence for initialization or error handling purposes.
In embedded systems, what happens before main() is as important as your application code itself. Walking through the start-up sequence gives insights into the magic enabling your C/C++ arm cortex-m application to execute successfully!
In summary, the Cortex-M boot sequence initializes the hardware and software environment needed to execute your arm cortex-m application successfully. Understanding this process helps debug issues and provides customization opportunities.