Skip to content

rtl/gpu.sv — GPU / AIPU Vector ALU (v1)

A 16-lane parallel INT32 vector ALU — the minimum-viable implementation behind the routing slot that had been reserved-but-unwired since v3. Mirrors npu.sv's IDLE/LOAD/COMPUTE/OUTPUT state-machine shape for consistency, but operates on plain INT32 vectors rather than an INT8/INT16 MAC array.

Where it fits

Instantiated once by base_die_top.sv as gpu_inst — directly on the base die, no UCIe chiplet crossing for this minimum-viable version (unlike the NPU's dedicated link). Dispatched through the same noc_router as CPU/NPU, via custom-0 opcode 0x57.

Ports

Direction Name Width Description
in clk, rst_n 1, 1 Clock / reset
in gpu_valid 1 Dispatch instruction valid
out gpu_ready 1 1 only in IDLE
in gpu_instruction 32 Encoded GPU instruction (opcode 0x57)
out result_valid 1 High during OUTPUT_ST
out result_index 4 Current output lane (0–15)
out result_data 32 result[result_index], INT32 signed
in result_ready 1 Backpressure — hold lane when low

Instruction encoding (opcode 0x57, custom-0)

 [6:0]   = 0x57
 [8:7]   = cmd     : 00=LOAD_A  01=LOAD_B  10=COMPUTE  11=NOP
 [12:9]  = lane    (4-bit, LOAD_A/LOAD_B)
 [14:13] = alu_op  : 00=ADD 01=SUB 10=AND 11=XOR (COMPUTE)
 [31:15] = data    (17-bit signed, sign-extended to 32; LOAD_A/LOAD_B)

data must stay within [-65536, 65535] to round-trip exactly through the 17-bit field — a real hardware limit of the encoding, the same tradeoff npu.sv makes for its narrower instruction-carried operands.

Functionality

Operand vectors: vec_a[0:15], vec_b[0:15], both INT32, persist across instructions — reloading only vec_b (say) keeps vec_a's prior values, letting a fixed operand be reused across multiple COMPUTEs without reloading it.

Compute: all 16 lanes' alu_out[i] = vec_a[i] <alu_op> vec_b[i] combinationally every cycle; COMPUTE_ST latches all 16 into result[] in one cycle.

FSM: IDLE → LOAD_ST → IDLE (one lane write) or IDLE → COMPUTE_ST → OUTPUT_ST → IDLE (16-lane compute + 16-cycle streamed output), identical shape to npu.sv.

Example — random signed ADD, then reload only one operand

# tb/tb_server_dispatch_unit.py: GPUDriver / GPUMonitor / GPUScoreboard
A  = [rng.randint(-60000, 60000) for _ in range(16)]
B1 = [rng.randint(-60000, 60000) for _ in range(16)]
await drv.load_vec_a(A)
await drv.load_vec_b(B1)
await drv.compute(GPU_ALU_ADD)
result1 = await mon.collect()          # result1[i] == A[i] + B1[i]

B2 = [rng.randint(-60000, 60000) for _ in range(16)]
await drv.load_vec_b(B2)               # vec_a NOT reloaded — persists
await drv.compute(GPU_ALU_ADD)
result2 = await mon.collect()          # result2[i] == A[i] + B2[i]

This is run_gpu_random_reload in tb_server_dispatch_unit.md.

  • gpu.md (docs) — full state-machine diagram and wiring notes
  • npu.md — the sibling co-processor this file's FSM shape mirrors
  • base_die_top.md — instantiation site (no UCIe crossing, unlike NPU)