sim β€” Simulation Engine

The discrete-event simulation core that drives gem5's timing model, managing object lifecycles, event scheduling, and system-level coordination.

πŸ’‘ Think of it like…

The sim module is like the engine of a car. While other modules (CPU, memory, caches) are the wheels, seats, and body, the sim module provides the crankshaft (EventQueue), the chassis (SimObject hierarchy), and the control computer (System) that makes everything run together in lockstep.

Architecture Overview

The sim module forms the foundation of gem5's discrete-event simulation framework. It provides three pillars: SimObject β€” the universal base class for all simulated components, EventQueue β€” the priority-driven scheduling engine, and System β€” the top-level container binding CPUs, memory, and workloads together.

graph TB
    SimObject["SimObject\n(base class)"]
    ClockedObject["ClockedObject\n(clock domain aware)"]
    System["System\n(top-level container)"]
    Process["Process\n(SE mode)"]
    EventQueue["EventQueue\n(scheduling)"]
    Event["Event\n(scheduled action)"]
    Workload["Workload\n(FS/SE)"]
    PowerState["PowerState"]
    VoltageDomain["VoltageDomain"]

    SimObject --> ClockedObject
    SimObject --> Workload
    SimObject --> Process
    ClockedObject --> System
    Workload --> KernelWorkload["KernelWorkload"]
    Workload --> SEWorkload["SEWorkload"]
    EventQueue --> Event
    System --> VoltageDomain
    ClockedObject --> PowerState
    

SimObject Hierarchy

Every component in gem5 β€” CPUs, caches, buses, memories β€” inherits from SimObject. This base class provides a uniform interface for construction (via Python-generated Params), initialization, serialization (checkpointing), and statistics registration.

SimObject Lifecycle

SimObjects follow a strict initialization protocol:

  1. Construction β€” Python creates Params structs, C++ constructors receive them
  2. init() β€” Post-construction setup, resolve cross-object references
  3. regStats() β€” Register statistics counters
  4. regProbePoints() β€” Register probe points for instrumentation
  5. startup() β€” Schedule initial events, final setup
class SimObject : public EventManager, public Serializable, public Drainable, public statistics::Group
{
  public:
    typedef SimObjectParams Params;
    const Params &params() const;

    SimObject(const Params &p);
    virtual ~SimObject();

    virtual void init();
    virtual void startup();
    virtual void regStats();
    virtual void regProbePoints();
    virtual void regProbeListeners();

    // Serialization for checkpointing
    void serializeSection(CheckpointOut &cp, const char *name) const;
    void unserializeSection(CheckpointIn &cp, const char *name);

    // Find SimObject by name
    static SimObject *find(const char *name);
};

ClockedObject

ClockedObject extends SimObject with clock domain awareness. It associates each object with a ClockDomain, enabling cycle-accurate timing. Components use clockEdge() to align operations to clock boundaries and cyclesToTicks() for time conversion.

class ClockedObject : public SimObject
{
    ClockDomain &clockDomain;
    Tick tick;
    Cycles cycle;

  public:
    Tick clockEdge(Cycles cycles = Cycles(0)) const;
    Tick cyclesToTicks(Cycles c) const;
    Tick clockPeriod() const;
    double voltage() const;
    Cycles ticksToCycles(Tick t) const;
    PowerState *powerState;
};

Event System

gem5 uses a discrete-event simulation model where the EventQueue maintains a time-ordered queue of Event objects. The simulation loop repeatedly dequeues the earliest event, advances simulation time, and invokes the event's process() method.

sequenceDiagram
    participant Main as simulate()
    participant EQ as EventQueue
    participant Ev as Event
    participant Obj as SimObject

    Main->>EQ: serviceOne()
    EQ->>EQ: dequeue head event
    EQ->>Ev: process()
    Ev->>Obj: execute action
    Obj->>EQ: schedule(newEvent, when)
    EQ->>EQ: insert sorted by time+priority
    Main->>EQ: serviceOne() [next iteration]
    

Event Priorities

Events at the same tick are ordered by priority. gem5 defines several standard priorities to ensure correct execution order within a single simulation tick:

PriorityValueDescription
Minimum_Pri-128Earliest possible within a tick
Debug_Enable_Pri-101Debug flag enable/disable
Debug_Break_Pri-100Debugger breakpoints
CPU_Tick_Pri0Default CPU tick events
DVFS_Update_Pri31DVFS transitions
Serialize_Pri32Checkpoint serialization
CPU_Exit_Pri64CPU exit events
Stat_Event_Pri90Pipeline β€” gem5 Wiki" class="xref">Statistics dumping
Sim_Exit_Pri100Simulation exit
Maximum_Pri127Latest possible within a tick

Event Class

class Event : public Serializable
{
  public:
    typedef int8_t Priority;

    Event(Priority p = Default_Pri, Flags f = AutoDelete);
    virtual ~Event();

    virtual void process() = 0;  // Override to define action
    virtual const char *description() const;

    bool scheduled() const;
    Tick when() const;
    Priority priority() const;

    void squash() { flags.set(Squashed); }
    bool squashed() const { return flags.isSet(Squashed); }
};

System and Workloads

The System class is the top-level container that binds all components together. It manages the physical memory address map, tracks all CPUs, provides access to the workload, and coordinates full-system vs. syscall-emulation modes.

System Class

class System : public SimObject
{
  public:
    std::vector<BaseRemoteGDB *> remoteGDB;
    std::vector<ThreadContext *> threads;

