First, understand the names
Armv7-A is an application-profile architecture generation commonly associated with 32-bit AArch32 execution. Armv8-A introduced AArch64, a 64-bit execution state, while also supporting AArch32 in many implementations. Support for AArch32 is optional in some later Armv8-A and newer implementations, so always check the specific CPU.
AArch32 and AArch64 are execution states, not processor models. A CPU implementing Armv8-A may run AArch64 software and, where implemented, AArch32 software. An operating system's supported state also depends on firmware, boot configuration, and the software image.
At-a-glance comparison
| Area | Armv7-A / AArch32 | Armv8-A / AArch64 |
|---|---|---|
| General-purpose registers | R0–R15 in the classic programmer's model, with banked registers in some modes | X0–X30, plus the stack pointer selected for the current exception level |
| Instruction set | A32 and, on supporting implementations, 16-bit Thumb encodings | A64 in AArch64; AArch32 execution can be available as a separate state |
| Addressing | 32-bit virtual addresses in the usual AArch32 application model | 64-bit registers and an architecturally defined virtual-address model; implemented address width is configuration-dependent |
| Exception model | Processor modes and banked registers; exceptions use vectors and mode transitions | Exception levels EL0–EL3, with stack pointers and vector tables structured around exception level and execution state |
| System registers | Many system controls are accessed through CP15 in common AArch32 implementations | AArch64 uses named system registers accessed with instructions such as MRS and MSR |
| MMU and translation | Short-descriptor or long-descriptor translation formats depending on configuration and extensions | AArch64 translation regimes use translation tables and configurable translation granules, commonly 4 KiB, 16 KiB, or 64 KiB where supported |
| Typical software context | 32-bit Linux, RTOS firmware, and embedded applications | 64-bit operating systems, newer firmware stacks, and applications needing a larger address space |
Registers: R0–R15 versus X0–X30
In AArch32, general-purpose registers are conventionally named R0 through R15. The roles of registers such as the stack pointer, link register, and program counter are tied to the AArch32 programmer's model, and exception modes can provide banked registers.
In AArch64, the general-purpose registers are X0–X30. Their 32-bit views are named W0–W30; writing a W register zero-extends the result into its corresponding X register. X30 is commonly used as the link register, and SP names the active stack pointer in contexts where it is valid.
AArch64 does not expose the program counter as an ordinary general-purpose register. Control flow is handled by branches, and the current instruction address can be obtained using PC-relative addressing patterns such as ADR and ADRP.
Instruction-set differences
AArch32 includes the A32 instruction set and may include Thumb, depending on the implementation. AArch64 uses the A64 instruction set, with fixed-width 32-bit instruction encodings. A64 is not just A32 with wider registers: instruction encodings, calling conventions, addressing forms, and system-register access differ.
For example, AArch64 commonly uses STP and LDP to store and load register pairs, often for function prologues and epilogues. Code ported from AArch32 should be rebuilt and reviewed for the AArch64 ABI rather than mechanically changing register names.
Exception handling: processor modes vs. exception levels
AArch32 traditionally describes privileged execution using modes such as User, Supervisor, IRQ, FIQ, Abort, Undefined, and System, with banked registers for selected modes. Exception vectors and the relevant status registers help the handler identify and restore the interrupted context.
AArch64 organizes privilege into exception levels: EL0 is typically applications, EL1 an operating-system kernel, EL2 a hypervisor, and EL3 secure monitor or firmware. Not every platform uses every level, and the security architecture and software configuration determine the actual transitions.
On exception entry, the processor records return state in exception-related registers such as ELR_ELx and SPSR_ELx, then branches through a vector table selected for the target exception level and the origin context. A handler returns using an exception-return instruction such as ERET.
MMU and address translation
Both architecture generations can use an MMU to translate virtual addresses to physical addresses and enforce access permissions. The formats and control registers differ between AArch32 and AArch64, so early boot code must configure translation tables and control registers according to the current execution state.
In AArch64, TTBR0_EL1 and TTBR1_EL1 commonly point to translation tables for different virtual-address regions, while TCR_EL1 configures translation properties and MAIR_EL1 defines memory attributes. The exact arrangement depends on the operating system and platform. A larger register width does not mean every CPU supports the full 64-bit virtual address space.
Boot and firmware implications
- Reset and initial execution: the reset state and entry point are defined by the processor and platform. Firmware must establish a valid execution environment before transferring control.
- Execution-state transition: a boot chain may start in one supported state and transition to another, but the exact path depends on the CPU, security configuration, and firmware design.
- Exception levels: firmware commonly configures which exception level the operating system enters at and which controls it inherits.
- MMU and caches: startup code must follow the architecture's required sequencing for translation, cacheability, barriers, and instruction synchronization.
- Device tree and kernel image: the bootloader must provide the correct kernel image, device tree, and boot arguments for the target platform and OS build.
What changes when porting low-level code?
- Review pointer sizes, integer assumptions, structure layout, and code that stores addresses in 32-bit variables.
- Use the correct compiler target and ABI; do not mix objects built for AArch32 and AArch64 in the same ordinary process.
- Rewrite inline assembly and exception entry/exit code for the target execution state.
- Update system-register access, interrupt-controller integration, and MMU setup for the target architecture and SoC.
- Verify alignment, calling convention, stack handling, and atomic operations.
- Check whether required features are implemented by the specific core instead of assuming all Armv8-A CPUs are identical.
Practical debugging checklist
- Identify the exact core and architecture revision from the processor documentation.
- Confirm whether the binary is AArch32 or AArch64 using your toolchain's file and ELF inspection tools.
- Inspect the exception level, vector base, translation controls, and current stack during early boot when debugging AArch64 firmware.
- Check linker scripts, image load addresses, device tree, and bootloader hand-off assumptions.
- When an exception occurs, capture the fault address and syndrome registers where applicable, plus the saved return address and processor state.
Key takeaway
Armv7-A is strongly associated with 32-bit AArch32 systems, while Armv8-A introduced AArch64 and a substantially revised execution and exception model. For firmware and kernel engineers, the most important differences are the execution state, register and ABI model, exception handling, system-register interface, and MMU setup—not just the size of an address or register.
This article is an architectural overview. Exact features vary by core and architecture revision; use the Arm Architecture Reference Manual and the technical reference manual for the specific processor when implementing boot or exception code.