gpu-compute

AMD GCN GPU Compute Model โ€” wavefront-based SIMD execution engine

๐Ÿ“ 68 files ๐Ÿ“ 22K LOC ๐Ÿ—๏ธ AMD GCN ๐Ÿ’ป C++
Compute Units Wavefront Scheduling SIMD Execution HSA Runtime Register Management
~12 min read
๐Ÿญ Analogy: GPU compute is like a massive factory floor with thousands of workers (wavefronts) that can all do Atomic and Timing Simple CPUs โ€” gem5 Wiki" href="../cpu_doc/simple/index.html">simple tasks simultaneously. A Shader is the factory, each ComputeUnit is a production line, and the GPUDispatcher is the foreman assigning work orders (kernels) to available lines. Each line has multiple SIMD lanes executing instructions in lockstep across 64 work-items.
Table of Contents

Architecture Overview

The gpu-compute module implements a cycle-level model of AMD GCN-class GPU compute units, including wavefront scheduling, SIMD execution, and HSA-compatible kernel dispatch. The module models the full pipeline from AQL packet submission through GPUCommandProcessor, dispatch via GPUDispatcher, and execution within ComputeUnits containing multiple SIMD lanes.

flowchart TD
    HSA["HSA Runtime / AQL Packets"]
    CmdProc["GPUCommandProcessor"]
    Shader["Shader (GPU Top-Level)"]
    Disp["GPUDispatcher"]
    CU1["ComputeUnit 0"]
    CU2["ComputeUnit 1"]
    CUN["ComputeUnit N"]
    FS["FetchStage"]
    SB["ScoreboardCheckStage"]
    SS["ScheduleStage"]
    ES["ExecStage"]
    GMP["GlobalMemPipeline"]
    LMP["LocalMemPipeline"]
    SMP["ScalarMemPipeline"]
    VRF["VectorRegisterFile"]
    SRF["ScalarRegisterFile"]

    HSA --> CmdProc
    CmdProc --> Shader
    Shader --> Disp
    Disp --> CU1
    Disp --> CU2
    Disp --> CUN
    CU1 --> FS
    FS --> SB
    SB --> SS
    SS --> ES
    ES --> GMP
    ES --> LMP
    ES --> SMP
    SS --> VRF
    SS --> SRF

The Shader object owns all compute units and manages GPU-wide state like aperture registers and active CU tracking. The GPUCommandProcessor receives AQL dispatch packets from the HSA packet processor, reads kernel metadata via DMA, and hands tasks to the dispatcher for workgroup-level scheduling across compute units.

Pipeline Stages

Each ComputeUnit contains a five-stage pipeline that processes wavefronts every cycle:

flowchart LR
    F["FetchStage\n(I-Cache Access)"] --> SC["ScoreboardCheckStage\n(Hazard Detection)"]
    SC --> S["ScheduleStage\n(Resource Arbitration)"]
    S --> E["ExecStage\n(ALU / Mem Issue)"]
    E --> M["Memory Pipelines\n(Global / Local / Scalar)"]
StageClassResponsibility
FetchFetchStageSelects eligible wavefronts, issues I-Cache Hierarchy | gem5 Wiki" href="../mem_doc/cache/index.html">cache requests via per-SIMD FetchUnit instances
ScoreboardScoreboardCheckStageChecks data and structural hazards; classifies wavefront readiness (NRDY_VGPR_NRDY, NRDY_WAIT_CNT, etc.)
ScheduleScheduleStageArbitrates execution resources, reads register operands, manages scheduling state machine (RFBUSY โ†’ RFREADY)
ExecuteExecStageIssues instructions to ALUs or memory pipelines; tracks utilization statistics
MemoryGlobal/Local/ScalarMemPipelineHandles TLB lookups, cache access, ordered response retirement

Kernel Dispatch Flow

