ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

tiny-gpu 如何启动一个 Kernel:加载程序与数据内存、设置线程数并拉高 start 信号

tiny-gpu 如何启动一个 Kernel:加载程序与数据内存、设置线程数并拉高 start 信号 tiny-gpu 如何启动一个 Kernel加载程序与数据内存、设置线程数并拉高 start 信号【免费下载链接】tiny-gpuA minimal GPU design in Verilog to learn how GPUs work from the ground up项目地址: https://gitcode.com/GitHub_Trending/ti/tiny-gputiny-gpu 是一个用 Verilog 编写的极简 GPU一次只执行一个 kernel。README 中把启动一个 kernel定义为固定的四步操作向 global program memory 写入 kernel 代码、向 data memory 写入所需数据、在 device control register 中指定要启动的线程数、把 start 信号拉高。本文沿着仓库自带的 cocotb 仿真环境把这四步落成一条可执行的完整路径并给出 kernel 执行成功的验证方式。准备条件安装仿真依赖在仓库根目录准备三样依赖README「Simulation」一节给出的安装方式iverilogIcarus Verilog 编译器cocotbPython 仿真测试框架sv2v —— 下载最新版本的 release 二进制解压后放进$PATH仓库源码是 SystemVerilog仿真前需要由它转成 Verilog。README 给出的安装命令为brew install icarus-verilog pip3 install cocotb另外需要在仓库根目录创建build目录Makefile 的编译步骤会往这里写中间产物mkdir buildbuild目录只存放make生成的*.v和sim.vvp不会改动仓库里的源码。Kernel 启动的四个步骤下图是 tiny-gpu 的顶层结构。GPU 由 device control register、dispatcher、若干计算核以及数据/程序内存控制器组成kernel 启动信号start进入 dispatcherdone信号由 dispatcher 在所有 block 执行完毕后拉高。1. 向 program memory 加载 kernel 代码program memory 的规格README「Memory」一节8 bit 寻址共 256 行每行 16 bit正好是一条 ISA 指令的宽度。kernel 代码以指令位流的形式写入 program memory。以 README「Matrix Addition」一节给出的矩阵加法 kernel 为例它的源码形态是.threads 8 .data 0 1 2 3 4 5 6 7 ; matrix A (1 x 8) .data 0 1 2 3 4 5 6 7 ; matrix B (1 x 8) MUL R0, %blockIdx, %blockDim ADD R0, R0, %threadIdx ; i blockIdx * blockDim threadIdx CONST R1, #0 ; baseA (matrix A base address) CONST R2, #8 ; baseB (matrix B base address) CONST R3, #16 ; baseC (matrix C base address) ADD R4, R1, R0 ; addr(A[i]) baseA i LDR R4, R4 ; load A[i] from global memory ADD R5, R2, R0 ; addr(B[i]) baseB i LDR R5, R5 ; load B[i] from global memory ADD R6, R4, R5 ; C[i] A[i] B[i] ADD R7, R3, R0 ; addr(C[i]) baseC i STR R7, R6 ; store C[i] in global memory RET ; end of kernel.threads声明该 kernel 要启动的线程数.data声明要写入 data memory 的初始数据汇编行会被写成对应的 16 bit 指令位流例如 test/test_matadd.py 中MUL R0, %blockIdx, %blockDim对应的就是0b0101000011011110。kernel 的最后一行必须是RETREADME 的 ISA 说明中RET表示当前线程执行结束。2. 向 data memory 加载数据data memory 的规格8 bit 寻址共 256 行8 bit 数据每行存放一个小于 256 的值。数据布局需要和 kernel 里的基地址常量对上。上面的矩阵加法 kernel 中baseA #0、baseB #8、baseC #16因此 test/test_matadd.py 把 16 个数据按顺序写入地址 0–15data [ 0, 1, 2, 3, 4, 5, 6, 7, # Matrix A (1 x 8) 0, 1, 2, 3, 4, 5, 6, 7 # Matrix B (1 x 8) ]地址 16–23 是留给结果矩阵 C 的存储区启动前保持为 0 即可。3. 在 device control register 中写入线程数tiny-gpu 的 device control register 只保存一个字段thread_count即当前 kernel 要启动的线程总数README「Device Control Register」一节。寄存器本身见 src/dcr.svdevice_control_data为 8 bit在时钟上升沿且device_control_write_enable为 1 时写入输出即thread_count。矩阵加法 kernel 声明了 8 个线程所以写入threads 8。4. 拉高 start 信号GPU 顶层 的文件头注释明确了启动前提Assumes that the program is loaded into program memory, data into data memory, and threads into the device control register before the start signal is triggered——程序内存、数据内存和 DCR 都必须先准备好再触发start。start拉高后dispatcher 按total_blocks (thread_count THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK计算总 block 数默认THREADS_PER_BLOCK 4、NUM_CORES 2见 src/gpu.sv 的 parameter 默认值把 block 逐个分发给空闲的计算核当所有 block 都完成后把done置 1kernel 执行结束。实际执行路径setup.py 与 make 目标仓库里的 test/helpers/setup.py 是这四步的完整参考实现顺序和时序如下# Setup Clock clock Clock(dut.clk, 25, unitsus) cocotb.start_soon(clock.start()) # Reset dut.reset.value 1 await RisingEdge(dut.clk) dut.reset.value 0 # Load Program Memory program_memory.load(program) # Load Data Memory data_memory.load(data) # Device Control Register dut.device_control_write_enable.value 1 dut.device_control_data.value threads await RisingEdge(dut.clk) dut.device_control_write_enable.value 0 # Start dut.start.value 1要点先启动 25us 周期的时钟并让reset保持一个时钟周期再拉低程序内存与数据内存由 Python 侧的Memory对象按地址顺序写入见 test/helpers/memory.py 的loadDCR 写使能device_control_write_enable只保持一个时钟周期随后拉低最后把start拉高kernel 开始执行。两个内置 kernel 的完整仿真入口是 Makefile 中的test_%目标在仓库根目录运行make test_matadd make test_matmul每个目标会依次执行三件事先用sv2v把src/下的 SystemVerilog 转成 Verilog 并合并进build/gpu.v再用iverilog -o build/sim.vvp -s gpu -g2012 build/gpu.v编译顶层模块是gpu最后以MODULEtest.test_matadd或test_matmul的形式加载 cocotb 测试模块运行仿真。验证 kernel 是否执行成功仿真运行结束后有两层验证。第一层是日志。README 说明仿真会在test/logs输出日志文件包含初始 data memory 状态、kernel 的完整执行 trace 和最终 data memory 状态。对照文件开头与结尾的内存表应能看到输入矩阵被替换成了结果矩阵trace 部分逐周期记录每个核内每个线程当前执行的指令、PC、寄存器值等状态效果类似下图文档示例第二层是测试断言两个测试脚本各自给出机器可判定的成功条件test/test_matadd.py等待dut.done.value 1后逐元素断言data_memory.memory[i 16] data[i] data[i 8]即结果矩阵 C 位于地址 16 起每个元素等于 A、B 对应元素之和文档示例两矩阵均为0..7时结果为0, 2, 4, 6, 8, 10, 12, 14test/test_matmul.py等待done后断言地址 8 起的 4 个元素等于 2x2 矩阵乘法的对应点积输入 A、B 均为[[1, 2], [3, 4]]。两个测试还会用logger打印Completed in {cycles} cycles记录本次 kernel 实际消耗的时钟周期数具体数值因运行而异只作为观察参考。边界与限制tiny-gpu 一次只执行一个 kernelDCR 也只保存thread_count这一个字段数据内存是 8 bit 值小于 256超出该范围的数据无法存放GPU 假设所有线程每条指令后都会收敛到同一 PC不支持分支发散README「PCs」与「Branch Divergence」两节的说明。因此 matmul kernel 能跑通的前提是所有分支最终收敛编写 kernel 时需要遵守这一点start必须在程序内存、数据内存与 DCR 全部就绪之后拉高否则启动行为没有保证GPU 顶层注释明确以此为前提。完成以上四步并看到done拉高、日志末尾的内存表出现预期结果即说明这个 kernel 已经在 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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表