tinygrad AMD AM 驱动:七个 IP 启动顺序与部分重启生命周期

作者:

在

结论:AMDev 的启动不是一条固定路径,而是先读 IP discovery 再分流:scratch7 等于 AMDev.Version 且未请求 AM_RESET 时才初步允许部分启动,scratch6 非零或 GC 保护故障寄存器非零通常退回完整启动,GC 9.5.0 的既有 AM 状态反而再次强制部分启动以避免完整启动损伤互连。完整启动按 SOC→GMC→IH→PSP→SMU 初始化,两条路径最终都重建 GFX 与 SDMA,VF 会跳过 PF 专属的 PSP 与 SMU。启动标记只表示 AM 自身的阶段完成,不代表模型已编译、GPU 队列已成功执行或已获车辆控制权。本文为固定归档 amdev.py 与 ip.py 的静态阅读,未打开 PCI/USB、未刷写固件、未碰 GPU/车辆/CAN。

启动顺序与实际分支

AMDev.__init__() 先处理 PCIe ASPM,映射 VRAM、doorbell 与 MMIO,判断 VF 并请求访问窗口;随后 _run_discovery() 从 VRAM 末端读取 IP discovery、harvested 实例与版本/基址,再由 _build_regs() 生成寄存器对象及可访问的 AID 集合。_run_discovery() 仅检查两类 signature,仍直接按表内偏移与数量做 ctypes 地址遍历,固件或设备损坏场景下需要台架注入验证长度边界,不能把它视为不可信数据解析沙箱。

init_sw() 建内存管理器、固件目录和七个 IP 实例,按 SOC、GMC、IH、PSP、SMU、GFX、SDMA 顺序对每个组件调用 init_sw()。scratch7 匹配且未请求 AM_RESET 时,才初步允许部分启动;若 scratch6 非零或 GC 保护故障寄存器非零,通常退回完整启动。完整启动如检测到存活的 PSP/SMU,可能先做 mode1 reset;hive 多卡状态要求另行复位。之后完整启动按 SOC→GMC→IH→PSP→SMU 初始化,VF 跳过 PF 专属的 PSP/SMU;部分启动沿用底层状态,非 VF 会重新初始化 TMR;两路最终都初始化 GFX→SDMA。

判定条件 走向 说明
scratch7 == AMDev.Version 且未请求 AM_RESET 初步允许部分启动 沿用底层状态,非 VF 重新初始化 TMR
scratch6 非零或 GC 保护故障寄存器非零 退回完整启动 可能先做 mode1 reset
GC 9.5.0 的既有 AM 状态 强制部分启动 避免完整启动损伤互连
VF 模式 跳过 PF 专属 PSP/SMU 经访问窗口获得并释放租约
两路共同收尾 GFX→SDMA 配置功耗/时钟与 clock gating,写启动标记

启动尾部对非 VF 配置功耗/时钟与 clock gating,再写 scratch5(TMR 大小)、scratch7(AM 版本)和 scratch6=1。reset_mode=True 的 hive 异常分支可提早返回,因此调用者不能仅以构造函数返回推断所有初始化阶段完成。

七个 IP 的入口与职责

子组件 主要入口 本层职责与风险边界
AM_SOC init_sw()、init_hw()、doorbell_enable() 选 SoC/中断源元数据、设置 NBIO/doorbell;门铃可达不等于队列完成
AM_GMC init_hub()、enable_vm_addressing()、flush_tlb() 计算 FB/MC/XGMI/VA 范围,初始化页表与 TLB;错误映射可能影响所有 GPU 内存访问
AM_IH init_hw()、interrupt_handler()、drain() 启动 IH ring,解析 SQ/VM fault/RAS,设置 is_err_state;日志打印不是自动恢复
AM_PSP init_hw()、_ring_submit()、_load_ip_fw_cmd() 启动 sOS、创建命令 ring、设置 TMR 并加载各 IP 固件;固件来源、哈希与响应状态是独立验收门
AM_SMU _send_msg()、mode1_reset()、set_clocks() 电源/时钟/重置消息,按 IP 版本选协议;超时与电源策略不能由模型插件自行决定
AM_GFX init_hw()、reset_mec()、setup_ring()、fini_hw() 初始化 GC hub/MEC,配置 MQD/HQD 与计算 ring;部分启动只重置 MEC
AM_SDMA init_hw()、setup_ring()、fini_hw() 按 SDMA 版本配置 watchdog、doorbell、copy ring 与关闭路径;传输完成与计算完成不是同一事件