sequenceDiagram
    participant App as HSA Application
    participant CP as GPUCommandProcessor
    participant SH as Shader
    participant DI as GPUDispatcher
    participant CU as ComputeUnit
    participant WF as Wavefront

    App->>CP: Submit AQL Dispatch Packet
    CP->>CP: DMA read kernel metadata (MQD)
    CP->>SH: dispatchKernelObject()
    SH->>DI: dispatch(HSAQueueEntry)
    DI->>DI: scheduleDispatch()
    loop For each workgroup
        DI->>CU: Allocate WG resources
        CU->>WF: Create wavefronts (64 work-items each)
    end
    loop Per cycle
        WF->>CU: FetchStage โ†’ ScoreboardCheck โ†’ Schedule โ†’ Execute
        CU->>CU: Memory pipeline access
    end
    WF->>DI: notifyWgCompl()
    DI->>CP: sendCompletionSignal()
    CP->>App: Update HSA signal

The dispatch path begins when the HSA runtime enqueues an AQL packet. The GPUCommandProcessor reads kernel metadata via DMA, then the GPUDispatcher maps workgroups to compute units with available resources. Each workgroup is split into wavefronts of 64 work-items that execute in SIMD lockstep through the pipeline stages.

Key Classes

ClassParentFileDescription
ShaderClockedObjectshader.hhGPU top-level; owns CUs, manages aperture registers, tracks active units
GPUCommandProcessorDmaVirtDevicegpu_command_processor.hhHSA command processor; receives AQL packets, dispatches kernels
GPUDispatcherSimObjectdispatcher.hhWorkgroup dispatcher; maps WGs to CUs, tracks kernel completion
ComputeUnitClockedObjectcompute_unit.hhSingle CU with SIMD lanes, pipeline stages, register files, LDS
WavefrontSimObjectwavefront.hh64 work-items executing in lockstep; holds instruction buffer and state
FetchStageโ€”fetch_stage.hhPer-SIMD instruction fetch via FetchUnit; I-cache interface
ScoreboardCheckStageโ€”scoreboard_check_stage.hhHazard detection; classifies wavefront readiness with enum codes
ScheduleStageโ€”schedule_stage.hhResource arbitration; reads operands, manages schedule lists
ExecStageโ€”exec_stage.hhALU/memory issue; tracks idle duration and utilization
GlobalMemPipelineโ€”global_memory_pipeline.hhOff-chip memory: TLB โ†’ cache; ordered response buffer
LocalMemPipelineโ€”local_memory_pipeline.hhLDS access: shared data within workgroup
VectorRegisterFileRegisterFilevector_register_file.hhVGPRs โ€” per-lane vector register storage
ScalarRegisterFileRegisterFilescalar_register_file.hhSGPRs โ€” uniform scalar registers shared across wavefront
GPUStaticInstGPUStaticInstFlagsgpu_static_inst.hhISA instruction definition; operand info, disassembly
GPUDynInstโ€”gpu_dyn_inst.hhDynamic instruction instance with execution context and atomics
WFBarrierโ€”compute_unit.hhWorkgroup barrier synchronization tracker
LdsChunkโ€”lds_state.hhPer-workgroup slice of Local Data Store memory
RegisterManagerPolicyโ€”register_manager_policy.hhAbstract register allocation/mapping interface

Deep Dive: Wavefront Lifecycle

Wavefront States

A wavefront transitions through several states during its lifetime, managed by the status field:

stateDiagram-v2
    [*] --> S_STOPPED: Created
    S_STOPPED --> S_RUNNING: Resources allocated
    S_RUNNING --> S_STALLED: Structural hazard
    S_STALLED --> S_RUNNING: Hazard cleared
    S_RUNNING --> S_WAITCNT: waitcnt instruction
    S_WAITCNT --> S_RUNNING: Counters reach zero
    S_RUNNING --> S_BARRIER_WAIT: Barrier instruction
    S_BARRIER_WAIT --> S_RUNNING: All WFs arrive
    S_RUNNING --> S_RETURNING: Last instruction
    S_RETURNING --> [*]: Resources freed

Instruction Buffer

Each wavefront maintains an instruction buffer (deque<GPUDynInstPtr>) that is populated by the FetchStage. The scoreboard checks the oldest instruction for data hazards before allowing it to proceed to scheduling. Key classification methods:

Deep Dive: Register Management

The GPU register system has two distinct file types, managed by a pluggable RegisterManagerPolicy:

Register TypeClassScopeWidth
Vector (VGPR)VectorRegisterFilePer-lane (ร—64 work-items)32-bit per lane
Scalar (SGPR)ScalarRegisterFileShared across wavefront32-bit