    Workload *workload;
    PhysicalMemory physmem;

    Enums::MemoryMode memoryMode;
    AddrRangeList shadowRomRanges;

    // Memory access
    bool isMemAddr(Addr addr) const;
    PortProxy &getPhysMem();
    Addr memSize() const;

    // Thread context management
    void registerThreadContext(ThreadContext *tc);
    int numContexts() const;
    ThreadContext *threads[int id];
};

Workload Hierarchy

The workload abstraction decouples the simulated system from the software being run:

Process & Syscall Emulation

The Process class models a user-space process in syscall emulation (SE) mode. It provides virtual memory (MemState), file descriptor tracking (FDArray), and system call dispatching without requiring a full OS kernel.

class Process : public SimObject
{
  public:
    System *system;
    std::shared_ptr<MemState> memState;
    std::shared_ptr<FDArray> fds;

    Addr brk_point;      // Current brk() address
    Addr stack_base;     // Stack base address
    Addr mmap_end;       // mmap region boundary

    ObjectFile *objFile; // Loaded binary
    SymbolTable *symtab; // Symbol table

    virtual void initState();
    virtual void syscall(ThreadContext *tc);

    // VMA management
    void allocateMem(Addr vaddr, int64_t size, bool clobber = false);
    bool mmapGrowsDown() const;
};

Simulation Control

The simulate() function is the main simulation loop entry point. It processes events from the EventQueue until a simulation exit event fires or the maximum tick count is reached.

// Core simulation entry β€” returns when exit event is triggered
GlobalSimLoopExitEvent *simulate(Tick num_cycles = MaxTick);

// Exit events for terminating simulation
class GlobalSimLoopExitEvent : public GlobalEvent
{
    std::string cause;
    int code;
  public:
    const std::string getCause() const { return cause; }
    int getCode() const { return code; }
};

πŸ” Narrated Walkthrough: Simulation Startup & Event Cycle

  1. Python Configuration β€” User writes a Python script instantiating SimObjects (CPU, Cache Hierarchy | gem5 Wiki" href="../mem_doc/cache/index.html">cache, memory, bus). The Python layer generates Params structs for each.
  2. C++ Construction β€” m5.instantiate() creates all C++ SimObjects from their Params. Each constructor registers with the global SimObject list.
  3. Initialization Phase β€” init() called on all objects (resolve ports, connect wires). Then regStats() registers statistics counters.
  4. Startup Phase β€” startup() called on all objects. CPUs schedule their first tick events. Memory controllers initialize their state machines.
  5. Event Loop β€” simulate() enters the main loop. Each iteration calls eventq->serviceOne(), which dequeues the earliest event and calls process().
  6. Event Cascade β€” A CPU tick event might generate a cache access event, which generates a memory request event, creating a chain of events across the simulated timeline.
  7. Exit β€” When a SimLoopExitEvent fires (e.g., max instructions reached), control returns to Python with the exit cause string.

Key Components Reference

ComponentHeaderPurposeCategory
SimObjectsim_object.hhUniversal base class for all simulated componentsCore
EventQueueeventq.hhTime-ordered priority queue for discrete eventsCore
Eventeventq.hhSchedulable action with time and priorityCore
Systemsystem.hhTop-level container binding all componentsCore
ClockedObjectclocked_object.hhSimObject with clock domain supportCore
Processprocess.hhUser-space process for SE modeCore
Workloadworkload.hhAbstract workload base classConfig
KernelWorkloadkernel_workload.hhFull-system kernel image loadingConfig
SEWorkloadse_workload.hhSyscall-emulation workload managerConfig
VoltageDomainvoltage_domain.hhVoltage domain for DVFSPower
DVFSHandlerdvfs_handler.hhDynamic voltage/frequency scaling controllerPower
PowerDomainpower_domain.hhHierarchical power domain managementPower
TickedObjectticked_object.hhSimObject with periodic tick eventsUtil
GlobalEventglobal_event.hhEvents synchronized across all event queuesCore
FaultBasefaults.hhBase class for architectural faults/exceptionsCore

Sub-Modules

⚑ power

Power state modeling, thermal simulation, DVFS control, and energy estimation for simulated components.

5 classes

πŸ”¬ probe

Non-intrusive instrumentation framework using ProbePoints and ProbeListeners for runtime monitoring.

4 classes

Cross-Module Connections

graph LR
    sim["sim\n(SimObject, EventQueue)"]
    base["base\n(types, stats, debug)"]
    cpu["cpu\n(BaseCPU)"]
    mem["mem\n(MemObject)"]
    arch["arch\n(ISA)"]
    dev["dev\n(devices)"]

    base -->|"Tick, Addr types"| sim
    sim -->|"SimObject base"| cpu
    sim -->|"SimObject base"| mem
    sim -->|"SimObject base"| dev
    sim -->|"System, Workload"| arch
    

Glossary

TermDefinition
TickThe fundamental time unit in gem5 (typically 1 ps = 10-12 s)
SE ModeSyscall Emulation β€” runs user binaries by trapping syscalls to the host OS
FS ModeFull System β€” boots a real OS kernel on simulated hardware
DrainMechanism to quiesce all components before checkpointing or switching CPU models
CheckpointSerialized snapshot of entire simulation state for resume
DVFSDynamic Voltage and Frequency Scaling β€” runtime power management
Probe PointNamed instrumentation hook for non-intrusive monitoring