generic β€” ISA-Independent Base Classes

Abstract interfaces that define the contract between CPU models and ISA implementations, enabling gem5's architecture-agnostic simulation framework.

📚 What is this & why does it exist?

πŸ’‘ Think of it like…

The generic module is like the standard electrical outlets in a building. It doesn't matter whether you plug in an American appliance (x86), a European one (AArch32, AArch64, TrustZone security extensions, SVE/SVE2 vector processing, PMU, and hardware transactional memory." href="../arm/index.html">ARM), or an Asian one (RISC-V) β€” as long as they follow the socket specification (generic base classes), they'll work with any building's wiring (CPU model). The generic module defines those socket specifications: how to decode instructions, translate addresses, manage ISA state, and handle faults.

Architecture Overview

The generic directory contains abstract base classes that every ISA must implement. These classes form the bridge between gem5's ISA-agnostic CPU models (O3, Minor, TimingSimple, KVM) and the ISA-specific behavior (instruction decoding, register layout, fault handling, address translation).

graph TB
    subgraph "Generic Interfaces"
      InstDecoder["InstDecoder\n(instruction fetch/decode)"]
      BaseMMU["BaseMMU\n(memory management)"]
      BaseISA["BaseISA\n(ISA state container)"]
      BaseTLB["BaseTLB\n(TLB interface)"]
      PCStateBase["PCStateBase\n(program counter)"]
    end

    subgraph "CPU Models (consumers)"
      O3["O3CPU"]
      Minor["MinorCPU"]
      Simple["TimingSimpleCPU"]
      KVM["KvmCPU"]
    end

    subgraph "ISA Implementations (providers)"
      X86["X86ISA::*"]
      ARM["ArmISA::*"]
      RISCV["RiscvISA::*"]
    end

    O3 --> InstDecoder
    O3 --> BaseMMU
    O3 --> BaseISA
    Minor --> InstDecoder
    Minor --> BaseMMU
    Simple --> InstDecoder
    Simple --> BaseMMU
    KVM --> BaseISA

    X86 -.->|"implements"| InstDecoder
    ARM -.->|"implements"| InstDecoder
    RISCV -.->|"implements"| InstDecoder
    X86 -.->|"implements"| BaseMMU
    ARM -.->|"implements"| BaseMMU
    RISCV -.->|"implements"| BaseMMU
    

InstDecoder β€” Instruction Decoder Interface

The abstract instruction decoder defines how CPU models feed fetched bytes to the ISA-specific decoder and retrieve decoded StaticInst objects.

class InstDecoder : public SimObject
{
  protected:
    void *machInst;  // ISA-specific machine instruction buffer

  public:
    InstDecoder(const Params &p);

    // Feed fetched bytes from the instruction cache
    virtual void moreBytes(const PCStateBase &pc,
                           Addr fetchPC) = 0;

    // Is a complete instruction available for decoding?
    virtual bool instReady() const = 0;

    // Decode the buffered instruction, advance PC
    virtual StaticInstPtr decode(PCStateBase &nextPC) = 0;

    // Reset decoder state (e.g., after branch, exception)
    virtual void reset() {}

    // Get moreBytesSize: how many bytes to fetch per cycle
    virtual Addr moreBytesSize() const { return sizeof(MachInst); }
};

Decoder Contract

CPU models interact with the decoder through a Atomic and Timing Simple CPUs β€” gem5 Wiki" href="../../cpu_doc/simple/index.html">simple protocol:

  1. Call moreBytes() with fetched Cache Hierarchy | gem5 Wiki" href="../../mem_doc/cache/index.html">cache line data
  2. Check instReady() β€” returns true when enough bytes for a complete instruction
  3. Call decode() to get the StaticInst and advance the PC
  4. Repeat. Call reset() on branch misprediction or exception.

BaseMMU β€” Memory Management Unit Interface

