The reset handler, also known as the reset vector, is the first code that runs when an ARM processor starts up or resets. It is responsible for performing essential low-level initializations and then transferring control to the main application. Understanding the reset handler is key to embedded software development on ARM platforms.
ARM Processor Startup
When an ARM processor first powers on, it begins executing code from a predetermined memory address known as the reset vector. This address is 0x00000000 on most ARM processors. The code located here is the reset handler.
The processor also jumps to the reset vector when a reset occurs, such as from a watchdog timer, external reset signal, or software requested reset. This allows the processor to reinitialize itself from a known state.
Functions of the Reset Handler
The key tasks performed by the reset handler include:
- Initializing core registers – The handler configures essential registers like the stack pointer, program counter, and various CPU mode registers.
- Setting up RAM – It initializes the system’s RAM including configuring the MMU if present.
- Initializing peripherals – Low-level peripherals like clocks and power management are set up.
- Copying initialized data – Data initialized at compile time is copied from ROM to RAM.
- Zeroing uninitialized data – BSS data is cleared to all zeros.
- Calling runtime initialization – C++ constructors and other language runtimes are initialized.
- Calling the main program – Control is finally passed to the user application code.
Handling these crucial steps allows the processor and subsystems to enter a ready state before the main firmware begins execution.
Reset Handler Implementation
While the reset handler performs standard functions across ARM devices, the actual implementation can vary significantly depending on the hardware platform, toolchain, and software environment.
The reset handler is typically written in assembly language, often handcoded rather than compiler generated. This allows precise control over register initialization and low-level hardware access. The amount of assembly code can range from a few lines to hundreds of lines depending on system complexity.
Many toolchains utilize start files or crt0 code to implement parts of the reset handler. These files contain assembly code and definitions that handle common initialization tasks in a general way. The start files may be customized for each project build.
In complex embedded devices like smartphones, the reset handler may load and run a more sophisticated bootloader. The bootloader then continues the startup sequence, loading the full OS. In this case, the reset handler simply initializes enough hardware to allow booting the first stage bootloader.
On advanced ARM processors like Cortex-A series, the reset may transfer control to a lower level processor, like a Cortex-M, to handle system initialization before returning execution. This allows separating reset and clock configurations from the main application startup.
The compiler and linker play an important role in setting up and generating the reset handler.
A special memory region is defined for the reset vector in the linker script. Code placed here will be located at the reset address during linking. The linker script ensures this code executes first.
The toolchain produces a vector table containing the initial stack pointer value and address of the reset handler function. This table is referenced by the processor on reset.
The toolchain coordinates calling constructors, initializing variables, and linking in crt0 libraries to support C/C++ runtimes. This takes place automatically when linking the final application.
Debugging the Reset Handler
Debugging the earliest code that runs on processor reset requires special techniques like a JTAG debugger. Many factors can prevent code from successfully transitioning from the reset handler to the main application, so carefully validating the startup sequence is key.
Problems like incorrect clock configurations, memory access issues, or stack overflows often manifest first in the reset handler. Toolchain problems around mixer linker sections or incorrect start file use may also surface at this stage. Rigorously testing recovery from processor reset can avoid many field issues.
ARM Cortex-M Reset Handling
The Cortex-M processor family follows a standardized reset sequence defined by ARM. This helps simplify reset handler development across the wide range of Cortex-M MCUs.
On reset, the Cortex-M processor will:
- Load the stack pointer from vector table address 0x0.
- Load the reset vector address from vector table address 0x4.
- Set vector table offset register to 0x0.
- Execute the reset handler code pointed to by the reset vector.
Toolchain Startup Code
The GCC toolchain provides start files and macros that implement common reset initialization for Cortex-M devices. These initialize stacks, zero .bss, call constructors in C++, and clear FPU registers.
The reset handler on Cortex-M chips will often start a bootloader located in internal flash or ROM. This handles more complex initialization before loading the main firmware into external memory.
Reset Handler in Linux Kernel
In the ARM Linux kernel, the reset handler goes through a number of architecture-specific stages before eventually joining into the common kernel startup flow.
The very first code run initializes ARM registers, modes, and security configurations to safe values. Board-specific early initialization also takes place here.
This stage detects the specific ARM platform and then executes silicon and board-specific startup code for that hardware. It provides the low level initialization required before entering common kernel code.
Linux Kernel Entry
The architecture code finally calls into the common Linux kernel startup code. This begins executing architecture-independent initialization like setting up page tables, enabling the MMU, and starting up kernel subsystems to get Linux booted.
Reset Handlers in Other OSs
Besides Linux, the reset handler has a crucial role in other ARM operating systems as well like:
- FreeRTOS – The handler sets up stacks, heap, and any static initialization before launching the RTOS scheduler.
- Rust – The Rust language runtime contains a compiler-generated reset handler that initializes RAM and launches into main().
- Zephyr RTOS – The reset_handler() provides board-level initialization, zeroes .bss, and starts the OS kernel.
- Windows CE – The reset handler in WinCE jumps to nk.exe which handles OS kernel startup.
The reset handler contains the crucial code that runs when an ARM CPU first powers on or resets. It handles low level system initialization before passing control to the application firmware. Understanding ARM reset handling is foundational to building robust embedded software.
While exact implementations vary, reset handlers generally initialize hardware, set up memory, call runtime code, and kick off the system’s main functionality. The reset sequence is a key design consideration on any ARM platform.