30分钟跑通你的第一块GPU:tiny-gpu保姆级实战
2026/9/13 4:13:41 网站建设 项目流程

30分钟跑通你的第一块GPU:tiny-gpu保姆级实战

【免费下载链接】tiny-gpuA minimal GPU design in Verilog to learn how GPUs work from the ground up项目地址: https://gitcode.com/GitHub_Trending/ti/tiny-gpu

商业GPU架构文档太厚,开源实现又太绕,想搞懂GPU在硬件层面怎么工作很难下手。tiny-gpu 是一个用 Verilog 写的极简 GPU,仅 15 个源文件、不足 2000 行代码,能在你电脑上仿真完整的矩阵运算内核。读完本文,你可以亲手跑通矩阵乘法,并逐周期看懂它的运行轨迹。路线图:看懂原理 → 跑通示例 → 判断结果 → 动手扩展。

它到底是什么

想学 CPU 怎么工作,网上资源一大把;GPU 就不一样了——现代 GPU 市场竞争太激烈,底层架构细节大多不公开。现成的开源 GPU 项目基本以"功能完整"为目标,看一个文件都要花功夫。tiny-gpu 反着来:砍掉所有图形相关部分,只保留各类现代加速器(GPGPU——用GPU做通用计算而不是画图、ML加速器)通用的核心。

你可以把它理解成一门"GPU 版 CPU 教学课":指令集、线程调度、内存控制器一应俱全,但体量小到能一口气读完。适合想理解硬件并行、但不打算深入图形学方向的人。

一句话定位:tiny-gpu 是一颗"读得完"的教学级 GPU,用可运行的最小系统讲清 GPU 原理。

  • 15 个 Verilog 文件、不到 2000 行代码,全部带注释
  • 内置矩阵加法、矩阵乘法两个内核,可直接仿真
  • "加载程序 → 灌入数据 → 启动内核 → 核对结果"全流程自动化

有了这张全局图,接下来看几个真正驱动它的关键机制。

核心原理速览

只需理解三件事:并行怎么切分、指令长什么样、一条指令怎么走完。

Block 与 Thread 的两级并行

  • 输入:把内核要启动的线程总数写进设备控制寄存器(src/dcr.sv)
  • 过程:调度器(src/dispatch.sv)把线程打包成块 Block,一个核心一次只处理一个块;块内每个线程各配一套 ALU、LSU、程序计数器和寄存器堆
  • 输出:块内线程同步执行,即 SIMD——同一条指令同时处理各线程自己的数据
层级职责对应硬件
Block可并行执行的一组线程一个计算核心
Thread最小执行单元ALU + 寄存器堆 + PC

妙处在于:每个线程的寄存器堆里有 3 个只读寄存器,存着 %blockIdx、%blockDim、%threadIdx。同一段代码,不同线程算出不同数据——这就是"一份代码、多路并行"的根源。

tiny-gpu 的16位指令集

全机只有 11 条指令、每条固定 16 位:LDR/STR 读写全局内存,ADD/SUB/MUL/DIV 做算术,CMP + BRnzp 比较后条件跳转(循环和 if 就这么写出来的),CONST 加载常量,RET 表示执行结束。操作数寄存器只占 4 位,所以一共 16 个寄存器,其中 3 个就是前面说的只读线程标识寄存器。

一条指令的六阶段旅程

  1. FETCH:从程序内存取指令
  2. DECODE:译码成控制信号
  3. REQUEST:LDR/STR 时发起内存请求
  4. WAIT:等内存控制器回数据(全流程唯一的异步步骤)
  5. EXECUTE:ALU 计算
  6. UPDATE:结果写回寄存器堆

为什么 WAIT 要单列?内存带宽有限,多核的请求得排队发出,线程只能停等。真实 GPU 的许多性能优化,正是冲着这段空闲时间去的。

原理讲完,下一步就是让这颗小 GPU 在你电脑上真正跑起来。

30分钟跑通第一个示例

给 tiny-gpu 仿真装好环境

需要三样东西:Icarus Verilog 编译器、cocotb 仿真框架(基于 Python 的硬件测试框架)、sv2v 转换器(把 SystemVerilog 转成 Icarus 能编译的 Verilog)。

git clone https://gitcode.com/GitHub_Trending/ti/tiny-gpu cd tiny-gpu
brew install icarus-verilog pip3 install cocotb

