This repository contains a custom GPU Streaming Multiprocessor (SM) designed using a warp-based SIMT (Single Instruction, Multiple Threads) model. It illustrates how multiple warps are executed in a GPU-like pipeline, how per-thread branching and reconvergence is handled, and how a banked scratchpad memory system is managed.
The design now exists both as the original RTL diagrams (walkthrough below) and as
synthesizable SystemVerilog in src/, with an extensive testbench suite in
tb/.
src/ synthesizable RTL for the SM
tb/ one self-checking testbench per module + a full-chip integration test
Makefile build/run everything with Icarus Verilog
| File | Role |
|---|---|
gpu_pkg.sv |
Shared parameters (16 warps × 4 lanes, 32-bit), the alu_op_t enum and instruction opcodes |
if_stage.sv |
Instruction fetch for the scheduled warp |
id_stage.sv |
Decode + per-warp vector register file (write-back port from WB) |
ex_stage.sv |
Per-lane SIMD ALU, address generation, branch compare, EX/MEM→EX forwarding |
mem_stage.sv |
Memory stage; drives and owns the banked LSU |
wb_stage.sv |
Per-lane write-back to the correct warp's registers |
lsu.sv |
Banked scratchpad with bank-conflict detection / replay reporting |
warp_state.sv |
Per-warp PC + active-mask table with sequential advance, branch redirect and pop |
branch_unit.sv |
Classifies all-taken / none-taken / diverged and pushes the fall-through path |
simt_stack.sv |
SIMT reconvergence stack (warp-tagged) |
warp_scheduler.sv |
Round-robin scheduler wiring warp-state + branch-unit + SIMT-stack together |
gpu_core.sv |
Top level: scheduler + 5-stage pipeline + instruction memory, fully connected |
[31:26] opcode [25:21] rd [20:16] rs1 [15:11] rs2 [10:8] funct3 [15:0] imm
| opcode | mnemonic | operation |
|---|---|---|
000001 |
R-type | rd = rs1 (op) rs2, op = funct3 (ADD/SUB/MUL/DIV) |
000010 |
LOAD | rd = MEM[rs1 + imm] |
000011 |
STORE | MEM[rs1 + imm] = rs2 |
000100 |
BRANCH | if (rs1 == rs2) pc = pc + 1 + imm (per-lane, may diverge) |
Requires Icarus Verilog (iverilog ≥ 11):
make # compile + run every testbench
make tb_ex_stage # run a single testbench
make lint # elaborate the whole core without running
make waves # run the integration test and dump gpu_core.vcdEvery testbench is self-checking and prints PASS/FAIL. The suite covers each module
in isolation plus tb_gpu_core, which loads a program and validates arithmetic, the
per-warp register file, shared-memory loads and a store→load round-trip across all
16 warps × 4 lanes.
- The scheduler advances a warp's PC at issue; branch redirection arrives later from
EX/MEM without pipeline squashing, so branch/divergence is exercised at the subsystem
level (
tb_warp_scheduler) while the full-chip test uses straight-line code (warps are interleaved deeply enough that intra-warp hazards drain naturally). - The SIMT stack is a single warp-tagged stack (production GPUs use per-warp stacks).
- The LSU scratchpad returns correct data on a bank conflict while still asserting the
stall/replay_maskoutputs that model the structural port hazard. - Per-lane 32-bit buses are packed (
[NUM_THREADS-1:0][31:0]) for portable cross-module connection.
Below is a step-by-step walkthrough of how the project was originally developed, with each step corresponding to an uploaded image.
The project started with a simple top-level overview of the GPU SM. At this stage, I created a high-level block diagram with major functional units: the 5-stage pipeline, warp scheduler, and memory subsystem. No internal signals were drawn yet—this diagram served as a conceptual starting point to organize the design.
Next, I designed the core 5-stage pipeline: Fetch, Decode, Execute, Memory, and Writeback. In this diagram, I illustrated how instructions flow through each stage, including how data moves between registers, ALUs, and memory interfaces. This stage also highlighted how scalar and vector operations are differentiated and how per-thread lanes could be tracked with active masks.
After the pipeline, I focused on control flow for warps. I diagrammed the Warp State Table, SIMT Stack, and Branch Unit, showing how warps track program counters and active threads. The SIMT stack handles divergence and reconvergence when threads in a warp take different paths through branches. This diagram clarifies how individual threads within a warp execute conditionally and how the branch unit updates the pipeline with the correct active masks.
With the pipeline and control-flow units complete, I integrated the Warp Scheduler. This diagram shows how the scheduler selects the next ready warp using a round-robin scheme and how it interacts with the Warp State Table and SIMT Stack. The scheduler ensures only valid, non-stalled warps are issued to the pipeline and that warp IDs and active masks propagate correctly through the stages.
Next, I designed the Load/Store Unit (LSU) and banked scratchpad memory system. I included four memory banks to show banked access, bank conflict detection, thread replay, and pipeline stalling. The diagram details how each memory request is mapped to a bank, how conflicts are identified, and how stalled threads are managed to maintain correct execution timing.
Finally, I combined all previous components into a complete top-level diagram with signals connecting every module. This diagram illustrates the full GPU SM, showing the pipeline, warp scheduler, SIMT stack, branch unit, warp state, and LSU all working together. Signals like warp IDs, active masks, PC values, and memory addresses are fully integrated, providing a clear end-to-end view of warp execution, memory operations, and control flow.





