minor

minor — In-Order Pipelined CPU

MinorCPU is gem5’s in-order pipelined processor model with four fixed pipeline stages: Fetch1, Fetch2, Decode, and Execute. It provides a balance between simulation speed and timing accuracy, making it ideal for studying in-order processor designs such as AArch32, AArch64, TrustZone security extensions, SVE/SVE2 vector processing, PMU, and hardware transactional memory." href="../../arch_doc/arm/index.html">ARM Cortex-A5/A7 class cores, embedded processors, and pipeline hazard behavior.

⚙ 4-Stage Pipeline 🔨 Scoreboard Hazards 🚀 Configurable FUs 🎯 In-Order Execution

📚 What is this module?

The minor/ directory implements an in-order CPU where instructions flow through the pipeline strictly in program order. Unlike the O3 model, Minor does not reorder instructions — if an instruction stalls (e.g., waiting for a Cache Hierarchy | gem5 Wiki" href="../../mem_doc/cache/index.html">cache miss), all subsequent instructions are blocked behind it. This accurately models simpler processor designs where hardware complexity is traded for power efficiency.

💡 Real-world analogy: Assembly Line

Think of MinorCPU like a factory assembly line with four stations. Each product (instruction) moves from station 1 (Fetch1: get raw materials from warehouse) to station 2 (Fetch2: cut materials to size) to station 3 (Decode: read the blueprint) to station 4 (Execute: assemble the product). Products move through strictly in order — if station 4 is slow on one product (e.g., a cache miss takes extra time), all products behind it on the line must wait. The Scoreboard acts like a foreman checking whether the tools (register values) needed at each station are ready before letting work proceed.

4
Pipeline Stages
26
Source Files
8K
Lines of Code
In-Order
Execution Model

Pipeline Architecture

The MinorCPU pipeline is contained in the Pipeline class (minor/pipeline.hh), which owns the four stages and the MinorBuffer FIFO queues between them. The MinorCPU class (minor/cpu.hh) inherits from BaseCPU and owns the Pipeline instance, the activity recorder, and per-thread state.

flowchart LR
    subgraph Pipeline["minor::Pipeline"]
        F1["Fetch1\nfetch1.hh"]
        F2["Fetch2\nfetch2.hh"]
        DEC["Decode\ndecode.hh"]
        EXE["Execute\nexecute.hh"]
    end
    subgraph Support["Support Structures"]
        SB["Scoreboard\nscoreboard.hh"]
        LSQ["LSQ\nlsq.hh"]
        FU["FuncUnit\nfunc_unit.hh"]
        DI["MinorDynInst\ndyn_inst.hh"]
    end
    subgraph Memory["Memory System"]
        IC["ICache Port"]
        DC["DCache Port"]
    end

    F1 -->|"LineBuffer"| F2
    F2 -->|"InstBundle"| DEC
    DEC -->|"InstBundle"| EXE
    F1 -->|"fetch request"| IC
    EXE -->|"load/store"| LSQ
    LSQ -->|"memory request"| DC
    EXE --- SB
    EXE --- FU
    F2 --- DI

Pipeline Stages in Detail

Fetch1 — Line Fetch

Fetch1 (minor/fetch1.hh) is responsible for fetching raw cache lines from the instruction cache. It generates fetch requests based on the current PC, handles ITLB misses by initiating page walks, and manages the fetch buffer. When the branch predictor redirects the PC, Fetch1 discards in-flight fetch requests and starts fetching from the new target.

Fetch2 — Line Decomposition

Fetch2 (minor/fetch2.hh) takes the raw cache line bytes from Fetch1 and uses the ISA decoder to decompose them into individual MinorDynInst objects. It handles variable-length instruction sets (like x86) by tracking the decode position within the line. Branch prediction also happens here — Fetch2 consults the branch predictor for each control-flow instruction and updates the fetch PC accordingly.

Decode — Micro-op Expansion

Decode (minor/decode.hh) expands macro-ops into micro-ops for ISAs that require it (e.g., x86 complex instructions). For RISC ISAs like ARM or RISC-V where most instructions are already single micro-ops, this stage is largely a pass-through. The decode stage also classifies instructions by type for the Execute stage.

Execute — Scoreboard & Functional Units

Execute (minor/execute.hh) is the most complex stage. It performs hazard checking using the Scoreboard, dispatches instructions to FuncUnit pipelines, handles memory operations through the LSQ, and manages instruction completion. The Execute stage stalls the pipeline when a data hazard is detected (read-after-write on a register not yet produced) or when a structural hazard occurs (no available functional unit).

Key Classes

ClassHeaderResponsibility
MinorCPUminor/cpu.hhTop-level CPU class inheriting BaseCPU. Owns Pipeline, activity recorder, thread state.
Pipelineminor/pipeline.hhContainer for the four stages and inter-stage buffers. Drives evaluate() each cycle.
Fetch1minor/fetch1.hhFetches cache lines from ICache port. Handles ITLB translation and fetch buffering.
Fetch2minor/fetch2.hhDecodes cache line bytes into instructions. Consults branch predictor.
Decodeminor/decode.hhExpands macro-ops to micro-ops. Pass-through for RISC ISAs.
Executeminor/execute.hhHazard check, FU dispatch, memory operations, instruction commit.
Scoreboardminor/scoreboard.hhTracks register write completion to detect RAW hazards.
LSQminor/lsq.hhLoad/Store Queue for memory operation management.
FuncUnitminor/func_unit.hhConfigurable functional unit with operation latency and pipelining.
MinorDynInstminor/dyn_inst.hhDynamic instruction carrying PC, static inst pointer, and execution state.
MinorBufferminor/buffers.hhGeneric FIFO buffer template for inter-stage communication.
PipeDataminor/pipe_data.hhData types passed between stages: ForwardLineData, ForwardInstData.
ExecContextminor/exec_context.hhMinor-specific ExecContext for instruction register/memory access.
MinorActivityRecorderminor/activity.hhTracks stage activity to enable CPU idling when no work is available.
MinorStatsminor/stats.hhPipeline-wide statistics collection (committed insts, cycles, bubbles).

Data Flow Diagram

sequenceDiagram
    participant IC as ICache
    participant F1 as Fetch1
    participant F2 as Fetch2
    participant DEC as Decode
    participant EXE as Execute
    participant SB as Scoreboard
    participant DC as DCache

    F1->>IC: sendTimingReq(PC)
    IC-->>F1: cache line response
    F1->>F2: ForwardLineData (raw bytes)
    F2->>F2: ISA decode: bytes → instructions
    F2->>F2: branch predict each control inst
    F2->>DEC: ForwardInstData (inst bundle)
    DEC->>DEC: macro-op → micro-op expansion
    DEC->>EXE: ForwardInstData (micro-ops)
    EXE->>SB: check source register readiness
    alt RAW hazard detected
        SB-->>EXE: stall (source not ready)
        Note over EXE: pipeline bubbles until ready
    else all sources ready
        EXE->>EXE: dispatch to FuncUnit
        EXE->>EXE: execute (1+ cycles)
        alt memory instruction
            EXE->>DC: sendTimingReq(addr)
            DC-->>EXE: data response
        end
        EXE->>SB: mark dest register ready
        EXE->>EXE: commit instruction
    end

Pipeline Hazards

MinorCPU handles three types of hazards through stalling and forwarding:

Hazard TypeDetectionResolution
RAW (Read After Write)Scoreboard check in Execute: source register not yet marked readyStall pipeline until producing instruction completes and marks the register ready
StructuralNo available FuncUnit for the instruction’s opclassStall until a functional unit becomes free
ControlBranch misprediction detected at ExecuteFlush younger instructions from all stages, redirect Fetch1 to correct target
Memory stallDCache miss or DTLB missBlock Execute stage (and all upstream stages) until memory response arrives
Key difference from O3: MinorCPU does not rename registers or reorder instructions. WAR and WAW hazards cannot occur because instructions execute strictly in program order. Only RAW, structural, and control hazards need handling.

Cross-Module Interactions

InteractionMinor ComponentExternal ModuleMechanism
Instruction fetchFetch1ICache (mem/)RequestPort::sendTimingReq()
Branch predictionFetch2BPredUnit (pred/)predict() / update()
Instruction decodeFetch2InstDecoder (arch/)decode() → StaticInst
TLB translationFetch1, LSQMMU (arch/)translateTiming()
Data memory accessLSQ in ExecuteDCache (mem/)RequestPort::sendTimingReq()
Interrupt checkExecute (commit)Interrupts (arch/)Poll at commit point, redirect fetch on interrupt

Directory Map

minor/ source tree
minor/
├── cpu.hh / cpu.cc               ← MinorCPU: top-level CPU class
├── pipeline.hh / pipeline.cc     ← Pipeline: stage container, evaluate() driver
├── fetch1.hh / fetch1.cc         ← Fetch1: cache line fetch, ITLB
├── fetch2.hh / fetch2.cc         ← Fetch2: instruction decode, branch predict
├── decode.hh / decode.cc         ← Decode: macro → micro-op expansion
├── execute.hh / execute.cc       ← Execute: hazard check, dispatch, commit
├── scoreboard.hh / scoreboard.cc ← Scoreboard: register readiness tracking
├── lsq.hh / lsq.cc              ← LSQ: memory operation management
├── func_unit.hh / func_unit.cc   ← FuncUnit: configurable execution units
├── dyn_inst.hh / dyn_inst.cc     ← MinorDynInst: dynamic instruction
├── buffers.hh                    ← MinorBuffer: inter-stage FIFO template
├── pipe_data.hh / pipe_data.cc   ← ForwardLineData, ForwardInstData
├── exec_context.hh               ← Minor ExecContext for register/memory I/O
├── activity.hh / activity.cc     ← Activity recorder for CPU idling
├── stats.hh / stats.cc           ← Pipeline statistics
├── trace.hh                      ← Debug tracing helpers
├── BaseMinorCPU.py               ← Python parameter class
├── MinorCPU.py                   ← Python CPU configuration
└── SConscript                    ← Build definitions

Configuration Parameters

ParameterDefaultDescription
fetch1LineSnapWidth0Fetch line snap width (0 = cache line size)
fetch1FetchLimit1Max outstanding fetch requests
fetch2InputBufferSize2Buffer between Fetch1 and Fetch2
decodeInputBufferSize3Buffer between Fetch2 and Decode
executeInputBufferSize7Buffer between Decode and Execute
executeMaxAccessesInMemory2Max outstanding memory accesses
executeLSQMaxStoreBufferStoresPerCycle2Max stores committed per cycle
executeBranchDelay1Branch resolution delay in cycles
executeFuncUnits(configured)List of functional unit types with latencies
executeIssueLimit2Max instructions issued per cycle
executeCommitLimit2Max instructions committed per cycle

Code Example: Execute Stage Evaluate

// Simplified view of Execute::evaluate() from minor/execute.cc
void Execute::evaluate()
{
    // Try to commit completed instructions at head of pipeline
    for (int i = 0; i < commitLimit; ++i) {
        MinorDynInstPtr inst = getInput();
        if (!inst) break;

        // Check scoreboard for RAW hazards
        if (!scoreboard.canInstIssue(inst)) {
            // Stall: source operand not ready
            break;
        }

        // Find available functional unit
        FuncUnit *fu = findFuncUnit(inst->staticInst->opClass());
        if (!fu || fu->isBusy()) {
            // Structural hazard: no FU available
            break;
        }

        // Dispatch to functional unit
        fu->issue(inst);

        // For memory ops, send to LSQ
        if (inst->isMemRef()) {
            lsq.pushRequest(inst);
        }
    }

    // Process FU completions and mark registers ready
    for (auto &fu : funcUnits) {
        if (auto inst = fu->getCompletedInst()) {
            scoreboard.markRegReady(inst->destReg());
        }
    }
}

Minor vs O3 Comparison

FeatureMinorCPUO3CPU
Execution orderStrict in-orderOut-of-order
Register renamingNoYes (RenameMap + FreeList)
Pipeline stages4 (Fetch1, Fetch2, Decode, Execute)5 (Fetch, Decode, Rename, IEW, Commit)
ROBNoYes (circular buffer)
SpeculationBranch prediction onlyFull speculative execution + memory dep pred
Simulation speed~3× faster than O3Most detailed, slowest
Target processorsARM Cortex-A5/A7, embeddedARM Cortex-A76, Intel Skylake
Hazard handlingStallRename (WAR/WAW), forward (RAW), speculate