base β€” Core Utility Library

Foundational types, data structures, debugging infrastructure, and utility classes used across the entire gem5 simulator.

πŸ’‘ Think of it like…

The base module is like a well-stocked workshop toolbox. Before you can build a house (simulate a processor), you need reliable measuring tapes (Addr, Tick types), screwdrivers (BitUnion for bit manipulation), logbooks (trace/debug), and quality meters (stats). Every other gem5 module reaches into this toolbox daily.

Architecture Overview

The base module provides the shared infrastructure that every gem5 component relies upon. It encompasses fundamental type definitions, address range management, statistics collection, binary loading, debug tracing, and generic data structures. Unlike sim (which manages simulation control) or arch (which defines ISAs), base is entirely ISA-agnostic and simulation-model-agnostic.

graph TB
    subgraph "Types & Primitives"
      types["types.hh\nAddr, Tick, MicroPC"]
      bitunion["bitunion.hh\nBitUnion templates"]
      flags["flags.hh\nFlags<T>"]
    end

    subgraph "Debug & Trace"
      debug["debug.hh\nSimpleFlag, CompoundFlag"]
      trace["trace.hh\nDPRINTF, OstreamLogger"]
    end

    subgraph "Data Structures"
      addrrange["addr_range.hh\nAddrRange"]
      circq["circular_queue.hh\nCircularQueue"]
      satcnt["sat_counter.hh\nSatCounter"]
      refcnt["refcnt.hh\nRefCountingPtr"]
      trie["trie.hh\nTrie"]
    end

    subgraph "I/O & Config"
      inifile["inifile.hh\nINI parser"]
      cprintf["cprintf.hh\nFormatted output"]
      str["str.hh\nString utilities"]
    end

    subgraph "Sub-Modules"
      loader["loader/\nBinary loading"]
      stats["stats/\nStatistics framework"]
    end

    types --> addrrange
    types --> bitunion
    debug --> trace
    

Fundamental Types

The types.hh header defines the core numeric types used throughout gem5. These provide a consistent, portable foundation for address representation, time tracking, and microarchitectural indexing.

namespace gem5 {

// Address type β€” typically 64-bit unsigned
typedef uint64_t Addr;
const Addr MaxAddr = (Addr)-1;

// Simulation time unit (1 Tick = 1 ps by default)
typedef uint64_t Tick;
const Tick MaxTick = (Tick)-1;
const Tick curTick();  // Current simulation time

// Microcode program counter
typedef uint16_t MicroPC;
const MicroPC MicroPCRomBit = 1 << 15;

// RegIndex for register file access
typedef uint16_t RegIndex;

} // namespace gem5

Address Range Management

AddrRange is a versatile class representing contiguous or interleaved address ranges. It supports hashed interleaving for multi-channel memory controllers and provides efficient containment testing.

class AddrRange
{
    Addr _start;
    Addr _end;
    std::vector<Addr> masks;    // Interleaving masks
    uint8_t intlvMatch;          // Interleave match value

  public:
    AddrRange(Addr start, Addr end);
    AddrRange(Addr start, Addr end, const std::vector<Addr> &masks,
              uint8_t intlv_match);

    Addr start() const { return _start; }
    Addr end() const { return _end; }
    Addr size() const;

    bool contains(Addr a) const;
    bool intersects(const AddrRange &r) const;
    bool isSubset(const AddrRange &r) const;

    // Remove interleaving bits to get offset
    Addr removeIntlvBits(Addr a) const;
    Addr addIntlvBits(Addr a) const;

    // Merge contiguous/overlapping ranges
    bool mergesWith(const AddrRange &r) const;
};

Debug and Tracing

gem5's debug system uses compile-time flags and the DPRINTF macro family for conditional tracing. Debug flags are organized into SimpleFlag (individual) and CompoundFlag (groups) categories, controllable at runtime via command-line options.

Debug Flags