Defines the abstract interface for address translation. Each ISA provides its own MMU implementation with ISA-specific page table formats and TLB organizations.

class BaseMMU : public SimObject
{
  public:
    enum Mode { Read, Write, Execute };

    // Separate TLBs for instructions and data
    BaseTLB *itb;
    BaseTLB *dtb;

    BaseMMU(const Params &p);

    // Atomic translation (blocks until complete)
    virtual Fault translateAtomic(
        const RequestPtr &req,
        ThreadContext *tc,
        Mode mode) = 0;

    // Timing translation (non-blocking, callback)
    virtual void translateTiming(
        const RequestPtr &req,
        ThreadContext *tc,
        Translation *translation,
        Mode mode) = 0;

    // Functional translation (for debug/initialization)
    virtual Fault translateFunctional(
        const RequestPtr &req,
        ThreadContext *tc,
        Mode mode);

    // TLB management
    virtual void flushAll() = 0;
    virtual void demapPage(Addr vaddr, uint64_t asn) = 0;
    virtual void takeOverFrom(BaseMMU *old) {}

    // Get TLBs
    BaseTLB *getITB() const { return itb; }
    BaseTLB *getDTB() const { return dtb; }
};

Translation Callback

// Used by timing mode for asynchronous translation
class BaseMMU::Translation
{
  public:
    virtual ~Translation() {}

    // Called when translation completes
    virtual void finish(const Fault &fault,
                        const RequestPtr &req,
                        ThreadContext *tc,
                        Mode mode) = 0;

    // Called if squashed before completion
    virtual void markDelayed() {}
};

BaseISA β€” ISA State Container

Holds all ISA-specific register state that isn't part of the standard integer/float register files. This includes control registers, status registers, and ISA configuration state.

class BaseISA : public SimObject
{
  public:
    BaseISA(const Params &p);

    // Read/write miscellaneous (system) registers
    virtual RegVal readMiscRegNoEffect(RegIndex idx) const = 0;
    virtual RegVal readMiscReg(RegIndex idx) = 0;

    virtual void setMiscRegNoEffect(RegIndex idx, RegVal val) = 0;
    virtual void setMiscReg(RegIndex idx, RegVal val) = 0;

    // Number of misc registers
    virtual int numMiscRegs() const = 0;

    // Clear all ISA state
    virtual void clear() = 0;

    // Initialize for thread context
    virtual void setThreadContext(ThreadContext *tc);

    // Serialize for checkpointing
    virtual void serialize(CheckpointOut &cp) const override;
    virtual void unserialize(CheckpointIn &cp) override;
};

BaseTLB β€” Translation Lookaside Buffer

class BaseTLB : public SimObject
{
  public:
    BaseTLB(const Params &p);

    // Flush all TLB entries
    virtual void flushAll() = 0;

    // Invalidate entries for a specific page
    virtual void demapPage(Addr vaddr, uint64_t asn) = 0;

    // Take over state from another TLB (CPU switching)
    virtual void takeOverFrom(BaseTLB *otlb) = 0;

    // Statistics
    virtual void regStats() override;
};

PCStateBase β€” Program Counter Abstraction

Different ISAs have different PC representations. x86 needs microcode PCs, ARM needs mode bits, RISC-V needs compressed instruction awareness. PCStateBase provides the polymorphic interface.

class PCStateBase
{
  protected:
    Addr _pc = 0;        // Current instruction address
    Addr _npc = 0;       // Next instruction address
    MicroPC _upc = 0;    // Micro-operation PC
    MicroPC _nupc = 0;   // Next micro-operation PC

  public:
    Addr pc() const { return _pc; }
    void pc(Addr val) { _pc = val; }

    Addr npc() const { return _npc; }
    void npc(Addr val) { _npc = val; }

    MicroPC upc() const { return _upc; }
    MicroPC nupc() const { return _nupc; }

    // Advance PC to next instruction
    virtual void advance() = 0;

