The ARM Cortex M4 is a 32-bit RISC processor core designed for embedded and IoT applications. It features DSP extensions, low power consumption, and high performance making it popular for a wide range of products. When an ARM Cortex M4 powers up, it follows a specific boot sequence to initialize hardware, configure memory, and begin executing instructions. Understanding this boot process is important for developers working with Cortex M4 devices.
Reset Vector
The first step in the Cortex M4 boot sequence is reset. This occurs when power is first applied or from an external reset signal. At reset, the processor jumps to the address 0x00000004, known as the reset vector. This memory location should contain a branch instruction that jumps to the start of the program code. An initializing bootloader is typically located here. The reset vector address is configurable but 0x00000004 is the default.
Bootloader Initialization
With the program counter set to the reset vector address, the processor will begin executing the instructions of the bootloader. This bootloader has the responsibility of setting up hardware, configuring memory, and initializing system state before jumping to the main application. Typical tasks performed by the bootloader include:
- Enabling floating point unit
- Configuring clock speeds
- Setting up memory protection units
- Initializing RAM
- Copying .data section values from ROM to RAM
- Clearing the .bss section in RAM
- Enabling interrupts
- Initializing peripherals
The complexity and features of the bootloader depend on the specific Cortex M4 device and application requirements. A minimal bootloader may just configure a few registers before jumping to main. A more fully featured one can set up memory protection, DRAM, initialize device drivers and more. Developers can customize the bootloader to meet their system needs.
Vector Table Initialization
In addition to general initialization, an important role of the Cortex M4 bootloader is to set up the vector table. This table contains the addresses of exception and interrupt handling routines. It must be relocated from ROM to RAM before passing control to the application. Here is the default vector table structure: 0x000: Stack Pointer Reset Value 0x004: Reset Vector 0x008: NMI Exception 0x00C: HardFault Exception … 0x3C: SysTick Exception
The bootloader copies this table from its link location, often in ROM, over to RAM. It then remaps the vector table base address to the new RAM location. This allows the application software to install its own interrupt routines. The bootloader may also populate some exception vectors like SysTick with default handlers if needed.
Clearing BSS Segment
Another standard bootloader task is clearing the BSS segment. This portion of RAM holds uninitialized global and static variables. The bootloader loops through the BSS section, filling it with zeros as per the C standard. This ensures all globals begin with expected values before the application runs. for (i = __bss_start__; i < __bss_end__; i++) { *i = 0; }
Initializing Static RAM
For devices with internal SRAM, the bootloader may also need to properly configure and test it. The M4 memory protection unit should be enabled early on to catch any invalid accesses. SRAM initialization routines may include:
- Configuring memory access timings
- Setting wait states
- Enabling error correction codes
- Performing memory read/write tests
Proper SRAM initialization is important to catch any faults and avoid corruption during application execution.
Initializing External DRAM
For systems using external DRAM, the bootloader takes on the additional task of initializing this memory. DRAM requires more care to set up properly compared to SRAM. Steps include:
- Configure memory controller with DRAM parameters
- Program DRAM timings and signals
- Enable clocking to DRAM
- Perform DRAM reset sequence
- Execute DRAM calibration process
- Test reads and writes to DRAM storage
With modern DDR DRAM densities, calibrating timing parameters is crucial for maximum performance. The bootloader handles this startup procedure before the application attempts to use it.
Peripheral Initialization
The Cortex M4 MCU includes a wide array of on-chip peripherals including communication buses, timers, ADCs, and more. The bootloader initializes any peripherals needed by the application. This may include:
- Enabling module clocking
- Resetting peripheral state
- Configuring pins
- Setting control registers
- Installing interrupt handlers
- Starting operation
Good practice is to only initialize peripherals actually used rather than all on the device. This saves boot time and power consumption. Developers customize the bootloader peripherals for their specific application.
Calling System Initialization and Main
After completing hardware setup and initialization, the last task of the Cortex M4 bootloader is to transfer control to the main application. This is done by calling the SystemInit() and main() functions. SystemInit() handles any additional initialization such as enabling the FPU or MPU. With that done, calling main() starts application execution. At this point, the CPU is fully operational and ready to run the main program.
Bootloader Considerations
When implementing a Cortex M4 bootloader, developers should keep some considerations in mind:
- Performance – Minimize unneeded delay during initialization.
- Size – Keep bootloader code compact to leave space for the application.
- Reliability – Defensively program for safety and fault tolerance.
- Security – Lock down access and validate firmware if needed.
- Upgradability – Allow for field upgrading of the bootloader.
- Standards – Follow established practices when possible.
With these principles in mind, developers can create effective bootloaders tailored for their Cortex M4 system.
Summary
The ARM Cortex M4 boot sequence initializes hardware, configures memory, and starts software execution in an embedded system. Key steps include branching to the reset vector on startup, initializing memory and peripherals, setting up the vector table, clearing BSS, and calling main(). Developers can customize the bootloader to best meet their specific application requirements.
Understanding this boot process is important for both bootloader designers as well as embedded engineers integrating a Cortex M4 into their project. Following best practices for performance, size, reliability, and design enables building robust devices with the Cortex M4 at their core.