tinygrad QCOM/KGSL:代际门与外部相机指针映射

作者:

在

结论:QCOM/KGSL 后端在入口处设了一道明确的代际门——gpu_id[:2] >= (7, 3) 直接抛出不支持异常,而 QCOMCLRenderer 的编译器构造器只接受 a630,二者说明「运行时接受的 GPU ID」不等于「该编译器可用」。外部相机指针映射有一条容易被误读的回退:_gpu_map() 仅当 errno=14 时才把原指针当 GPU 地址,这不能被当作任何普通 CPU 指针的通用零拷贝保证。此外 signal() 的 7.3+ 分支尚未实现,与设备初始化的代际门一致。以下为固定源码快照 c8fb906 及固定 tinygrad 归档的静态追踪,不是 Qualcomm GPU、AGNOS 或车辆上的运行验证。

入口与编译选择

AGNOS 上 load_oob() 选择 DEV=QCOM;构建模型的 SConscript 还设置 IMAGE=1、FLOAT16=1 等标志,因此运行产物的设备名、图像布局和编译器必须匹配,改选新 SoC 不能只替换设备字符串。QCOMDevice.__init__() 打开 /dev/kgsl-3d0,创建带优先级/细粒度抢占等标志的 KGSL 上下文,设置功率约束并查询 chip_id;gpu_id[:2] >= (7, 3) 直接抛出不支持异常。

随后选 QCOMCLRenderer 或 IR3Renderer,安装栈、cache-flush dummy 与 border-color 的缓冲替换规则。QCOMCLRenderer 使用 QCOMCompiler,其构造器只接受 a630;非 aarch64 主机尝试下载编译环境并经 QEMU/Docker 服务编译,不能推断任意被运行时接受的 GPU ID 都能走该编译器。IR3 是另一条编译路径,其可用性同样需要目标平台验证。

内存与外部相机指针

QCOMAllocator._alloc() 在 external_ptr 非零时走 _gpu_map(),否则走 _gpu_alloc()。所有权随来源变化:自分配对象走 GPUOBJ_FREE 和 munmap,映射的外部 CPU 指针走 SHAREDMEM_FREE,回退的外部 GPU 指针不调用 KGSL 释放。因此主机析构不能替相机驱动释放底层图像,外部相机缓冲复用必须晚于 GPU 完成。

函数 源码行为 对车载硬件的契约
QCOMAllocator._alloc() external_ptr 非零走 _gpu_map(),否则走 _gpu_alloc() 外部指针必须有明确长度、来源和所有者;空指针不进入外部映射分支。
_gpu_alloc() 按 4 KiB 对齐向 KGSL 分配 GPU 对象,并 mmap 成 CPU 可见地址,可选 uncached/清零 要验证目标驱动的分配标志、CPU/GPU 一致性和峰值内存。
_gpu_map() 页对齐外部地址与跨度,先调用 CPU dcache_flush(),再尝试 KGSL_MAP_USER_MEM;仅 errno=14 时退回把原指针当 GPU 地址 errno=14 回退不是任何普通 CPU 指针的通用零拷贝保证,须确认指针为 GPU 可用地址且租约有效。
_free()、_gpu_free() 释放前同步;自分配走 GPUOBJ_FREE 和 munmap,映射的外部 CPU 指针走 SHAREDMEM_FREE,回退的外部 GPU 指针不调 KGSL 释放 所有权随来源变化,外部相机缓冲复用必须晚于 GPU 完成。
_ensure_stack_size() 设备共享私有栈,仅在需要增长时同步并重分配 内核首次触发更大栈可能引入帧延迟尖峰,须预热与量测。

QCOM has_copy_queue=False,通用 get_enqueue_devs() 还将 QCOM COPY 排除 HCQ 入队;exec_copy() 会根据缓冲能力选 host 可见复制或其他分支。不能把 Buffer.copy_from() 一律算成 QCOM DMA,也不能只测 kernel 耗时作为整帧推理时延。

二进制、参数与命令包

QCOMCompiler.compile() 在 aarch64 上通过专用 LLVM 接口编译/链接,再取设备二进制;checked() 在错误时重建编译实例并抛错,非 aarch64 走编译服务,存在额外环境与网络依赖。QCOMProgramData 依所选 renderer 解析 NIR 或专有库的程序镜像、寄存器占用、私有/共享内存、图像和参数布局;qcom_build_program() 以二进制内容和设备集合缓存程序占位。