    // Check if branching (npc != sequential)
    virtual bool branching() const = 0;

    // Clone for speculative execution
    virtual PCStateBase *clone() const = 0;

    // Comparison for branch prediction validation
    virtual bool equals(const PCStateBase &other) const;

    // Size of current instruction (for NPC calculation)
    virtual Addr instSize() const { return sizeof(MachInst); }
};

How ISAs Implement These Interfaces

graph TB
    subgraph "InstDecoder"
      ID["InstDecoder (generic)"]
      X86D["X86::Decoder\n- Variable length (1-15 bytes)\n- State machine parser\n- Microcode ROM"]
      ArmD["ArmISA::Decoder\n- ARM/Thumb/AArch64 modes\n- Conditional execution\n- Fixed-width per mode"]
      RvD["RiscvISA::Decoder\n- 16 or 32-bit\n- Simple 2-bit length check\n- C-extension expansion"]
    end

    subgraph "BaseMMU"
      MMU["BaseMMU (generic)"]
      X86M["X86::MMU\n- 4-level page tables\n- CR3 page table base\n- Segmentation layer"]
      ArmM["ArmISA::MMU\n- Sv39/Sv48 tables (AArch64)\n- Secure/Non-Secure worlds\n- TTBR0/TTBR1"]
      RvM["RiscvISA::MMU\n- Sv39/Sv48 tables\n- PMP enforcement\n- SATP register"]
    end

    ID --> X86D
    ID --> ArmD
    ID --> RvD
    MMU --> X86M
    MMU --> ArmM
    MMU --> RvM
    

πŸ” Narrated Walkthrough: Adding a New ISA to gem5

  1. Create Directory β€” Add src/arch/myisa/ with SConscript and Kconfig files.
  2. Implement InstDecoder β€” Subclass InstDecoder and implement moreBytes(), instReady(), and decode() for your instruction encoding.
  3. Implement BaseISA β€” Subclass BaseISA to define your ISA's system registers (CSRs, control registers, etc.).
  4. Implement BaseMMU β€” Subclass BaseMMU and BaseTLB with your page table format and translation logic.
  5. Define PCState β€” Subclass PCStateBase with your ISA's PC representation (instruction size, modes, etc.).
  6. Define Faults β€” Create fault classes inheriting from FaultBase for each exception type.
  7. Define StaticInst β€” Implement instruction classes with execute() methods for each opcode.
  8. System and Standard Library β€” the control plane of gem5" href="../../python/index.html">Python Wrappers β€” Create SimObject Python classes (MyISACPU.py, MyISAMMU.py, etc.) for configuration.

Key Components Reference

ComponentHeaderKey MethodsImplemented By
InstDecoderdecoder.hhmoreBytes(), decode(), instReady()x86, ARM, RISC-V, MIPS, SPARC, POWER
BaseMMUmmu.hhtranslateAtomic(), translateTiming(), flushAll()x86, ARM, RISC-V, MIPS, SPARC
BaseISAisa.hhreadMiscReg(), setMiscReg(), clear()x86, ARM, RISC-V, MIPS, SPARC
BaseTLBtlb.hhflushAll(), demapPage(), takeOverFrom()x86, ARM, RISC-V, MIPS, SPARC
PCStateBasepcstate.hhpc(), npc(), advance(), branching()x86, ARM, RISC-V, MIPS, SPARC

Design Patterns

Glossary

TermDefinition
StaticInstDecoded instruction object containing operands, execute method, and flags
ThreadContextPer-thread CPU state providing access to registers, PC, and ISA state
MachInstRaw machine instruction encoding β€” ISA-specific size and format
MiscRegMiscellaneous register β€” ISA-specific system/control registers not in the standard regfile
Functional TranslationInstant address translation for debug or initialization (no timing modeled)
Atomic TranslationBlocking address translation that completes immediately in atomic CPU mode
Timing TranslationNon-blocking translation with callback β€” models real TLB miss latency