从零用 Verilog 搭建一个能跑的 GPU:tiny-gpu 完整指南(1337 行代码跑通矩阵乘法) 📅 发布时间:2026/9/13 10:31:56 👁 浏览次数: 从零用 Verilog 搭建一个能跑的 GPUtiny-gpu 完整指南1337 行代码跑通矩阵乘法【免费下载链接】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 用 12 个 Verilog 文件、共 1337 行代码造出一台能仿真矩阵乘法的极简 GPU。下面带你装好环境、跑通内核再逐模块拆它的原理。快速上手3 步装好仿真环境先让 GPU 转起来原理稍后再说。仿真链路是iverilogIcarus Verilog 编译器 cocotbPython 写测试激励的框架 sv2v把 SystemVerilog 转成 Verilog 的工具。# 1. 拿到代码 git clone https://gitcode.com/GitHub_Trending/ti/tiny-gpu cd tiny-gpu # 2. 安装工具链 brew install icarus-verilog # Linux 可用包管理器装 iverilog pip3 install cocotb # 3. 下载 sv2v 对应平台的预编译二进制解压后放进 PATH # 4. 准备构建目录 mkdir build装完工具后确认iverilog --version、cocotb-config --prefix、sv2v --version三条命令都有输出。最小命令跑通一个矩阵乘法内核项目用 Makefile 把编译和仿真串好了矩阵乘法和矩阵加法各一个测试用例make test_matmul # 4 个线程算 2x2 矩阵乘法 make test_matadd # 8 个线程做向量加法命令跑完后会生成一个日志文件放在test/logs目录下里面按周期记录了每个线程的 PC、寄存器、ALU 输出和内存操作文末还会打印最终的数据内存状态。内核启动与内存GPU 是怎么领到任务的这部分回答一个基础问题内核代码和数据从哪来、GPU 靠什么知道该干多少活。启动一个内核需要 4 步把内核机器码写进程序内存、把运算数据写进数据内存、在设备控制寄存器DCR见 src/dcr.sv里写入要启动的线程总数、把 start 信号拉高。内存规格刻意做小方便你一眼看全数据内存8 位地址256 行每行 8 位能存 0~255 的数程序内存8 位地址每行 16 位正好装一条指令两套内存各配一个内存控制器负责给请求排队、按带宽限流、把响应送回正确的线程DCR 里只存一个数thread_count。真正的分发是调度器Dispatcher见 src/dispatch.sv干的——它把线程按每块 4 个分组一块一块派给计算核心全部分发完才上报内核执行完毕。计算核心内部4 个线程如何共享一颗核心这一节拆解一块 4 线程的活硬件怎么并行做完。核心模块 src/core.sv 用THREADS_PER_BLOCK参数控制线程数默认 4。每个线程都有一整套独立硬件ALUsrc/alu.sv算 ADD/SUB/MUL/DIV还负责 CMP 比较并把负/零/正结果写进 NZP 寄存器LSUsrc/lsu.sv负责 LDR/STR 访存并扛住异步等待PCsrc/pc.sv程序计数器每条指令默认 1遇 BRnzp 按 NZP 状态跳转——循环和条件就这样实现寄存器堆src/registers.sv16 个寄存器R0~R12 可读写R13~R15 是只读的%blockIdx、%blockDim、%threadIdx那 3 个只读寄存器就是 SIMD单指令多数据的关键同一条指令每个线程拿自己的线程号去算地址各干各的数据。核心里的调度器src/scheduler.sv策略很朴素一个块从头执行到 RET 才接下一个块块内所有线程锁步同步。它主要要对付的敌人是访存延迟——算术指令一个周期就出结果LDR/STR 却要在 WAIT 状态空转若干周期。指令集与六阶段流水线11 条指令够写内核吗这里回答为什么 11 条指令就能表达循环和分支。指令固定 16 位前 4 位操作码后面分寄存器/立即数字段11 条指令分四类算术ADD/SUB/MUL/DIV、访存LDR/STR、控制CMP/BRnzp、杂项CONST 装立即数、RET 结束线程、NOP 空转。矩阵乘法内核里CMP R9, R2加BRn LOOP就是那个 k N 的循环。每条指令在核心状态机里走 6 个阶段状态机实现在 src/controller.sv 和 src/fetcher.svFETCH 取指 → 2. DECODE 译码 → 3. REQUEST 发访存请求 → 4. WAIT 等内存响应 → 5. EXECUTE 执行 → 6. UPDATE 写回寄存器非访存指令会很快跳过 3、4 两阶段这个六段划分牺牲了速度换来的是每一拍你都能在波形里看懂在干什么。实战验证核对矩阵乘法的结果用 2x2 矩阵复现一遍输入两个矩阵都是[[1,2],[3,4]]按行优先平铺在数据内存地址 0~7。make test_matmul cat test/logs/*.log # 打开生成的日志预期结果与手工计算对照A x B [[1,2],[3,4]] x [[1,2],[3,4]] C[0][0] 1*1 2*3 7 C[0][1] 1*2 2*4 10 C[1][0] 3*1 4*3 15 C[1][1] 3*2 4*4 22对照要点日志开头的 data memory 状态应看到1 2 3 4 / 1 2 3 4日志末尾 4 个线程每线程负责 C 的一个元素把7 10 15 22写进了地址 8~11测试脚本 test/test_matmul.py 最后会断言这 4 个值全对才算过每个周期的执行快照长这样能直接看到 4 个线程各自的 PC、寄存器值和 LSU 状态延伸路径跑通之后改哪里README 的 Next Steps 列表就是路线图对应源码入口指令缓存与多级缓存现在取指直接走程序内存加一层缓存能省带宽。入口在 src/fetcher.sv 和 src/gpu.sv顶层已留了 cache 位置标注 WIP。流水线与 warp 调度调度器目前严格一条指令跑完再下一条改成指令重叠或分 warp 轮转能显著提升核心利用率。入口 src/scheduler.sv、src/controller.sv。分支发散与访存合并现在假设所有线程每条指令后 PC 必然收敛放开这个假设或把相邻地址请求合并成一笔事务都是真实 GPU 的核心难题。入口 src/pc.sv 和 src/lsu.sv。想加新指令从 src/decoder.sv 的操作码译码和 test/helpers/format.py 的反汇编格式同步改起即可。关键资源完整架构与 ISA 文档README.md硬件源码12 个 Verilog 模块src/两个内核的仿真用例与辅助脚本test/架构图、ISA 表、执行轨迹图docs/images/打开 test/test_matmul.py 和 src/controller.sv 对照着读一遍你手里就有了一台可以逐拍解剖的 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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考