o3

o3 — Out-of-Order Superscalar Pipeline

The O3 CPU model is gem5’s most detailed processor model, implementing a superscalar out-of-order pipeline with support for Simultaneous Multi-Threading (SMT). It models a 5-stage pipeline — Fetch, Decode, Rename, Issue/Execute/Writeback (IEW), and Commit — connected through TimeBuffer communication channels, with a Reorder Buffer (ROB) maintaining program order for precise exceptions.

⚙ 5-Stage Pipeline 🔨 Register Renaming 🚀 SMT Support 🎯 Speculative Execution

📚 What is this module?

The o3/ directory implements a detailed out-of-order superscalar CPU that models the behavior of modern high-performance processors like AArch32, AArch64, TrustZone security extensions, SVE/SVE2 vector processing, PMU, and hardware transactional memory." href="../../arch_doc/arm/index.html">ARM Cortex-A76 or Intel Skylake. Instructions can be fetched, decoded, and dispatched out of program order, then executed as soon as their operands are ready, and finally retired in strict program order through the ROB.

💡 Real-world analogy: Airport Security

Think of the O3 pipeline like airport security with multiple parallel lanes. Fetch is the check-in counter handing out boarding passes (instructions) in order. Decode is the document checker verifying each passport. Rename assigns each passenger to an available screening lane (physical register), regardless of their ticket number. IEW is the actual security screening — passengers go through whichever lane is free first, so a passenger with just a backpack (Atomic and Timing Simple CPUs — gem5 Wiki" href="../simple/index.html">simple ALU op) finishes before someone with three suitcases (memory load), even if they arrived later. Commit is the departure gate — passengers must board the plane in their original ticket order, no matter when they cleared security. If security finds a problem (branch misprediction), everyone behind that point is sent back to check-in (squash).

5
Pipeline Stages
48
Source Files
20K
Lines of Code
SMT
Multi-Threading

Class Architecture

The O3 CPU model is organized around a central o3::CPU class that owns all five pipeline stages and the communication buffers between them. Each stage is a separate C++ class that implements tick() logic called every cycle by the CPU.

classDiagram
    class CPU {
        +Fetch fetch
        +Decode decode
        +Rename rename
        +IEW iew
        +Commit commit
        +ROB rob
        +Scoreboard scoreboard
        +TimeBuffer fetchQueue
        +TimeBuffer decodeQueue
        +TimeBuffer renameQueue
        +TimeBuffer iewQueue
        +tick()
        +init()
        +startup()
    }
    class Fetch {
        +BPredUnit branchPred
        +FTQ ftq
        +BAC bac
        +IcachePort icachePort
        +tick()
        +fetch()
        +squash()
    }
    class Decode {
        +tick()
        +decode()
        +squash()
    }
    class Rename {
        +UnifiedRenameMap renameMap
        +FreeList freeList
        +tick()
        +rename()
        +squash()
    }
    class IEW {
        +InstructionQueue instQueue
        +LSQ ldstQueue
        +Scoreboard scoreboard
        +tick()
        +dispatchInsts()
        +executeInsts()
        +writebackInsts()
    }
    class Commit {
        +ROB rob
        +tick()
        +commitInsts()
        +squashAll()
    }
    class ROB {
        +list~DynInstPtr~ instList
        +insertInst()
        +retireHead()
        +squash()
        +isFull()
        +isEmpty()
    }
    class FreeList {
        +getReg()
        +addReg()
        +numFreeRegs()
    }
    class RenameMap {
        +lookup()
        +rename()
        +setEntry()
    }
    CPU --> Fetch
    CPU --> Decode
    CPU --> Rename
    CPU --> IEW
    CPU --> Commit
    CPU --> ROB
    Fetch --> Decode : fetchQueue
    Decode --> Rename : decodeQueue
    Rename --> IEW : renameQueue
    Rename --> ROB : allocate entry
    IEW --> Commit : iewQueue
    Commit --> ROB : retire/squash
    Rename --> RenameMap
    Rename --> FreeList

Key Classes

ClassHeaderResponsibility
o3::CPUo3/cpu.hhTop-level CPU: owns pipeline stages, TimeBuffers, thread contexts. Drives tick() each cycle, handles squashes and thread scheduling.
Fetcho3/fetch.hhFetches instructions from ICache, drives branch predictor, creates DynInst objects. Supports SMT fetch policies (RoundRobin, IQCount, Branch).
Decodeo3/decode.hhValidates decoded instructions, checks PC-relative branches, handles macro-op expansion. Passes instructions to rename queue.
Renameo3/rename.hhMaps architectural registers to physical registers using RenameMap. Allocates ROB entries. Handles squash by restoring old mappings from rename history.
IEWo3/iew.hhCombined Issue/Execute/Writeback stage. Dispatches to IQ and LSQ, issues ready instructions to functional units, writes results back.
Commito3/commit.hhRetires instructions in program order from ROB head. Detects mispredictions, initiates squash signals. Handles non-speculative instruction barriers.
ROBo3/rob.hhReorder Buffer: circular buffer of in-flight DynInsts. Maintains program order for retirement. Drives squash when mispredictions are detected.
InstructionQueueo3/inst_queue.hhIssue queue that tracks operand readiness via dependency graph. Issues instructions to FUs when all sources are ready. Implements age-based priority.
LSQo3/lsq.hhLoad/Store Queue: manages memory ordering, store-to-load forwarding, and memory dependency speculation. Per-thread LSQUnit instances.
LSQUnito3/lsq_unit.hhPer-thread load/store queue. Handles store buffer, load queue, and memory ordering violations that trigger squashes.
FreeListo3/free_list.hhPool of available physical registers. Rename allocates from here; Commit returns old mappings back.
UnifiedRenameMapo3/rename_map.hhMaps architectural register indices to physical register indices. Separate maps for int, float, vec, pred, and misc register classes.
Scoreboardo3/scoreboard.hhTracks which physical registers are ready (have been written). Used by IQ to determine instruction issue readiness.
DynInsto3/dyn_inst.hhDynamic instruction instance carrying sequence number, PC, physical register mappings, execution result, and status flags.
FTQo3/ftq.hhFetch Target Queue: buffers fetch targets from the branch predictor for decoupled fetch.
BACo3/bac.hhBranch Address Calculator: resolves branch targets early in the fetch stage to reduce misprediction penalty.
MemDepUnito3/mem_dep_unit.hhMemory dependency predictor using store sets. Predicts which loads depend on prior stores to enable speculative load execution.
StoreSeto3/store_set.hhStore set memory dependency predictor: tracks store-load conflicts and conservatively serializes predicted-dependent loads.
FUPoolo3/fu_pool.hhPool of functional units shared by the IQ. Manages FU availability, latencies, and pipelining.

Pipeline Data Flow with Stalls and Squashes

This diagram shows the full pipeline interaction including stall signals (back-pressure when structures fill) and squash signals (misprediction recovery):

sequenceDiagram
    participant IC as ICache
    participant FE as Fetch
    participant DE as Decode
    participant RN as Rename
    participant IQ as InstructionQueue
    participant LSQ as LSQ
    participant FU as FuncUnits
    participant ROB as ROB
    participant CM as Commit

    Note over FE: Cycle N: Fetch phase
    FE->>IC: request cache line at PC
    IC-->>FE: return instruction bytes
    FE->>FE: create DynInst, predict branch
    FE->>DE: push to fetchQueue

    Note over DE: Cycle N+1: Decode phase
    DE->>DE: validate, expand macros
    DE->>RN: push to decodeQueue

    Note over RN: Cycle N+2: Rename phase
    RN->>RN: lookup RenameMap for src regs
    RN->>RN: allocate dst phys reg from FreeList
    RN->>ROB: allocate ROB entry
    alt ROB full or IQ full or LSQ full
        RN-->>DE: stall signal (block)
    else resources available
        RN->>IQ: dispatch non-memory insts
        RN->>LSQ: dispatch loads/stores
    end

    Note over IQ,FU: Cycle N+3+: Issue/Execute
    IQ->>IQ: check Scoreboard for ready ops
    IQ->>FU: issue to functional unit
    FU->>FU: execute (1+ cycle latency)
    FU-->>IQ: writeback result, wake dependents
    FU-->>ROB: mark inst completed

    Note over LSQ: Memory operations
    LSQ->>LSQ: check store-to-load forwarding
    LSQ->>IC: send load to DCache
    IC-->>LSQ: data response

    Note over CM: Cycle N+K: Commit
    CM->>ROB: check head entry
    alt head is completed
        CM->>CM: retire instruction
        CM->>RN: free old phys reg
    else head is mispredicted branch
        CM-->>FE: squash(seqNum, correctPC)
        CM-->>DE: squash(seqNum)
        CM-->>RN: squash + restore RenameMap
        CM-->>IQ: squash younger insts
        CM-->>LSQ: squash younger entries
        CM->>ROB: squash younger entries
    end

Cross-Module Interactions

InteractionSourceTargetMechanism
Instruction fetchFetchICache (mem/)RequestPort::sendTimingReq() via IcachePort
Branch predictionFetchBPredUnit (pred/)predict() on each fetched branch
Load/Store accessLSQDCache (mem/)RequestPort::sendTimingReq() via DcachePort
TLB translationFetch/LSQMMU (arch/)MMU::translateTiming() for page walks
Instruction decodeFetchDecoder (arch/)InstDecoder::decode() creates StaticInst
Interrupt handlingCommitInterrupts (arch/)Check pending interrupts at commit, redirect fetch
Functional checkerCommitCheckerCPU (checker/)Verify committed results against functional execution
StatisticsAll stagesStats (base/)statistics::Scalar, statistics::Vector counters

Deep-Dive Topics

Register Renaming

How RenameMap and FreeList eliminate WAR/WAW hazards by mapping architectural registers to a larger physical register file. The rename history buffer enables rollback on squash.

Instruction Queue & Issue Logic

The InstructionQueue dependency graph tracks which instructions are waiting on which physical registers. When a writeback arrives, the IQ wakes all dependents, enabling back-to-back scheduling.

Load/Store Queue & Memory Ordering

The LSQ/LSQUnit manages speculative loads, store-to-load forwarding, and memory ordering violations. The MemDepUnit with StoreSet predictor reduces unnecessary serialization.

Squash & Recovery

When a misprediction is detected at Commit, a squash signal propagates backward through all stages. Each stage removes younger instructions, the RenameMap is restored from the history buffer, and Fetch restarts at the correct PC.

SMT Scheduling Policies

O3 supports multiple SMT policies for each pipeline stage: RoundRobin, IQCount (prioritize thread with fewer IQ entries), Oldest (prioritize oldest instruction), and Branch (prioritize thread with fewer unresolved branches).

TimeBuffer Communication

TimeBuffer<T> in cpu/timebuf.hh models pipeline register latency. Each buffer has configurable forward/backward delay slots, allowing stages to communicate with realistic timing.

Directory Map

o3/ source tree
o3/
├── cpu.hh / cpu.cc               ← o3::CPU: top-level OOO CPU class
├── fetch.hh / fetch.cc           ← Fetch stage: ICache access, branch prediction
├── decode.hh / decode.cc         ← Decode stage: instruction validation
├── rename.hh / rename.cc         ← Rename stage: register renaming
├── iew.hh / iew.cc               ← IEW stage: issue, execute, writeback
├── commit.hh / commit.cc         ← Commit stage: in-order retirement
├── rob.hh / rob.cc               ← Reorder Buffer
├── inst_queue.hh / inst_queue.cc ← Instruction Queue with dependency graph
├── lsq.hh / lsq.cc              ← Load/Store Queue top-level
├── lsq_unit.hh / lsq_unit.cc    ← Per-thread Load/Store Queue
├── dyn_inst.hh / dyn_inst.cc     ← Dynamic instruction representation
├── dyn_inst_ptr.hh               ← Smart pointer for DynInst
├── rename_map.hh / rename_map.cc ← Architectural → Physical register map
├── free_list.hh / free_list.cc   ← Physical register free pool
├── scoreboard.hh / scoreboard.cc ← Physical register readiness tracker
├── fu_pool.hh / fu_pool.cc       ← Functional Unit pool management
├── mem_dep_unit.hh / .cc         ← Memory dependency predictor
├── store_set.hh / store_set.cc   ← Store set algorithm
├── ftq.hh / ftq.cc               ← Fetch Target Queue
├── bac.hh / bac.cc               ← Branch Address Calculator
├── comm.hh                       ← Inter-stage communication structs
├── dep_graph.hh                  ← Dependency graph for IQ
├── limits.hh                     ← Compile-time width/size limits
├── regfile.hh / regfile.cc       ← O3-specific register file
├── thread_context.hh / .cc       ← O3 thread context implementation
├── thread_state.hh / .cc         ← Per-thread state
├── checker.hh / checker.cc       ← Integration with CheckerCPU
├── probe/                        ← O3-specific probe points
├── BaseO3CPU.py                  ← Python parameter definitions
├── O3CPU.py                      ← Python CPU class configuration
├── FUPool.py                     ← Functional unit pool config
└── SConscript                    ← Build definitions

Configuration Parameters

ParameterDefaultDescription
fetchWidth8Max instructions fetched per cycle
decodeWidth8Max instructions decoded per cycle
renameWidth8Max instructions renamed per cycle
dispatchWidth8Max instructions dispatched to IQ/LSQ per cycle
issueWidth8Max instructions issued to FUs per cycle
wbWidth8Max instructions written back per cycle
commitWidth8Max instructions committed per cycle
numROBEntries192Reorder Buffer capacity
numIQEntries64Instruction Queue capacity
LQEntries32Load Queue entries
SQEntries32Store Queue entries
numPhysIntRegs256Physical integer registers
numPhysFloatRegs256Physical floating-point registers
fetchBufferSize64Fetch buffer size in bytes
numThreads1Number of SMT threads

Code Example: O3 Fetch Stage Tick

// Simplified view of Fetch::tick() from o3/fetch.cc
void Fetch::tick()
{
    // Select thread to fetch from (SMT policy)
    ThreadID tid = getFetchingThread();
    if (tid == InvalidThreadID) return;

    // Consult branch predictor for fetch direction
    const auto &pred = branchPred->predict(fetchPC[tid]);

    // Send ICache request
    icachePort.sendTimingReq(createFetchReq(fetchPC[tid]));

    // On cache response: decode instructions, create DynInsts
    for (int i = 0; i < fetchWidth; ++i) {
        auto inst = buildInst(tid, fetchPC[tid]);
        fetchQueue[tid].push(inst);
        if (inst->isControl()) {
            // Update fetch PC based on prediction
            fetchPC[tid] = pred.target;
            break;
        }
        advancePC(fetchPC[tid]);
    }
}

Code Example: Register Rename

// Simplified view of Rename::rename() from o3/rename.cc
void Rename::rename(DynInstPtr &inst)
{
    // For each source operand: look up current mapping
    for (auto &src : inst->srcRegs()) {
        PhysRegIdPtr phys = renameMap->lookup(src.archReg());
        inst->renameSrcReg(src.index(), phys);
    }

    // For each destination: allocate new physical register
    for (auto &dst : inst->destRegs()) {
        PhysRegIdPtr newReg = freeList->getReg(dst.classValue());
        PhysRegIdPtr oldReg = renameMap->lookup(dst.archReg());
        renameMap->setEntry(dst.archReg(), newReg);
        inst->renameDestReg(dst.index(), newReg, oldReg);
        // Record history for squash recovery
        renameHistory.push({dst.archReg(), newReg, oldReg});
    }
}