ARMv8 A Cheat Sheet - MarekBykowski/readme GitHub Wiki
ARMv8-A / A-profile β Interview Handbook
A readable walkthrough of the ARMv8-A application profile (the "A-profile" that runs Linux, hypervisors and secure firmware). AArch64 is the focus; AArch32 is covered where it differs. Every abbreviation is spelled out the first time. Source of truth: Arm Architecture Reference Manual (A-profile) + Arm Learn the architecture guides β https://www.arm.com/architecture/learn-the-architecture/a-profile. Use this for recall; verify exact bit fields in the ARM ARM.
1. Execution States
An Execution state is the fundamental "width" the CPU runs in. ARMv8-A has two:
- AArch64 β the 64-bit state. Runs the A64 instruction set. This is what modern Linux/Android, KVM and TF-A use.
- AArch32 β the 32-bit state, backwards-compatible with ARMv7-A. Runs A32 ("ARM") and T32 ("Thumb") instruction sets. Kept for legacy 32-bit apps and OSes.
| AArch64 | AArch32 | |
|---|---|---|
| Reg width | 64-bit | 32-bit |
| GPRs | 31 Γ X0βX30 (+ SP, PC, XZR) |
16 Γ R0βR15, banked per mode |
| ISA | A64 | A32 + T32 (Thumb) |
| Privilege | EL0βEL3 (Exception levels) | PL0/PL1/PL2 + processor modes |
Why "banked" matters: in AArch32 the CPU does not have one flat set of 16 registers. Several registers (notably the stack pointer R13/SP, link register R14/LR, and the saved status SPSR) are duplicated per processor mode β so switching mode silently swaps in a different SP. AArch64 abandons that: it has 31 flat general-purpose registers and only the stack pointer is banked per Exception level.
How state changes. The Execution state is tied to the Exception level and can only change when the Exception level changes:
- Taking an exception (moving to a higher EL): the CPU may switch AArch32 β AArch64.
- Returning from an exception (moving to a lower EL, via
ERET): it may switch AArch64 β AArch32.
You never flip state mid-stream inside one EL.
Key rule: a lower EL cannot be AArch64 while a higher EL is AArch32 β you can't run a wider world underneath a narrower one. The width of each level is chosen by the level above it:
SCR_EL3.RWfixes EL2/EL1's width,HCR_EL2.RWfixes EL1/EL0's width.
2. Exception Levels, Privilege & Security
ARMv8-A splits software into four Exception levels (EL). "Exception level" is just Arm's word for a privilege ring β the name comes from the fact that you move up a level only by taking an exception.
| EL | Typical software | What it can do |
|---|---|---|
EL0 |
Applications (user space) | least privilege; no system registers |
EL1 |
OS kernel (Linux) | manages EL0, owns stage-1 MMU |
EL2 |
Hypervisor (KVM, Xen) | virtualizes EL1 guests, stage-2 MMU |
EL3 |
Secure Monitor / firmware (TF-A = BL31) | owns the Secure/Non-secure switch |
You go up only via an exception (interrupt, system call, fault) and down only via ERET (exception return). EL0 has no system-register access at all β it asks EL1 for everything through SVC system calls.
The AArch32 equivalent: PL0/PL1/PL2 and processor modes
AArch32 doesn't use the EL0βEL3 names directly. Instead it has Privilege Levels (PL) and, underneath them, classic ARM processor modes:
- PL0 = unprivileged = user space (the image's "PL0 user-space"). Maps to EL0.
- PL1 = privileged / kernel. Maps to EL1.
- PL2 = hypervisor. Maps to EL2.
The "+ modes" in the table are the AArch32 processor modes β a ARMv7 concept ARMv8 still honours in 32-bit state. Each mode is a different operating context, and most of them bank their own SP, LR and SPSR:
| Mode | Abbr | Priv / EL | Entered when / used for |
|---|---|---|---|
| User | usr |
PL0 / EL0 | normal application code (the only unprivileged mode) |
| System | sys |
PL1 / EL1 | privileged code that shares the User registers |
| Supervisor | svc |
PL1 / EL1 | after reset and after an SVC β the OS kernel |
| IRQ | irq |
PL1 / EL1 | taking a normal interrupt |
| FIQ | fiq |
PL1 / EL1 | fast interrupt β also banks R8βR12 for low latency |
| Abort | abt |
PL1 / EL1 | data or prefetch abort (memory fault) |
| Undefined | und |
PL1 / EL1 | undefined / trapped instruction |
| Hyp | hyp |
PL2 / EL2 | hypervisor |
| Monitor | mon |
(Secure) / EL3 | secure monitor, the TrustZone gateway |
So "PL0/PL1/PL2 + modes" means: three privilege levels, with PL1 further split into several banked modes (svc, irq, fiq, abt, und, sys). This banking is exactly what AArch64 throws away in favour of flat registers + a single vector table.
Security states (TrustZone)
Orthogonal to the Exception levels is the Security state:
- Secure vs Non-secure β chosen by
SCR_EL3.NS. The Non-secure world (Linux, apps) cannot see Secure memory or peripherals; the Secure world (trusted OS, firmware) can see both. - EL3 is the gatekeeper: only the Secure Monitor at EL3 flips
NSand brokers calls between the two worlds (via theSMCinstruction). - Newer cores (Armv9 / v8.5 RME β Realm Management Extension) extend this to four worlds: Root, Secure, Realm, Non-secure (
SCR_EL3.NSE:NS), adding confidential-compute "Realms".
3. Registers & Processor State (AArch64)
The general-purpose register file is deliberately simple:
X0βX30β 31 flat 64-bit registers.W0βW30are the low 32-bit views (writing aWregister zeroes the top 32 bits).X30is the Link Register (LR) βbl/blrwrite the return address here.XZR/WZRβ the zero register: reads as 0, writes are discarded. (Register number 31 decodes as eitherXZRorSPdepending on the instruction.)PCβ the program counter is not a general register in AArch64 (unlike AArch32'sR15). You can'tmovit around; you change it with branches andERET.SPβ the stack pointer is banked per Exception level:SP_EL0,SP_EL1,SP_EL2,SP_EL3. TheSPSelbit chooses whether the current EL uses its ownSP_ELx(writtenELxh) or sharesSP_EL0(ELxt).
PSTATE β the current processor state
PSTATE is the live collection of status bits (the AArch64 replacement for the AArch32 CPSR). The ones worth knowing:
N Z C Vβ the condition flags (Negative, Zero, Carry, oVerflow), set by compares and used byB.cond/CSEL.DAIFβ four interrupt masks: Debug, A = SError (async abort), I = IRQ, F = FIQ. Set a bit to disable that exception.EL(current level),SP(which stack pointer),nRW(current width).- Security/hardening bits:
PAN(Privileged Access Never),UAO,SSBS,BTYPE(for BTI).
You read/write these with the MRS (read) and MSR (write) instructions.
Procedure Call Standard (AAPCS64) β who owns which register
| Registers | Role |
|---|---|
X0βX7 |
function arguments and return values |
X8 |
indirect result location (big struct return) |
X9βX15 |
caller-saved scratch (temporaries) |
X16/X17 |
IP0/IP1 β intra-procedure-call scratch (linker veneers) |
X18 |
platform register (reserved by the OS/ABI) |
X19βX28 |
callee-saved (a function must preserve these) |
X29 |
Frame Pointer (FP) |
X30 |
Link Register (LR) |
Key system registers (suffix _ELx, x = 1/2/3)
| Register | What it controls |
|---|---|
SCTLR_ELx |
System Control β MMU on (M), caches (C,I), alignment (A), endianness (EE) |
VBAR_ELx |
Vector Base Address β where the exception table lives |
ELR_ELx |
Exception Link Register β where to return to |
SPSR_ELx |
Saved PSTATE β processor state at the moment of exception |
ESR_ELx |
Exception Syndrome β why the exception happened |
FAR_ELx |
Fault Address β the VA that faulted |
TTBR0/1_ELx, TCR_ELx, MAIR_ELx |
translation table bases, control, memory attributes |
Cross-level control registers:
SCR_EL3β Secure Configuration:NS(world),RW(lower-EL width),EA/IRQ/FIQrouting to EL3,HCE/SMD(enableHVC/disableSMC).HCR_EL2β Hypervisor Configuration:RW,TGE(trap EL0 to EL2),AMO/IMO/FMO(route SError/IRQ/FIQ to EL2),VM(enable stage-2),E2H(VHE).CPACR_EL1/CPTR_ELxβ enable or trap FP/SIMD/SVE.
4. Floating-Point & NEON (Advanced SIMD)
AArch64 has one 128-bit vector register file shared by scalar floating-point and SIMD. SIMD = Single Instruction, Multiple Data β one instruction operates on several "lanes" at once. A scalar add computes A0+B0 per instruction; a SIMD add computes A0+B0, A1+B1, A2+B2, A3+B3 in a single instruction β e.g. ADD V2.4S, V0.4S, V1.4S adds four 32-bit lanes in parallel:
V0βV31, 128-bit each. Scalar FP math and NEON (Advanced SIMD, parallel operations on multiple lanes) use the same registers.- Each register has narrower views by name:
Q= 128-bit (whole),D= 64,S= 32,H= 16,B= 8. For SIMD the register is sliced into lanes (e.g.V0.4S= four 32-bit floats). - Control/status:
FPCR(rounding mode, flush-to-zero, default-NaN) andFPSR(cumulative exception flags). - Must be enabled:
CPACR_EL1.FPEN(andCPTR_EL2/CPTR_EL3must not trap). If disabled, the first FP/SIMD instruction takes a trap (syndromeEC=0x07). Kernels exploit this for lazy FP context save. - ABI (AAPCS):
V0βV7pass FP args/returns;V8βV15are callee-saved (but only the low 64 bitsD8βD15are preserved);V16βV31are caller-saved. - Data types: FP16/FP32/FP64, BF16 (machine learning), integer SIMD, saturating and polynomial arithmetic.
SVE / SVE2 (Scalable Vector Extension) is the modern successor to NEON for HPC/ML. Instead of a fixed 128-bit width it uses vector-length-agnostic registers Z0βZ31 whose width is implementation-defined (128β2048 bits) β the same binary runs on any width. It adds predicate registers P0βP15 (per-lane masks) and the first-fault register FFR. Enabled separately via CPACR.ZEN.
5. Instruction Sets β A64 vs A32/T32
| A64 (AArch64) | A32 / T32 (AArch32) | |
|---|---|---|
| Width | fixed 32-bit | A32 fixed 32-bit; T32 mixed 16/32-bit |
| PC access | not a register | R15 = PC (reads as +8/+4) |
| Conditional | only B.cond, CSEL, CCMP⦠|
most instructions conditional |
| Registers | 31 flat GPRs | 16, banked per mode |
| Addressing | PC-relative ADRP, clean imm |
barrel shifter, literal pools |
- A32 ("ARM") β the classic 32-bit fixed-width encoding.
- T32 ("Thumb"/Thumb-2) β mixes 16- and 32-bit encodings for code density; conditional execution comes via
IT(If-Then) blocks. - A64 is a clean redesign: it drops per-instruction conditional execution (you use
CSEL/CSINC/B.condinstead), drops load/store-multiple (LDM/STM), drops register banking, and makesPCandSPspecial rather than general. The payoff is simpler decoding, 31 registers, and native 64-bit operations.
6. LPAE (AArch32 only)
LPAE = Large Physical Address Extension β the 64-bit page-table descriptor format for 32-bit CPUs. It exists to break the 4 GB wall: a 32-bit VA can only name 4 GB, but LPAE lets the physical address reach 40 bits so the system can have more RAM, and it's required for hardware virtualization (stage-2) on AArch32.
| Short descriptor (classic) | Long descriptor (LPAE) | |
|---|---|---|
| Enable | default | TTBCR.EAE = 1 |
| Levels | 2 | 3 |
| Descriptor | 32-bit | 64-bit |
| VA | 32-bit (4 GB) | 32-bit, split TTBR0/TTBR1 |
| PA | 32-bit (40-bit via supersections) | up to 40-bit |
In AArch64 there is no "LPAE switch" β the 64-bit long-descriptor format is the only translation format, so this chapter is really an AArch32 compatibility note.
7. Exception Handling
An exception is any event that diverts the CPU to a higher (or same) Exception level: a system call, an interrupt, a memory fault. Handling them is the core job of EL1/EL2/EL3.
Kinds of exception
- Synchronous β caused by an instruction, precisely at that instruction:
SVC(Supervisor Call) β trap to EL1 (a Linux syscall).HVC(Hypervisor Call) β trap to EL2.SMC(Secure Monitor Call) β trap to EL3 (firmware/PSCI).- plus data/instruction aborts (MMU miss, permission fault), undefined instruction, alignment, misaligned PC/SP.
- IRQ / FIQ β asynchronous interrupts delivered by the GIC. FIQ is conventionally the higher-priority/secure one.
- SError β a System Error: an asynchronous external abort (e.g. a bus error reported late).
What the hardware does on entry
- Saves
PSTATEintoSPSR_ELxand the return address intoELR_ELx. - Records the cause in
ESR_ELx(the syndrome: which instruction class, which fault) and, for faults, the address inFAR_ELx. - Masks
DAIF(so the handler isn't immediately re-interrupted), selects the target EL and its stack pointer, and jumps toVBAR_ELx+ a fixed offset.
Your handler runs, then executes ERET, which atomically restores PSTATE from SPSR_ELx and jumps to ELR_ELx β dropping back to the interrupted code (possibly at a lower EL).
The vector table (VBAR_ELx)
Unlike AArch32's one-entry-per-exception table, AArch64 has 16 entries arranged as 4 groups of 4, each entry 0x80 bytes apart (room for a short handler/branch):
| Offset | Exception came from⦠| Sync | IRQ | FIQ | SError |
|---|---|---|---|---|---|
| 0x000 | Current EL, using SP_EL0 |
000 | 080 | 100 | 180 |
| 0x200 | Current EL, using SP_ELx |
200 | 280 | 300 | 380 |
| 0x400 | Lower EL, in AArch64 | 400 | 480 | 500 | 580 |
| 0x600 | Lower EL, in AArch32 | 600 | 680 | 700 | 780 |
The source (current vs lower EL, which SP, which state) picks the group; the type (Sync/IRQ/FIQ/SError) picks the slot. Total table = 0x800 bytes. VBAR_ELx is the only thing that tells the CPU where your handlers live β set it up early in boot; until then exceptions jump to whatever VBAR resets to.
Worked example β a system call from EL0 to EL1
A user program calls write(). The C library puts the syscall number in X8, arguments in X0βX2, and executes SVC #0. Follow the registers (this is Figure 10-4):
- EL0 runs
SVC #0at, say, VA0x4007c0. That's a synchronous exception targeting EL1. - Hardware, in one atomic step (going "up" in the figure):
- return address (the instruction after
SVC) βELR_EL1, - current processor state
PSTATEβSPSR_EL1, - cause β
ESR_EL1(EC = 0x15, "SVC from AArch64", + the#imm), - mask
DAIF, switch to EL1, selectSP_EL1, PC = VBAR_EL1 + 0x400β the "lower EL / AArch64 / synchronous" slot.
- return address (the instruction after
- The kernel handler at
VBAR_EL1 + 0x400saves the user registers, readsESR_EL1to confirm it's an SVC, callssys_write, and leaves the result inX0. - The kernel executes
ERET(going "down"): hardware restoresSPSR_EL1βPSTATEandELR_EL1βPC, so the app resumes at the instruction right afterSVC, back at EL0, with the return value inX0.
A data abort (page fault) works identically, except it's taken on the current instruction (which re-executes after the handler fixes the mapping), FAR_EL1 holds the faulting address, and ESR_EL1.EC says "data abort". An IRQ is the same shape too, but it lands in the IRQ slot (VBAR_EL1 + 0x480) and the interrupted instruction boundary β not the instruction itself β is what ELR_EL1 points back to.
Two-level handler: assembly top-half + a C subroutine
In practice the vector slot holds only a tiny assembly top-half that stacks the volatile registers and then calls a C subroutine to do the real work. Two return mechanisms nest:
- Outer (exception) pair β the CPU entered via
PC β ELR_EL1andPSTATE β SPSR_EL1;ERETrestores both and drops back to the interrupted code. This is the same "going up / going down" as the worked example above. - Inner (procedure-call) pair β the C routine is an ordinary AAPCS64 call: the top-half does
PC β LR (X30)on theBL, andRETbranches back throughLR. Pure register save/restore, no exception-level change.
So the handler is a V-into-a-V: ERET for the EL transition on the outside, RET for the function call on the inside.
Routing
Which EL an exception lands in is configurable: HCR_EL2.{AMO,IMO,FMO} can route SError/IRQ/FIQ to EL2 (and TGE makes EL0 traps go to a host at EL2); SCR_EL3.{EA,IRQ,FIQ} route them to EL3. This is how a hypervisor or secure monitor intercepts interrupts.
8. Caches
Caches hide slow memory β on this class of SoC the L3/system cache is only ~24 MB against ~32 GB of DDR, yet a cache hit is roughly a dozen times quicker than a DDR access. The closer to the core, the faster and smaller: L1 < L2 < L3.
Organisation (Cortex-A53 class):
- L1 is split (Harvard): a separate I-cache (instructions) and D-cache (data) per core.
- L2 is unified β holds both code and data β per cluster.
- Inner vs outer: inner caches live inside the CPU cluster (L1, L2); outer caches are L3/L4 (L4 is rare in practice). The LLC (Last-Level Cache) is usually L3 β e.g. part of ARM's CCN (Cache-Coherent Network), ~24 MB, 12-way set-associative (this is the SYSCACHE L3).
- Inclusive caches (like the A53 data side): the effective cache size is the size of the LLC β inner-cache lines are also held in the LLC, so you don't count them twice.
- A cache line is usually 64 bytes β the smallest loadable unit, a contiguous block copied from memory on a miss.
- The data cache is normally PIPT (Physically-Indexed, Physically-Tagged) so there's no aliasing; the I-cache may be VIPT.
- Whether a region is cacheable, and how widely it's shared, comes from the page tables (
AttrIndxβMAIRfor the type,SHfor shareability), not from the cache itself. Caches are globally turned on withSCTLR_ELx.{M,C,I}.
Direct-mapped vs set-associative
In a direct-mapped cache each memory address maps to exactly one cache line. Simple, but addresses one cache-size apart collide: looping over 0x0, 0x100, 0x200, β¦ keeps evicting and re-filling the same line β cache thrashing, and every access pays the miss cost.
A set-associative cache divides storage into equal ways. An address now maps to a set β one candidate line in each way β instead of a single line. So 0x0 and 0x100 land in the same set but can occupy different ways, and both stay cached: no thrashing. The cost: the address tells you the set but not which way the line sits in, so on every lookup you must check the tag in all ways of that set.
Terminology (ARM's definitions)
- Line β the smallest loadable unit: a block of contiguous words from main memory (64 B here).
- Way β a subdivision of the cache; each way is equal in size and indexed the same way.
- Set β the lines at one index value, one taken from each way, grouped together.
- Index β the part of the address that selects which line(s)/set the address may live in.
- Tag β the part of the address stored with the line, identifying which main-memory address it holds.
Worked example β splitting an address into TAG / INDEX / OFFSET
64-bit addressing, 256 B D-cache, 64 B line, 2-way set-associative (deliberately tiny β you'd never ship a 256-byte cache). Each field is as wide as logβ(how many things it selects): every bit doubles the count (a bit is 0 or 1 β Γ2), so bits to number N things = logβN, and it counts bytes / lines / sets β never the 8 bits inside a byte, because we address bytes.
- OFFSET β which byte within the line? 64 bytes β
logβ64 = 6bits β[5:0]. - INDEX β which set? Each way is 256 / 2 = 128 B = 2 lines β 2 sets β
logβ2 = 1bit β[6]. - TAG β the remainder:
64 β 6 β 1 = 57bits β[63:7].
Tag = [63:7] (57 bits) β Index = [6] (1 bit) β Offset = [5:0] (6 bits)
[hi:lo] is inclusive, so it holds hi β lo + 1 bits: [5:0] = 6, [63:7] = 57. On a 64-bit address the top bit is 63, not 64.
The lookup
To find an address the hardware:
- extracts the index (
[6]) and reads the stored tag from that line in every way (both ways here) β this is "looking up a set"; - compares each way's tag against the address's tag β a hit if one matches;
- on a hit, uses the offset (
[5:0]) to pick the right byte/word from the line; - on a miss where the line is valid but holds a different address β evict that line, then linefill from the requested address.
Two important "points"
- PoU β Point of Unification: the point where a single core's I-cache, D-cache and TLB see the same copy of memory. Relevant when you write code and then execute it.
- PoC β Point of Coherency: the point where all observers (every core, DMA) see the same copy. Relevant for sharing with other masters.
Maintenance operations
| Scope | Operations |
|---|---|
| by address | DC CVAC/CVAU/CIVAC/IVAC (clean/invalidate data), IC IVAU (invalidate instr) |
| by set/way | DC CSW/CISW/ISW (used to flush a whole cache level) |
Running freshly written/loaded code (JITs, bootloaders copying the next stage): the I-cache is not automatically coherent with the D-cache. You must:
DC CVAU(push the code out of D-cache to PoU) βDSBβIC IVAU(drop stale instructions) βDSBβISB(flush the pipeline). This was the root of the SYSCACHE cache-coherency bug.
9. Memory Management (MMU) β EL1 & EL0
The MMU translates virtual addresses (VA) to physical addresses (PA) and enforces permissions, using in-memory translation tables walked by hardware.
EL0 and EL1 share one translation regime (EL1&0) with two VA windows, so user and kernel can have separate page tables without reprogramming on every syscall:
| Window | VA looks like | Base register | Size set by |
|---|---|---|---|
| Low | 0x0000_β¦ |
TTBR0_EL1 |
TCR.T0SZ |
| High | 0xFFFF_β¦ |
TTBR1_EL1 |
TCR.T1SZ |
The top VA bits decide which TTBR is used β conventionally user space lives in the low half (TTBR0) and the kernel in the high half (TTBR1). TCR_EL1 (Translation Control) holds the granule sizes, the physical address size (IPS), and the cacheability/shareability of the page-table walks.
ASID (Address Space ID): so the TLB doesn't have to be flushed on every context switch, user translations are tagged with an ASID carried in the TTBR. Entries marked nG (non-global) are ASID-specific; global kernel mappings are shared across all ASIDs.
Granules, levels, descriptors
| Granule | Reach / levels |
|---|---|
| 4 KB | 48-bit VA via levels L0βL3 (52-bit with LPA2) |
| 16 KB | up to 48/52-bit |
| 64 KB | 42/48-bit, 3 levels |
A table walk descends through descriptors: a table descriptor points to the next level; a block descriptor maps a large region (huge page); a page descriptor is the final leaf. Each leaf carries attributes: AP (read/write permission), UXN/PXN (execute-never for user/privileged), AF (access flag), SH (shareability) and AttrIndx (which MAIR memory type).
Other regimes and two-stage translation
- EL2: uses
TTBR0_EL2(andTTBR1_EL2once VHE is on). - EL3: uses
TTBR0_EL3only β a single VA window. - Stage-2 (virtualization): under a hypervisor, a guest's translation is done twice. The guest's own tables map VA β IPA (Intermediate Physical Address, stage-1); the hypervisor's tables (
VTTBR_EL2/VTCR_EL2) then map IPA β PA (stage-2). The guest thinks it owns physical memory; EL2 really controls it.
10. Memory Ordering
ARMv8-A has a weakly-ordered (relaxed) memory model: to go fast, the hardware may reorder, merge and speculate memory accesses. It is other-multi-copy-atomic (once any observer sees a write, all observers that see it see it in a consistent order). Program order between two accesses is guaranteed only when there's a dependency or you insert a barrier.
Memory types (set by the page tables)
- Normal memory β for RAM/ROM. Freely cacheable, reorderable, speculatable. This is where you need barriers for ordering.
- Device memory β for MMIO peripherals, with four strengths spelling out what's allowed:
Device-nGnRnE(strongest, nothing allowed) Β·nGnREΒ·nGREΒ·GRE. The letters mean Gathering (merge accesses), Re-ordering, Early write acknowledgement β eachnforbids one.
Ordering you get for free: data/address/control dependencies, accesses to the same location (coherence), and Device-nGnRnE.
Barriers
The weak model lets the core reorder, merge and speculate accesses; barriers claw ordering back where it matters. Two families: DMB/DSB act on data accesses (and cache/TLB/branch-predictor maintenance); ISB acts on instruction fetch (context).
| Barrier | Acts on | Guarantee |
|---|---|---|
DMB |
data accesses | Orders memory-before vs memory-after: every load/store before it is visible to the shareability domain before any after it. Visibility ordering only β it does not wait for completion, and non-memory instructions may still reorder across it. |
DSB |
data accesses + maintenance | Everything DMB does plus completion: no instruction after it (not just loads/stores) runs until all prior accesses and cache/TLB/branch-predictor maintenance have completed. |
ISB |
instruction fetch | Flushes the pipeline and re-fetches the following instructions, so they are fetched and permission-checked against the current MMU/system-register config. |
DMB β order, don't wait:
LDR x0, [x1] // must be visible to the memory system before the STR below
DMB ISHLD
ADD x2, x2, #1 // non-memory β may run before OR after the LDR is seen
STR x3, [x4] // must be visible after the LDR above
DSB β order and wait for completion. Used after cache/TLB maintenance, and before signalling other cores (e.g. before SEV, so the event only arrives once the data has really landed):
DC ISW, x5 // must COMPLETE before the DSB can complete
STR x0, [x1] // must COMPLETE before the DSB can complete
DSB ISH
ADD x2, x2, #3 // cannot execute until the DSB completes
ISB β refetch under new rules. After writing a system register that affects fetch or translation (SCTLR, TTBR, TCR, VBAR), pair DSB (let the write complete / become visible) then ISB (refetch) β otherwise the next instructions may still run under the old config.
Scope parameter (DMB/DSB; ISB takes none) = access Γ domain:
- access:
LD(order earlier loads before later loads+stores),ST(order stores only), or full (both) β e.g.ISHLD,ISHST,ISH. - domain:
SY(full system) βOSH(outer-shareable) βISH(inner-shareable) βNSH(non-shareable). Narrower = cheaper; pick the smallest that still covers the other observers you care about.
Quick mental model:
DMBβ "A before B in the order other observers see" β ordering of visibility only.DSBβ "A finished before B even starts" β completion, and it stalls the core until then.ISBβ "reload the pipeline under the new MMU / system-register state" β a refetch.
Worked example β publishing freshly written code. A JIT, or a bootloader copying the next stage, writes instructions through the data side, but the core fetches through the instruction side β the two are not automatically coherent. The fix needs completion (DSB, not just the ordering a DMB would give) and then a refetch (ISB):
DC CVAU, Xn // push the written code out of D-cache to the PoU
DSB ISH // WAIT until the clean has completed (DMB would only order, not wait)
IC IVAU, Xn // invalidate the stale instructions in the I-cache
DSB ISH // WAIT until the invalidate has completed
ISB // flush the pipeline β fetch the fresh code
This is exactly the shape of the SYSCACHE cache-coherency fix (see Β§8) β and why DMB alone would have been wrong there: you needed the maintenance to complete, not merely be ordered.
Ordered accesses and atomics
- Acquire / Release (one-way barriers):
LDAR(load-acquire β nothing after it floats before) andSTLR(store-release β nothing before it floats after). The natural building blocks for locks. - Exclusives:
LDXR/STXRwith a local exclusive monitor implement compare-and-swap loops (the classic LL/SC pattern). - LSE (Large System Extensions, v8.1): single-instruction atomics β
LDADD,SWP,CAS,STADDβ¦ β which scale better than LL/SC under contention.
Extras worth knowing
Virtualization (EL2)
A hypervisor at EL2 runs guest OSes at EL1 using two-stage translation (Β§9) and traps: HCR_EL2 can trap guest wait-for-interrupt, certain register writes, etc., so EL2 can emulate hardware. VHE (Virtualization Host Extensions, HCR_EL2.E2H=1) lets a host kernel (like Linux+KVM) run directly at EL2 with an EL2&0 regime and TTBR1_EL2, avoiding costly EL2βEL1 bounces. Guests receive virtual interrupts (vIRQ/vFIQ/vSError) via GICv3 virtualization.
TrustZone / world switch
SCR_EL3.NS selects Secure vs Non-secure; the EL3 Secure Monitor owns the switch. Normal-world software reaches firmware through the SMCCC (SMC Calling Convention): an SMC traps to EL3, which runs PSCI (Power State Coordination Interface β CPU_ON, CPU_OFF, SYSTEM_RESET), SiP (silicon-provider) and TRNG services. Secure memory is fenced off by the interconnect / TZASC. Typical boot chain: BL1 β BL2 β BL31 (EL3 monitor) β BL33 (U-Boot/UEFI) β OS.
Interrupts, timers, power
- GIC (Generic Interrupt Controller, v3/v4): classifies interrupts as SGI (software), PPI (per-core private), SPI (shared peripheral) and LPI (message-based). The per-core CPU interface is accessed through system registers
ICC_*_EL1. - Generic Timer: an always-on counter
CNTPCT_EL0at frequencyCNTFRQ_EL0, with per-EL comparators (CNTPphysical,CNTVvirtual,CNTHPhyp) that raise interrupts. - Power hints:
WFI(wait for interrupt β idle the core) andWFE/SEV(wait-for-event / send-event β used to park spinning cores cheaply).
Interconnect: AMBA β flits β CCN/CMN
Two layers. At the edge, masters speak a protocol from the AMBA family; inside, the interconnect carries everything as flits over a ring (CCN) or mesh (CMN).
- Edge protocols (AMBA):
- AXI4 (AMBA 4) β high-performance, 5 channels (AW/W/B, AR/R), no coherency.
- ACE (AMBA 4) β AXI + coherency: adds snoop channels AC/CR/CD β fully-coherent CPU clusters.
- ACE-Lite (AMBA 4) β one-way / IO coherency (can snoop into caches, isn't itself snooped) β DMA, GPU, accelerators.
- CHI (AMBA 5) β packet/flit-native coherent protocol β the mesh CMN (CCN's successor).
- Transport (flits): a protocol message ("read line X") β packet β flits (flow-control units, where buffering/credits apply) β phits (what physically moves per clock). A 64 B line = 512 bits β e.g. 2 flits on a 256-bit data path. CHI defines four flit classes: REQ / RSP / SNP / DAT.
- Nodes (CCN/CMN): RN-F / RN-I (request node, fully/IO-coherent) Β· HN-F (home node + L3 β the SYSCACHE HN-F nodes
0x20β0x26, modes OFF/SFONLY/HALF/FULL) Β· SN-F (slave node β DMC β DDR).
In one line: AMBA is the protocol at the edge; flits are how it's carried inside. An ACE AC snoop becomes an internal SNP flit; clearing CPUECTLR.SMPEN drops a core from the ACE coherency domain, so the HN-F stops sending it SNP flits. (CCN-512 is the one with the arm-ccn perf driver / DTS binding.)
Architecture versions & headline features
| Version | Added |
|---|---|
| v8.1 | LSE atomics, VHE, PAN |
| v8.2 | FP16, RAS (reliability), 52-bit addressing |
| v8.3 | PAuth β pointer authentication (ROP defence) |
| v8.4 | Secure-EL2, nested virtualization |
| v8.5 | BTI (branch-target ID), MTE (memory tagging) |
| v9.0 | SVE2 as baseline |
| v9.2+ | RME/CCA β Realms, confidential compute (4 worlds) |
Endianness is selectable (SCTLR.EE/E0E; data can be LE or BE, instructions are always little-endian). Alignment checking is SCTLR.A.
TLB & context maintenance β the sequences to remember
- After editing page tables: write the entries β
DSB ISHβTLBI(invalidate; e.g.VAE1ISby VA,ASIDE1ISby ASID,VMALLE1ISall) βDSB ISHβISB. - After changing a system register that affects fetch or translation (
SCTLR,TTBR,TCR,VBAR): issue anISBso it takes effect before the next instructions. - Break-before-make: when changing a live block/page mapping, first install an invalid entry (+ TLBI) before writing the new one, to avoid the CPU seeing two conflicting translations.