内存侧还有一条必须保持的先后关系:AMMemoryManager.on_range_mapped() 在页表变更后依次请求 GC 和 MM TLB flush,AM_GMC.flush_tlb() 会先做 HDP flush,未初始化 hub 则返回,VF 与常规路径的 invalidation/ACK 不同。插件若要抽象 GPU 内存,必须保持映射、TLB、队列提交的先后关系,不能只替换分配器。

停机、恢复与未验证项

AMDev.fini() 先在 VF 需要时请求访问窗口,只对 SDMA/GFX 调用 fini_hw(),再调低时钟、处理 IH、写 scratch6 并释放 VF 租约;它不是逐一销毁七个 IP 或解除所有 BAR 与固件状态。recover() 只在错误态或 force=True 时处理 IH 并重置 MEC,不能等同于 PCI 热复位、全卡重新发现或固件重验。本专题没有运行 AMD/AM 单元测试或真机驱动,初始化寄存器表、PSP 固件加载、SMU 响应、IH 故障与内存一致性需要单独台架验证;AMDev.__init__() 的多阶段初始化没有包裹通用清理块,若中间异常,已映射资源、VF 租约和部分配置如何回退应通过故障注入确认。此外,VF 与 PF 模式、hive 多卡状态以及 GC 9.5.0 的强制部分启动分支,都不能用一块普通单卡的测试结果替代——它们各自有独立的复位路径与访问租约,验收时必须分别覆盖。

自制车载硬件的插件边界

应把 AMDev/IP 层限定为受信任的计算设备驱动,而不是任意动态插件。硬件适配需声明 PCI/USB 身份、BAR 布局、IP 发现表版本、固件哈希、PF/VF 模式、AID/XCC/harvest 信息、内存与 TLB 语义、队列和中断超时。验证顺序为只读发现与固件校验、独立台架冷/暖启动和异常回滚、GPU 队列与内存数值回归、车端只读观察;每一步单独上报设备在线、固件准备、队列可提交、任务完成与模型输出有效,任何状态都不得绕过车型 CAN 编码或 panda 安全钩子。

常见问题

AM 启动后 scratch6=1 代表什么?

它只是 AM 自身的启动标记之一,与 scratch5(TMR 大小)、scratch7(AM 版本)一起写入。它不表示任何模型已编译、GPU 队列已成功执行或系统已获车辆控制权限,因此不能把标记写入当作端到端就绪。

什么情况下会走部分启动,什么情况下退回完整启动?

scratch7 等于 AMDev.Version 且未请求 AM_RESET 时才初步允许部分启动;scratch6 非零或 GC 保护故障寄存器非零通常退回完整启动。GC 9.5.0 的既有 AM 状态是例外,会再次强制部分启动以避免完整启动损伤互连。

recover() 等于把 GPU 重新初始化一遍吗?

不等于。recover() 只在错误态或 force=True 时处理 IH 并重置 MEC,它不能等同于 PCI 热复位、全卡重新发现或固件重验;AMDev.fini() 也只对 SDMA/GFX 调用 fini_hw(),并非逐组件销毁。

为什么说队列可达不等于任务完成?

因为各层完成点是分开的:AM_SOC 的 doorbell 可达、AM_SDMA 的传输完成与 GPU 计算完成都不是同一事件,IH 打印故障日志也不等于自动恢复。上报时应把设备在线、固件准备、队列可提交、任务完成与模型输出有效分开记录。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/TINYGRAD_AM_DEVICE_IP_BOOT_PARTIAL_RESET_LIFECYCLE_DEEP_DIVE.md(取用了启动顺序、完整与部分启动分支、七个 IP 入口、停机恢复与硬件插件边界)。
  • 项目 02 —— openpilot-research/TINYGRAD_AM_GMC_VM_PTE_TLB_COHERENCY_DEEP_DIVE.md(仅用于确认 TLB flush 与页表变更的先后关系)。

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

更新记录

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

评论

发表回复

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