namespace debug {

class Flag
{
  public:
    Flag(const char *name, const char *desc);
    const char *name() const;
    virtual void enable() = 0;
    virtual void disable() = 0;
    virtual bool tracing() const = 0;
};

class SimpleFlag : public Flag
{
    bool _tracing = false;
  public:
    void enable() override { _tracing = true; }
    void disable() override { _tracing = false; }
    bool tracing() const override { return _tracing; }
};

class CompoundFlag : public Flag
{
    std::vector<Flag *> _kids;
  public:
    void enable() override;   // Enables all children
    void disable() override;  // Disables all children
};

} // namespace debug

Trace System

// Usage pattern in gem5 source code:
DPRINTF(CacheTags, "Looking up addr %#x in set %d\n", addr, set);
DPRINTFR(Fetch, "Fetching instruction at PC %s\n", pc);

// Trace output logger
namespace trace {
class OstreamLogger : public Logger
{
  public:
    OstreamLogger(std::ostream &os);
    void logMessage(Tick when, const std::string &name,
                    const std::string &flag,
                    const std::string &message) override;
};
} // namespace trace

Bit Manipulation

The BitUnion template system provides type-safe access to bit fields within machine words. It's essential for decoding instruction encodings, register fields, and hardware status registers.

// Define a bit union for an x86 page table entry
BitUnion64(PageTableEntry)
    Bitfield<63>    nx;      // No-execute
    Bitfield<51, 12> ppn;   // Physical page number
    Bitfield<7>     ps;      // Page size
    Bitfield<5>     accessed;
    Bitfield<2>     user;
    Bitfield<1>     write;
    Bitfield<0>     present;
EndBitUnion(PageTableEntry)

// Usage
PageTableEntry pte = readMemory(addr);
if (pte.present && pte.user) {
    Addr physAddr = pte.ppn << 12;
}

Data Structures

CircularQueue

A fixed-size circular buffer used in CPU pipeline implementations (e.g., reorder buffers, load/store queues). Supports random access with automatic wrap-around and iterator-based traversal.

template<typename T>
class CircularQueue
{
    std::vector<T> data;
    uint32_t head, tail, capacity;

  public:
    CircularQueue(uint32_t size);
    T& front();
    T& back();
    void push_back(const T &val);
    void pop_front();
    bool empty() const;
    bool full() const;
    uint32_t size() const;
    T& operator[](uint32_t idx);  // Wrapping access
};

Saturating Counter

Used extensively in branch predictors. A counter that clamps at min/max instead of wrapping around.

class SatCounter8
{
    uint8_t counter;
    uint8_t maxVal;
    uint8_t initialVal;

  public:
    SatCounter8(unsigned bits, uint8_t initial = 0);
    void increment() { if(counter < maxVal) ++counter; }
    void decrement() { if(counter > 0) --counter; }
    operator uint8_t() const { return counter; }
    void reset() { counter = initialVal; }
};

Reference Counting Pointer

RefCountingPtr provides intrusive reference counting for objects inheriting from RefCounted. Used for managing shared ownership of fault objects, requests, and packets.

class RefCounted
{
    mutable int count = 0;
    friend class RefCountingPtr;
  public:
    virtual ~RefCounted() {}
};

template<class T>
class RefCountingPtr
{
    T *data;
  public:
    RefCountingPtr(T *p = nullptr);
    RefCountingPtr(const RefCountingPtr &r);
    ~RefCountingPtr();
    T *operator->() const { return data; }
    T &operator*() const { return *data; }
    operator bool() const { return data != nullptr; }
};

I/O and Configuration

INI File Parser

Parses the config.ini file generated by System and Standard Library β€” the control plane of gem5" href="../python/index.html">Python configuration scripts. Each section maps to a SimObject, with key-value pairs defining parameter values.

class IniFile
{
  public:
    bool load(const std::string &file);
    bool find(const std::string &section, const std::string &entry,
              std::string &value) const;
    bool sectionExists(const std::string &section) const;
    void getSectionNames(std::vector<std::string> &list) const;
    bool printUnreferenced() const;  // Warn about unused params
};