The RegisterManagerPolicy interface provides virtual methods for allocation checking (canAllocateVgprs, canAllocateSgprs), mapping (mapVgpr, mapSgpr), and freeing. The concrete StaticRegisterManagerPolicy partitions the physical register files statically among wavefront slots.

Each register file supports operand readiness tracking โ€” the ScheduleStage queries operandsReady() before issuing an instruction, and calls scheduleWriteOperands() to reserve write-back slots after execution.

Deep Dive: Memory Pipelines

Global Memory Pipeline

Handles off-chip memory requests through the GPU TLB and cache hierarchy. Maintains an ordered response buffer (gmOrderedRespBuffer) keyed by sequence number to ensure in-order retirement. Tracks inflight loads and stores separately, with configurable queue depths.

Local Memory Pipeline

Provides access to the Local Data Store (LDS), a per-workgroup scratchpad memory. The LdsChunk class represents the allocated memory region with templated read<T>/write<T> access. The pipeline tracks bank conflict cycles for performance analysis.

flowchart LR
    EX["ExecStage"] --> G["GlobalMemPipeline"]
    EX --> L["LocalMemPipeline"]
    EX --> S["ScalarMemPipeline"]
    G --> TLB["GPU TLB"]
    TLB --> Cache["L1 Data Cache"]
    Cache --> L2["L2 Cache / Memory"]
    L --> LDS["LDS (Shared Memory)"]
    S --> STLB["Scalar TLB"]
    STLB --> SC["Scalar Cache"]

Cross-Module Interactions

DependencyModulePurpose
ISA decodingarch/amdgpuGCN instruction set definitions and decoder
Memory systemmemSystem | gem5 Wiki" class="xref">Packet, Request, TLB, Cache interfaces
HSA runtimedev/hsaHSA packet processor, queue entries, signals
Simulation coresimClockedObject, SimObject, EventQueue
GPU devicedev/amdgpuAMDGPUDevice, MMIO, interrupt controller
Base utilitiesbaseTypes, statistics, trace, logging

Directory Map

src/gpu-compute/ โ”œโ”€โ”€ Top-level โ”‚ โ”œโ”€โ”€ shader.hh/cc โ€” GPU top-level object โ”‚ โ”œโ”€โ”€ compute_unit.hh/cc โ€” Individual compute unit โ”‚ โ””โ”€โ”€ gpu_command_processor.hh/cc โ€” HSA command processor โ”œโ”€โ”€ Dispatch โ”‚ โ”œโ”€โ”€ dispatcher.hh/cc โ€” Workgroup dispatch engine โ”‚ โ”œโ”€โ”€ gpu_compute_driver.hh/cc โ€” KFD-compatible driver โ”‚ โ””โ”€โ”€ kernel_code.hh โ€” Kernel metadata structures โ”œโ”€โ”€ Pipeline Stages โ”‚ โ”œโ”€โ”€ fetch_stage.hh/cc โ€” Instruction fetch โ”‚ โ”œโ”€โ”€ fetch_unit.hh/cc โ€” Per-SIMD fetch unit โ”‚ โ”œโ”€โ”€ scoreboard_check_stage.hh/cc โ€” Hazard detection โ”‚ โ”œโ”€โ”€ schedule_stage.hh/cc โ€” Resource scheduling โ”‚ โ”œโ”€โ”€ scheduler.hh/cc โ€” Scheduling policy โ”‚ โ””โ”€โ”€ exec_stage.hh/cc โ€” Execution issue โ”œโ”€โ”€ Memory Pipelines โ”‚ โ”œโ”€โ”€ global_memory_pipeline.hh/cc โ€” Off-chip memory access โ”‚ โ”œโ”€โ”€ local_memory_pipeline.hh/cc โ€” LDS access โ”‚ โ””โ”€โ”€ lds_state.hh/cc โ€” Local data store โ”œโ”€โ”€ Register Files โ”‚ โ”œโ”€โ”€ register_file.hh/cc โ€” Base register file โ”‚ โ”œโ”€โ”€ vector_register_file.hh/cc โ€” VGPRs โ”‚ โ”œโ”€โ”€ scalar_register_file.hh/cc โ€” SGPRs โ”‚ โ”œโ”€โ”€ register_manager_policy.hh โ€” Allocation interface โ”‚ โ”œโ”€โ”€ static_register_manager_policy.hh/cc โ”‚ โ”œโ”€โ”€ pool_manager.hh/cc โ€” Register pool tracking โ”‚ โ””โ”€โ”€ dyn_pool_manager.hh/cc โ€” Dynamic pool management โ”œโ”€โ”€ Instructions โ”‚ โ”œโ”€โ”€ gpu_static_inst.hh/cc โ€” Static instruction โ”‚ โ”œโ”€โ”€ gpu_dyn_inst.hh/cc โ€” Dynamic instruction โ”‚ โ”œโ”€โ”€ gpu_exec_context.hh/cc โ€” Execution context โ”‚ โ””โ”€โ”€ operand_info.hh โ€” Operand descriptors โ”œโ”€โ”€ Wavefront โ”‚ โ”œโ”€โ”€ wavefront.hh/cc โ€” Wavefront state machine โ”‚ โ””โ”€โ”€ comm.hh โ€” Inter-stage communication โ””โ”€โ”€ Misc โ”œโ”€โ”€ misc.hh โ€” Shared enums/typedefs โ””โ”€โ”€ of_scheduling_policy.hh โ€” Oldest-first policy