QCOMComputeQueue.kernargs() 把普通缓冲地址、标量、常量、sampler、texture 和 image buffer 编成参数表,NIR 分支还按编译器给出的映射重排图像槽位。exec() 检查资源/维度、取得程序镜像和私有栈,随后设置 Adreno A6xx 寄存器、装载常量与 shader,分别使用 CP_EXEC_CS 或 CP_RUN_OPENCL 启动。cmd()、reg() 将其包装为 Type7/Type4 命令,动态地址由 HCQ 链接期或运行期 patch 处理。

源码中 exec() 的维度异常条件为「某全局乘局部维度超 65536 且 某局部维度超 1024」;单独超出前者不会由这个条件拒绝。它还单独检查 max_threads < prod(local_size)。这是静态可见的输入检查范围,不等于目标驱动一定接受其余组合;移植测试应分别覆盖每一维边界、组合边界及驱动错误回报。

提交、缓存与失效

QCOMComputeQueue.submit() 为命令缓冲构造 kgsl_command_object 和 kgsl_gpu_command,经 ioctl(KGSL_GPU_COMMAND) 提交。memory_barrier() 发缓存写回、失效和等待;signal()、wait() 用 GPU 事件/内存轮询配合 HCQ 时间线,而 signal() 的 7.3+ 分支尚未实现,这与设备初始化的代际门一致。

QCOMDevice._wait_signal() 在共享信号未达目标值时先尝试 KGSL timestamp 等待,抑制该等待的 OSError/RuntimeError,再调用通用轮询检查;通用 Compiled.synchronize() 还等待 peer pending,并在运行时超时后调用 on_device_hang()。这不是车辆控制的故障降级策略;模型无输出、过期输出和 GPU 挂起应由上层状态/安全链处理,不得以成功提交命令代替有效推理结果。

验证证据与待办

验证项 归档测试覆盖 待补
CPU dcache flush test/device/test_qcom.py 覆盖(仅 ARM64/Clang 条件) —
KGSL 映射 未覆盖 缓冲租约与缓存一致性
命令包/时间线/图像参数 未覆盖 帧级尾延迟、错误码与挂起超时
模型数值 未覆盖 纯读输入与输出数值

归档中的 test/device/test_qcom.py 仅有一个 ARM64/Clang 条件下的 CPU dcache flush 测试,不覆盖 KGSL 映射、图像参数、命令包、时间线或模型数值;本文没有运行 QCOM 硬件测试。硬件验收需要固定模型产物与目标驱动版本,依次验证缓冲租约/缓存一致性、纯读输入与输出数值、帧级尾延迟、错误码与挂起超时,再验证摄像头断流/重连和降级状态。任何上车步骤都应先在隔离台架执行,保持 panda 独立 safety 边界。

常见问题

QCOM 后端支持所有 Adreno GPU 吗?

不是。QCOMDevice.__init__() 中 gpu_id[:2] >= (7, 3) 直接抛出不支持异常,这是一条明确的代际门;而 QCOMCompiler 构造器只接受 a630,运行时接受的 GPU ID 不等于该编译器可用。

外部相机指针映射失败会怎样?

_gpu_map() 先做 CPU dcache_flush() 再尝试 KGSL_MAP_USER_MEM,仅 errno=14 时退回把原指针当 GPU 地址。该回退不是任何普通 CPU 指针的通用零拷贝保证,必须确认指针为 GPU 可用地址且租约仍有效。

exec() 一定拒绝超维度的启动吗?

不完全是。维度异常条件为「某全局乘局部维度超 65536 且某局部维度超 1024」,单独超出前者不会被这个条件拒绝;它还单独检查 max_threads 小于 prod(local_size)。这只是静态可见的检查范围。

7.3+ 代际的 GPU 时间线信号可用吗?

源文档说明 signal() 的 7.3+ 分支尚未实现,这与设备初始化的代际门一致;GPU 挂起与超时应由上层状态/安全链处理,不能以命令成功提交代替有效推理结果。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/TINYGRAD_QCOM_KGSL_RUNTIME_DEEP_DIVE.md(代际门、外部指针映射、命令包与提交同步)。
  • 项目 02 —— openpilot-research/ 同目录模型运行时与 HCQ 专题(作为相邻边界参考)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

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