Linux 下直接用发行版包管理器装 iverilog 即可;sv2v 则去它的开源项目主页下载最新的 release 二进制,放进 PATH 里的目录。

一条命令运行矩阵乘法

mkdir build make test_matmul

这行命令背后做的事:sv2v 把 src/ 的 SystemVerilog 合并成一份 Verilog,Icarus 编译它,然后 cocotb 写入内核程序和输入矩阵、启动内核,并逐周期打印执行轨迹。看到成片的轨迹日志输出,就说明已经跑起来了。

结果会落在 test/logs 下的日志文件里,下面教你怎么读。

看懂运行结果

test/logs/ 下的日志按时间戳命名,依次包含:初始数据内存状态、逐周期执行轨迹、最终数据内存状态,末尾一行Completed in N cycles告诉你内核花了几个周期。

结果对不对,一步核对

以 matmul 为例:A、B 均为[[1,2],[3,4]],内核启动 4 个线程,一线程算一个结果元素。

结果元素算式期望值
C[0][0]1×1 + 2×37
C[0][1]1×2 + 2×410
C[1][0]3×1 + 4×315
C[1][1]3×2 + 4×422

怎么判断跑成功了:打开日志末尾的最终内存显示,前 8 字节是输入 A 和 B,紧随的 4 字节是结果矩阵——看到 7、10、15、22 依次出现即正确 ✅。测试脚本自带断言,失败时终端会直接报Result mismatch at index i,并给出期望值和实际值。

轨迹怎么读

轨迹里每行对应一个周期,记录每个核心、每个线程的 PC、正在执行的指令、寄存器值和所处阶段。初读不必逐行,挑一个线程跟住它的 PC 一步步前进,观察它在 WAIT 阶段停了几拍——那就是原理部分说的"等内存"。

结果会看了,再把最容易踩的几个坑提前交代清楚。

常见问题与踩坑

Q:make 报错sv2v: command not foundA:最常见。sv2v 没装或没放进 PATH。终端执行sv2v --version验证,仍报错就重新下载二进制,放到 PATH 目录(如 /usr/local/bin),确保任意目录下能直接调用。

Q:threads 参数是什么?matadd 用 8、matmul 用 4 为什么?A:它是内核启动的线程总数,会写进设备控制寄存器。matadd 逐元素加 8 个数需要 8 个线程;matmul 一线程算一个元素,2×2 共 4 个。改动它要同步改测试脚本里的数据布局。

Q:结果在哪?日志为什么那么长?A:日志在 test/logs/,重复运行每次生成新文件,以最新时间戳为准。先翻到最后的内存显示判断结果错没错,错了再用轨迹按 PC 定位异常周期。

Q:Linux 上没有 brew 怎么办?A:brew 只是 macOS 的包管理器。Linux 用 apt/yum 装 iverilog、pip3 装 cocotb,多出来的只有 sv2v 需要自己下载。

坑扫完,就可以开始改出属于自己的版本了。

下一步去哪儿

性能向(让它更快):

  1. 指令流水线:现在每条指令等上一条彻底结束才取下一条,改 src/scheduler.sv 的状态机,让下一条在 WAIT 期间就开始取指
  2. 内存合并:相邻线程请求相邻地址时合并成一次事务,从 src/lsu.sv 的内存请求路径入手
  3. 缓存:README 里 cache 还是 WIP,可以在核心与内存控制器之间加一层 SRAM 省带宽

功能向(让它更强):

  1. 分支发散:当前假设所有线程每条指令后回到同一 PC,需让 src/pc.sv 支持线程各自记 PC
  2. 加新指令:在 src/decoder.sv 加操作码,到 src/alu.sv 补对应逻辑,比如移位
  3. 共享内存:给块内线程加一段可互读的内存,src/core.sv 层要开新端口

导航收尾:完整架构与 ISA 说明见 README.md;读源码从 src/core.sv 开始;仿真全流程参考 test/test_matmul.py。有扩展想法的话,欢迎直接到项目 issue 页面聊,作者大概率愿意接话。

【免费下载链接】tiny-gpuA minimal GPU design in Verilog to learn how GPUs work from the ground up项目地址: https://gitcode.com/GitHub_Trending/ti/tiny-gpu

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询