2026年9月21日 星期一 猫川漫记

猫川漫记

Lonely journey of a cat.

未分类

华为马良 920 GPU 架构分析

concrete buildings in the city
Photo by Qingju Wen on Pexels.com

(本文内容基于本地硬件内核、用户态驱动以及华为公开的马良 GPU 优化文档分析,不保证准确性,如果侵害您公司权益我立马投降)

总体硬件结构

马良 920 是带有四颗 Shader Core 以及一颗基于 RV32 的 JMCP 任务管理协处理器(和其对应的固件控制处理器),采用 TBDR 的,移动图形处理器核心。其中 Shader Core 可以根据功耗热插拔,支持动态上下线单颗 GPU。每个 Shader Core 核心包含 64 个逻辑线程(Warp),硬件支持将 64 线程分为一组或两组调度。核心指令设计采用了 VLIW 架构。另外,GPU 驱动中有残留的光线追踪单元信息,但在该产品硬件中并未存在。

GPU 本身的 MMU 为 39 位虚拟地址,采用三级页表,每级均为 512 个页表项,页大小为 4 KB。主处理器和 GPU 具有硬件缓存一致性,并有 8 个 4G 的映射窗口,其中 6 个可以分配给用户上下文。

软件堆栈则是一个基于 LLVM 15.4 的 Shader Compiler (毕昇编译器) 提供 GLES 3.2 和 Vulkan,OpenGL 则是由 Zink 转译到 Vulkan 实现。毕昇编译器同时有两个 Target:ShaderCore 和 RV32 协处理器,其中 ShaderCore 支持数据中混排 RV 指令。

JMCP 控制核心

JMCP 为一颗只有 M 特权模式的 RV32IMAFCZicsr 处理器,带有 20K 指令紧密耦合寄存器,8K 数据紧密耦合寄存器,不具有独立 MMU 或缓存。
该处理器固件启动自检过后直接进入 idle loop,将任务交给由中断驱动的微码,并负责进行 Shader 的预处理,包含常量展开以及常量参数绑定,并将任务进行合适的切分,之后交给 Shader 核心进行运算。

启动时,对应的固件控制处理器会从驱动处请求固件,进行校验后通过 DMA 操作写入 JMCP 的相应耦合内存中,并为 JMCP 充当类似 MMU 的地址翻译组件,之后在运行时进入空闲状态,只在之后休眠时负责保存状态并在发生故障时做上报操作。

该设备微码中有 11 个中断处理程序,负责内部任务分发,数据搬运,任务切分等任务。中断处理无队列和优先级等功能,具体信息在此不再赘述。同时提供 per-task code 功能,允许主机驱动程序上传针对单个任务的代码。

Shader Core

Shader Core 为支持 SIMD32/64 的 VLIW 架构单元,在 SIMD32 模式下最多支持使用 192 个寄存器(如果同一核心要支持2组同时驻留则最多 168 个),SIMD64模式下最多 128 个。

寄存器信息

硬件寄存器堆由 512 个 32 位通用寄存器C,384 个半宽专用寄存器H,152 个浮点寄存器F,128 个整数寄存器I,32 个 64 位常量展开寄存器WGPR,以及 640 个常量寄存器FAU,此外还有 4 个Tile结果寄存器TR以及数个控制寄存器。
其中通用寄存器 C0 在调用约定中为返回地址寄存器。另外,大部分寄存器提供半宽度模式视图和向量视图。

执行单元信息

以下信息通过 microbenchmark 获取,仅供参考:
每个核心具有 2 ALU,1 SFU(2周期一条),1SAM(5周期一条),以及 1 IMAD。

指令格式和编码

单个指令为 64 bit 长度,分为两个 32 bit 指令槽位,具体格式如下:

bit 63                           32 31                            0
    +------------------------------+------------------------------+
    |          Insr Slot 1         |          Insr Slot 0         |
    | Hi 16b OPCode | Lo 16b Opand | Hi 16b OPCode | Lo 16b Opand |
    +------------------------------+------------------------------+

其中每个指令槽内部指令格式如下:

bit 31                     16 15              10 9             0
    +------------------------+------------------+--------------+
    |         OPCode         | 6b Reg/Const ID  | Ins Modifier |
    +------------------------+------------------+--------------+

每一个指令要求两个指令槽位以下列形式组合:

  • BUNDLE1 : ALU+访存指令
  • BUNDLE2 : ALU+ALU/SFU/Emit
  • BUNDLE3 : ALU+控制流
  • BUNDLE3ECAF : ALU+ECAF (同时也是指令束结束的标记)
  • BDL3 : 带转发槽的 BUNDLE3

两个指令槽位分别有各自的指令集,指令内部格式为

bit 15      12 11        8 7              0
    +---------+----------+---------------+
    |  Group  |  OpCode  |    Op Unit    |
    +---------+----------+---------------+

指令中有如下分类单元(Op Unit):

  • 0x1: IMAD – 整数及定点数乘加
  • 0x2: ALU – 整数算术/逻辑/移位/位操作
  • 0x3: CTRL – MOV/EMIT/特殊寄存器操作,以及指令束等待相关
  • 0x4: TEX/SAM – 纹理相关命令
  • 0x5: LD/ST/ATOM – 访存及原子指令
  • 0x6: TILE/IMG – Tile 及图像相关指令,同时也提供JMCP访问(组内 op 0)
  • 0x7: CFF – 控制流:跳转,分支屏障等命令
  • 0x9: WARP-ALU – 线程束相关位运算
  • 0xc: DOT – 点积
  • 0xe: GPR – 通用寄存器相关指令

每个分类单元中还有指令组,划分单元内的功能大类,此处不给出详细信息。

另外,还有一些特殊的非指令形式:

  • 其中一个指令槽全0: 单指令模式
  • 程序起始标识: 0080_0000_0000_0000
  • 程序结束标识: c000_*,之后5800_*
  • 控制核心卸载命令。

具体指令不在本文章中给出。该 ISA 指令设计吸收了 Bifrost 的 Clause 语义,但有不同的设计:采用定长指令,具有专用控制槽和 FMA 前递等特性,同时给出了编译器可见的依赖模型。

主机控制接口

该 GPU 在主机端映射为一个 /dev/hvgr0 字符设备,内核态驱动不对具体任务内容做控制,只提供状态管理接口。用户态驱动通过五条通道控制 GPU 作业,分别为 ioctl 管理控制接口,mmap 内存零拷贝数据映射,基于 128KiB 环形缓冲区的任务队列(32B * 4096),基于中断的任务完成回调以及最后的 dma-fence 数据同步机制。

总结

该处理器在设计方面吸收了之前多款核心设计的先进经验:驱动堆栈,指令设计接近 ARM Mali,渲染和队列调度较为接近 Qualcomm Adreno,而核心内部 ISA 设计则更接近于 Apple AGX。不过和这些核心不同的是,华为充分利用了该 GPU 和整片系统整合较强的优势,让 GPU 能够直接访问物理页表,同时将调度留给了内核态驱动,方便独立于硬件进行优化。

附注

标题图片为 Lima,来自 Pexels.

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注