跳转至

QEMU 训练营 2026 专业阶段总结

主要贡献者


背景介绍

本人是从事驱动开发工作,但平时开发只着重于驱动自身,忽略 qemu 平台以及 model 自身,对这些东西不是很了解,故希望参加此次训练营,来补充这方面知识,提高自己对 qemu 平台的能力和对 model 的理解

专业阶段

完成的是专业阶段 GPGPU 方向的实验

实验总共 10 个,其中从第 8 个实验开始才涉及 kernel 的执行,所以前 7 个实验基本都是来模拟 mmio 的实现

前面的实验开始需要理解 qemu object model,qemu 就是通过这个东西来实现面向对象。当我们注册并实现一个 pci 设备后,接下来就需要实现各自 bar 并映射 mmio 区域,目前只涉及 bar0 和 bar2 其中 bar0 主要用来暴露设备信息,控制寄存器,dma 以及 kernel 相关寄存器,大小为 1m,按部就班实现即可 bar2 主要用来 vram 访问使用,也可以直接通过 mmio 来实现

实现 1-7 需要注意的地方是有些寄存器每个 bit 可能有不同用处,清零时要只针对单个 bit,不能针对整个寄存器进行清零,还有就是需要做边界检查,包括 VRAM 的访问

从实验 8 开始就正式进入了 kernel 相关

当 mmio 写 dispatch 寄存器时候,就会触发 gpgpu core 开始执行,其相关实现都在 gpgpu_core.c 这个文件里

由于 GPU 的调度是以 warp 作为基本单位,所以我们必须遍历所有的 dim 来展开 warp,每个 warp 有 32 个 lane,每个 lane 包含 PC,gpr,fpr,hart id, activeFlag 等,warp 则包含所有的 lane,warp 的 active_mask(可以只激活部分 lane),thread_id_base,warp_id,block id 等

在 gpgpu_core_init_warp 这个 func 中需要实现为每个活跃 lane 设置 pc(指向入口),编码 hart id,寄存器清零,浮点状态初始化

gpgpu_core_exec_warp 则是真正的 warp 执行 func,需要实现取值,解码,执行,写回等 代码实现如下

while(cycle < max_cycles && warp->active_mask != 0)
{
    uint32_t pc = lane0->pc;//简化设计

    uint32_t fetch_addr = pc;

    uint32_t ins = *(uint32_t *)(s->vram_ptr + fetch_addr);

    //decode 
    uint32_t opcode = ins & 0x7f;
    //...........

    switch(opcode)
    {
        //......这里需要注意每条指令执行的时候由于我们也是基于 warp 的,所以也需要 loop 所有有效的 lane
    }

    lane0->pc += 4;
    cycle++;
}
//另外在指令模拟时候最好每添加一条指令就加上指令的 log,不然后面 debug 十分麻烦
这里我们为了简单方便期间直接 switch case,这种方式指令一多效率很差,可阅读性也很低 实际如果要实现 simt 栈,最好的方式还是每个指令类型一个类,warp 执行按如下 code
    warp->fetch();
    warp->decode();
    warp->readOperand();
    warp->execute();
    warp->writeBack();

同时这里也学习了 risc v 指令,risc v 指令有如下 6 种指令格式

opcode :指令操作码
imm:代码立即数
func3和funct7:代表指令对应的功能
rs1:源寄存器1
rs2:源寄存器2
rd:目标寄存器(RSIC-V 一个指令可以提供三个寄存器操作)

具体指令模拟的方法可以参考 vortex,或者 ai 给出

实验 9 这里涉及浮点指令模拟,需要注意的是我们 fpr 也是用 uint32_t 模拟,所以在模拟 FCVT.S.W 这种指令时候,需要使用 memcpy,不能直接赋值转换,FCVT.W.S 还需要注意舍入模式

实验 10 涉及一些特殊浮点数格式,主要有 BF16 E4M3 E5M2 E2M1

FP16:sign 1bit, exponent 5bit , fraction 10bit BF16:sign 1bit, exponent 8bit , fraction 7bit E4M3:sign 1bit, exponent 4bit , fraction 3bit E5M2:sign 1bit, exponent 5bit , fraction 2bit E2M1:sign 1bit, exponent 2bit , fraction 1bit

添加对应格式的转换函数即可,这个实验难点在于指令比较多,即使有 log,也会非常多,想偷懒可以交给 ai 审查代码

总结

通过本次实验学到了很多东西,比如 pci 设备模拟以及 riscv 指令解析,也学到了很多浮点数相关知识,对理解指令会很有帮助

由于时间原因,为了通过测试,简化了很多设计,只相当于了实现指令的解析,并没有真正完成 simt 栈的模拟,但这个相当于打开学习模拟器的入门,后续准备来学习真正的 gpu 模拟器来补充这方面知识。

建议后续学员学习时候 log 机制一定要一开始设计好,指令多起来后如果 log 比较混乱,观感会非常差,还有就是可以同时对比其他开源 GPU 模拟器来学习,这样更有助于理解