arm β€” ARM/AArch64 ISA Implementation

Comprehensive ARM architecture support including AArch32, AArch64, TrustZone security extensions, SVE/SVE2 vector processing, PMU, and hardware transactional memory.

📚 What is this & why does it exist?

πŸ’‘ Think of it like…

The ARM ISA implementation is like a Swiss Army knife β€” compact and versatile. The decoder flips between three blades: 32-bit ARM mode (the full-size knife), 16-bit Thumb mode (the compact tool), and 64-bit AArch64 mode (the modern multi-tool). TrustZone adds a lockbox (Secure World) that even the main blade can't access, while SVE extends the toolkit with adjustable-length vector tools that scale to any width.

Architecture Overview

gem5's ARM implementation is one of the most feature-complete ISA ports, supporting ARMv7 (AArch32) through ARMv8+ (AArch64). It models the full ARM exception model, security extensions, virtualization, and advanced SIMD/SVE vector processing.

graph TB
    subgraph "Decoder"
      ArmDec["ArmISA::Decoder"]
      ARM32["ARM Mode\n(32-bit fixed)"]
      Thumb["Thumb Mode\n(16/32-bit)"]
      A64["AArch64 Mode\n(32-bit fixed)"]
      ArmDec --> ARM32
      ArmDec --> Thumb
      ArmDec --> A64
    end

    subgraph "Security Model"
      EL0["EL0 (User)"]
      EL1["EL1 (Kernel)"]
      EL2["EL2 (Hypervisor)"]
      EL3["EL3 (Secure Monitor)"]
      NS["Non-Secure World"]
      S["Secure World\n(TrustZone)"]
    end

    subgraph "Extensions"
      PMU["PMU\n(counters)"]
      SVE["SVE/SVE2\n(scalable vectors)"]
      HTM["HTM\n(transactions)"]
      Semi["Semihosting\n(host I/O)"]
    end

    EL0 --> EL1 --> EL2 --> EL3
    EL3 --> NS
    EL3 --> S
    

Instruction Decoder

The ARM decoder handles three instruction sets within the same architecture: ARM (32-bit fixed-width), Thumb (16/32-bit variable), and AArch64 (32-bit fixed-width but different encoding). The current execution state determines which decoder path is used.

namespace ArmISA {

class Decoder : public InstDecoder
{
  public:
    void moreBytes(const PCStateBase &pc, Addr fetchPC) override;
    bool instReady() const override;
    StaticInstPtr decode(PCStateBase &nextPC) override;

  private:
    // Decode based on current execution state
    StaticInstPtr decodeInst(ExtMachInst mach_inst);

    // AArch32 decode paths
    StaticInstPtr decodeArm(ExtMachInst mach_inst);
    StaticInstPtr decodeThumb(ExtMachInst mach_inst);

    // AArch64 decode path
    StaticInstPtr decodeAArch64(ExtMachInst mach_inst);

    bool bigThumb;    // In second half of 32-bit Thumb
    bool aarch64;     // AArch64 execution state
    ExtMachInst emi;
};

Exception Levels

ARM uses a hierarchical exception level model that cleanly separates privilege levels and security states:

LevelNameTypical UseKey Registers
EL0UserApplication codeSP_EL0
EL1KernelOS kernel, device driversSCTLR_EL1, TTBR0_EL1, TTBR1_EL1
EL2HypervisorVirtual machine managerHCR_EL2, VTTBR_EL2
EL3Secure MonitorTrustZone firmware (ATF)SCR_EL3

TrustZone Security

TrustZone partitions the system into Secure and Non-Secure worlds. EL3 (Secure Monitor) mediates transitions. gem5 models the full security extension including secure memory regions, secure interrupts, and world switching.

// Security state check
bool isSecure(ThreadContext *tc) {
    SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);
    return !scr.ns;  // NS bit = 0 means Secure
}

// Exception model considers security state for:
// - Memory access permissions (secure vs non-secure)
// - Interrupt routing (FIQ to Secure World)
// - Page table selection (secure/non-secure TTBR)
// - Device access (TrustZone-aware peripherals)

Fault Handling

ARM faults implement the full exception model including precise exception levels, syndrome registers, and fault address reporting.

namespace ArmISA {

class ArmFault : public FaultBase
{
  protected:
    ExceptionLevel toEL;     // Target exception level
    OperatingMode toMode;    // Target mode (AArch32)
    FaultSource faultSource; // Hardware or software fault

  public:
    void invoke(ThreadContext *tc, const StaticInstPtr &inst) override;

    // ARM-specific: update syndrome register
    virtual uint32_t iss() const = 0;      // Instruction Specific Syndrome
    virtual ExceptionClass ec() const = 0; // Exception Class