Formatted Output (cprintf)

Type-safe printf-style formatting that works with C++ streams. Used throughout gem5 for consistent output formatting.

// cprintf β€” print to stdout
cprintf("Address: %#x, Size: %d bytes\n", addr, size);

// csprintf β€” return as string
std::string msg = csprintf("CPU%d: PC=%#x", cpuId, pc);

// ccprintf β€” print to arbitrary stream
ccprintf(std::cerr, "Error at tick %d: %s\n", curTick(), err);

Key Components Reference

ComponentHeaderPurposeCategory
Addr, Ticktypes.hhFundamental address and time typesCore
AddrRangeaddr_range.hhAddress range with interleaving supportCore
BitUnionbitunion.hhType-safe bit field access templatesCore
Flags<T>flags.hhTypesafe flags/bitmask containerCore
SimpleFlagdebug.hhIndividual debug trace flagDebug
CompoundFlagdebug.hhGroup of debug flagsDebug
OstreamLoggertrace.hhTrace output to streamsDebug
CircularQueuecircular_queue.hhFixed-size circular bufferData
SatCounter8sat_counter.hhSaturating counter for predictorsData
RefCountingPtrrefcnt.hhIntrusive reference countingData
Trietrie.hhTrie for fast address lookupData
IniFileinifile.hhConfiguration file parserConfig
cprintfcprintf.hhType-safe formatted printingI/O
Callbackcallback.hhCallback list managementUtil
Coroutinecoroutine.hhCooperative coroutine supportUtil

Sub-Modules

πŸ“¦ loader

Binary file loading subsystem β€” reads ELF, a.out, and raw image files, constructs memory images, and manages symbol tables for simulated workloads.

6 classes

πŸ“Š stats

Statistics collection framework β€” scalar counters, vectors, distributions, histograms, and formulas for performance analysis and validation.

12+ classes

Cross-Module Connections

graph LR
    base["base\n(types, debug, data structures)"]
    sim["sim\n(SimObject, Event)"]
    cpu["cpu\n(BaseCPU, pipelines)"]
    mem["mem\n(packets, ports)"]
    arch["arch\n(ISA decoders)"]

    base -->|"Addr, Tick, Flags"| sim
    base -->|"BitUnion, CircularQueue"| cpu
    base -->|"AddrRange, RefCntPtr"| mem
    base -->|"BitUnion, types"| arch
    base -->|"stats, trace"| sim
    

πŸ” Narrated Walkthrough: How Debug Tracing Works

  1. Flag Definition β€” Each module declares debug flags in its SConscript (e.g., DebugFlag('CacheTags')). The build system generates flag registration code.
  2. DPRINTF Macro β€” Source code uses DPRINTF(CacheTags, "msg %x", val). At compile time, this checks if the flag is enabled before formatting the string.
  3. Runtime Activation β€” User passes --debug-flags=CacheTags on the command line. The SimpleFlag::enable() method sets the tracing boolean.
  4. Output Routing β€” When tracing is active, OstreamLogger::logMessage() formats the output with timestamp, object name, and flag name, then writes to the trace file.
  5. Compound Flags β€” Flags like MemoryAccess are compound flags that enable multiple Atomic and Timing Simple CPUs β€” gem5 Wiki" href="../cpu_doc/simple/index.html">simple flags (e.g., CachePort, Bus, DRAM) simultaneously.

Glossary

TermDefinition
Addr64-bit unsigned integer representing a physical or virtual memory address
TickFundamental simulation time unit, default resolution is 1 picosecond
BitUnionTemplate system for type-safe access to individual bits and bit ranges within integers
DPRINTFDebug printf macro β€” conditionally prints trace output when the specified flag is enabled
AddrRangeRepresents a memory address range, possibly with interleaving for multi-channel memory
SatCounterCounter that clamps at maximum/minimum instead of wrapping β€” used in branch predictors
RefCountingPtrSmart pointer using intrusive reference counting for shared object ownership