Configuration Parameters

ParameterDefaultDescription
num_SIMDs4Number of SIMD units per compute unit
wf_size64Work-items per wavefront
simd_width16SIMD lanes executing per cycle
num_scalar_regs2048Physical SGPRs per SIMD
num_vector_regs1024Physical VGPRs per SIMD
max_wfs_per_simd10Maximum concurrent wavefronts per SIMD
lds_size65536LDS capacity in bytes per CU
gm_queue_size16Global memory request queue depth
lm_queue_size16Local memory request queue depth
num_barrier_slots16Workgroup barrier slots per CU

Code Examples

Kernel Dispatch Sequence

// GPUCommandProcessor receives AQL packet and dispatches kernel
void GPUCommandProcessor::submitDispatchPkt(void *raw_pkt,
                                             uint32_t queue_id,
                                             Addr host_pkt_addr)
{
    auto *task = new HSAQueueEntry(aql_pkt, queue_id, host_pkt_addr);
    // DMA read kernel object to get code and metadata
    dispatchKernelObject(task);
}

// GPUDispatcher maps workgroups to compute units
void GPUDispatcher::dispatch(HSAQueueEntry *task)
{
    hsaQueueEntries[task->dispatchId()] = task;
    scheduleDispatch();
}

// Each ComputeUnit creates wavefronts for assigned workgroups
void ComputeUnit::startWavefront(Wavefront *wf, int wg_id,
                                  HSAQueueEntry *task)
{
    wf->kernId = task->dispatchId();
    wf->setStatus(Wavefront::S_RUNNING);
    wf->resizeRegFiles(task->numVgprs(), task->numSgprs());
}

Pipeline Stage Execution

// Each cycle: stages execute in reverse order (exec โ†’ sched โ†’ sb โ†’ fetch)
void ComputeUnit::exec()
{
    // Execute stage โ€” issue to ALUs or memory pipelines
    execStage.exec();

    // Schedule stage โ€” arbitrate resources, read operands
    scheduleStage.exec();

    // Scoreboard โ€” check hazards, populate ready list
    scoreboardCheckStage.exec();

    // Fetch โ€” select wavefronts, issue I-cache requests
    fetchStage.exec();

    // Memory pipelines
    globalMemoryPipe.exec();
    localMemoryPipe.exec();
}

Barrier Synchronization

// WFBarrier tracks workgroup barrier participation
class WFBarrier {
    int _numAtBarrier = 0;
    int _maxBarrierCnt = 0;
public:
    bool allAtBarrier() const { return _numAtBarrier == _maxBarrierCnt; }
    void incNumAtBarrier() { ++_numAtBarrier; }
    void setMaxBarrierCnt(int cnt) { _maxBarrierCnt = cnt; }
    void reset() { _numAtBarrier = 0; _maxBarrierCnt = 0; }
};