    // Determine target EL based on routing rules
    ExceptionLevel targetEL(ThreadContext *tc) const;
};

// Specific faults
class UndefinedInstruction : public ArmFault { /* EC=0x00 */ };
class SupervisorCall : public ArmFault { /* EC=0x15 (SVC) */ };
class HypervisorCall : public ArmFault { /* EC=0x16 (HVC) */ };
class SecureMonitorCall : public ArmFault { /* EC=0x17 (SMC) */ };
class DataAbort : public ArmFault {
    Addr faultAddr;     // FAR_ELx
    bool isWrite;
    /* EC=0x24 (lower EL) or 0x25 (same EL) */
};
class PrefetchAbort : public ArmFault { /* EC=0x20/0x21 */ };

} // namespace ArmISA

SVE/SVE2 β€” Scalable Vector Extension

gem5 supports ARM's Scalable Vector Extension with configurable vector lengths (128-2048 bits). SVE uses predication for per-element control and a programming model that is vector-length agnostic.

// SVE registers are scalable β€” width set at configuration time
// Z0-Z31: Vector registers (VL bits wide)
// P0-P15: Predicate registers (VL/8 bits wide)
// FFR: First Fault Register

// Vector length configuration (in Python config)
// system.cpu.isa.sve_vl = 256  # 256-bit vectors (4 doubles)

Hardware Transactional Memory

gem5 models ARM's TME (Transactional Memory Extension) including transaction start/commit/abort and conflict detection.

class HTMCheckpoint
{
  public:
    void save(ThreadContext *tc);
    void restore(ThreadContext *tc);

    // Transaction state
    bool isValid() const;
    int getNumRetries() const;
    Addr getAbortAddress() const;

  private:
    std::array<RegVal, NumIntRegs> intRegs;
    PCStateBase *pcState;
    int nesting;  // Transaction nesting depth
};

Performance Monitoring Unit

The ARM PMU models hardware performance counters that count architectural events. It uses the probe framework to attach to CPU events without modifying pipeline code.

// ARM PMU event types
enum PMUEvent {
    SW_INCR        = 0x00,  // Software increment
    L1I_CACHE_REFILL = 0x01, // L1 I-cache refill
    L1D_CACHE_REFILL = 0x03, // L1 D-cache refill
    L1D_CACHE      = 0x04,  // L1 D-cache access
    INST_RETIRED   = 0x08,  // Instruction retired
    EXC_TAKEN      = 0x09,  // Exception taken
    BR_MIS_PRED    = 0x10,  // Branch misprediction
    CPU_CYCLES     = 0x11,  // CPU cycle count
    BR_PRED        = 0x12,  // Branch predicted
    MEM_ACCESS     = 0x13,  // Data memory access
    // ... many more event types
};

πŸ” Narrated Walkthrough: AArch64 Exception Entry

  1. Trigger β€” A DataAbort fault occurs when the CPU accesses an unmapped page at EL0 (user space).
  2. Target EL β€” The fault routing logic checks HCR_EL2 and SCR_EL3 to determine the target exception level. For a standard OS, data aborts route to EL1.
  3. State Save β€” The processor saves PSTATE to SPSR_EL1 and the return address to ELR_EL1. The faulting address is written to FAR_EL1.
  4. Syndrome β€” ESR_EL1 is loaded with the exception syndrome: EC (exception class = DataAbort), ISS (instruction-specific syndrome including DFSC, WnR bit).
  5. Vector Jump β€” PC is set to VBAR_EL1 + offset (0x000 for synchronous from lower EL using AArch64). Execution continues at the kernel's exception handler.
  6. Handler β€” The OS reads FAR_EL1 and ESR_EL1 to determine what happened, maps the page, then returns via ERET to resume the faulting instruction.

Key Components Reference

ComponentFilePurpose
Decoderdecoder.hhARM/Thumb/AArch64 instruction decoder
ArmISA::ISAisa.hhARM ISA state (system registers, CPSR/PSTATE)
ArmMMUArmMMU.pyARM MMU with multi-level page table walk
ArmFaultfaults.hhException hierarchy with syndrome generation
Interruptsinterrupts.hhGIC (Generic Interrupt Controller) interface
PMUArmPMU.pyPerformance counter unit using probe listeners
HTMCheckpointhtm.hhTransactional memory checkpoint/restore
ArmSystemArmSystem.pyARM-specific system config (GIC, EL support)

Glossary

TermDefinition
AArch3232-bit ARM execution state (ARMv7 compatible)
AArch6464-bit ARM execution state (ARMv8+)
ThumbCompact 16/32-bit instruction encoding for AArch32
Exception Level (EL)ARM privilege hierarchy: EL0 (user) through EL3 (secure monitor)
TrustZoneHardware security extension partitioning Secure and Non-Secure worlds
SVEScalable Vector Extension β€” variable-length SIMD with predication
GICGeneric Interrupt Controller β€” ARM's standard interrupt distribution mechanism
PSTATEProcessor State register in AArch64 (condition flags, exception mask, EL)