news 2026/8/19 22:51:55

第 3 章 SDMA 指令集:Packet 格式速览

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第 3 章 SDMA 指令集:Packet 格式速览

本章目标:上一章说"硬件只认命令流",本章就来看下命令(packet)到底长什么样。不需要记忆全部字段,我们抓住header = opcode + sub-opcode + 参数这个统一骨架,然后用本仓库里几个真实的 emit 函数,看懂WRITE/INDIRECT/FENCE/TRAP/POLL_REGMEM这几个最常用的命令是怎么拼出来的。

3.1 packet 的统一骨架

SDMA 的每条命令都是若干个dword(32 位)拼起来的。第一个 dword 是header,其余是这条命令的参数。header 里最关键的两个字段:

  • OP(opcode):主操作码,决定"这是哪一类命令"(写内存?拷贝?发 fence?)。
  • SUB_OP(sub-opcode):子操作码,在同一大类里再细分(比如 WRITE 里再分"线性写 / tiled 写")。

在代码里,header 用一组宏拼装,最典型的就是SDMA_PKT_HEADER_OP(...)

// 一个 header 的通用形态SDMA_PKT_HEADER_OP(op)|SDMA_PKT_HEADER_SUB_OP(sub_op)|<其它标志位>

驱动侧用amdgpu_ring_write(ring, dword)把一个个 dword 依次写进 ring buffer,硬件就按顺序读出来解释执行。理解 packet,本质就是理解"每个 dword 放了什么"。

3.2 最小可验证例子:WRITE(写一个魔数)

理解 packet 的最佳入口,是 ring 自检函数sdma_v4_0_ring_test_ring()。它干的事极简单:用 SDMA 往一块内存写一个已知值0xDEADBEEF,然后 CPU 去读,读到了就说明引擎活着。

// sdma_v4_0.c: sdma_v4_0_ring_test_ring()(节选)tmp=0xCAFEDEAD;adev->wb.wb[index]=cpu_to_le32(tmp);// 先把目标内存填成 0xCAFEDEADamdgpu_ring_alloc(ring,5);// 这条命令占 5 个 dwordamdgpu_ring_write(ring,SDMA_PKT_HEADER_OP(SDMA_OP_WRITE)|// dw0: headerSDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));amdgpu_ring_write(ring,lower_32_bits(gpu_addr));// dw1: 目标地址低 32amdgpu_ring_write(ring,upper_32_bits(gpu_addr));// dw2: 目标地址高 32amdgpu_ring_write(ring,SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(0));// dw3: 写多少(count)amdgpu_ring_write(ring,0xDEADBEEF);// dw4: 要写入的数据amdgpu_ring_commit(ring);// 提交(推 WPTR + 敲门)

把这 5 个 dword 排开看,WRITE (LINEAR)packet 的结构一目了然:

dword内容说明
dw0headerOP=WRITE,SUB_OP=WRITE_LINEAR
dw1dst addr lo目标 GPU 地址低 32 位
dw2dst addr hi目标 GPU 地址高 32 位
dw3count写入的 dword 数(0 表示 1 个)
dw4data要写的值0xDEADBEEF

执行后 CPU 轮询那块内存,从0xCAFEDEAD变成0xDEADBEEF就算通过。这就是一条完整 SDMA 命令的最小闭环,把它记牢,后面再复杂的 packet 也是同一套路。

3.3INDIRECT:执行一个 IB(间接缓冲区)

ring buffer 本身不大,不适合放很长的命令流。真正干活的大命令流通常放在单独的IB(Indirect Buffer)里,然后在 ring 里放一条INDIRECTpacket,告诉硬件"去那个地址执行一段命令"。

// sdma_v4_0.c: sdma_v4_0_ring_emit_ib()(节选)amdgpu_ring_write(ring,SDMA_PKT_HEADER_OP(SDMA_OP_INDIRECT)|// dw0: header + vmidSDMA_PKT_INDIRECT_HEADER_VMID(vmid&0xf));amdgpu_ring_write(ring,lower_32_bits(ib->gpu_addr)&0xffffffe0);// dw1: IB 地址低(32B 对齐)amdgpu_ring_write(ring,upper_32_bits(ib->gpu_addr));// dw2: IB 地址高amdgpu_ring_write(ring,ib->length_dw);// dw3: IB 长度(dword)amdgpu_ring_write(ring,0);// dw4/5: csa 等amdgpu_ring_write(ring,0);

要点:

  • header 里带了VMID——说明这段 IB 在哪个虚拟地址空间里执行,这是 GPU 虚拟内存隔离的一部分。
  • IB 地址要求32 字节对齐& 0xffffffe0)。
  • 这就是"ring → IB"两级命令结构的硬件基础,第 6 章会从软件角度再讲一遍。

3.4FENCE+TRAP:宣告"我干完了"

一批命令执行完,需要两件事:① 写一个递增的序号(seq)到某个地址(fence),让别人能查询进度;② 触发一个中断(trap),主动通知驱动。SDMA 把这两步分别用FENCETRAP两条 packet 完成:

// sdma_v4_0.c: sdma_v4_0_ring_emit_fence()(节选)/* ① 写 fence:把 seq 写到 addr */amdgpu_ring_write(ring,SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));amdgpu_ring_write(ring,lower_32_bits(addr));// fence 地址低amdgpu_ring_write(ring,upper_32_bits(addr));// fence 地址高amdgpu_ring_write(ring,lower_32_bits(seq));// 序号/* (64 位 fence 时再写一遍高 32 位,略)*//* ② 触发中断:TRAP */amdgpu_ring_write(ring,SDMA_PKT_HEADER_OP(SDMA_OP_TRAP));amdgpu_ring_write(ring,SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(0));

回顾第 2 章的硬件模型:TRAP 触发的正是trap_irq,驱动在中断里推进 fence、唤醒等待者。FENCE(可查询)+TRAP(主动通知)就是 GPU 任务完成通知的标准组合拳。

3.5POLL_REGMEM:等待某个条件成立

有时命令流需要"等到某寄存器/内存等于某值再继续",比如 HDP cache flush。这用POLL_REGMEM(轮询寄存器或内存)实现:

// sdma_v4_0.c: sdma_v4_0_wait_reg_mem()(节选)amdgpu_ring_write(ring,SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM)|SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(hdp)|SDMA_PKT_POLL_REGMEM_HEADER_MEM_POLL(mem_space)|SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3));/* 3 == "等于" */// ... 地址、reference(期望值)、mask、retry/interval ...

它内建"比较函数"(这里FUNC(3)表示==)、期望值、掩码和重试间隔,硬件会自旋等待直到条件满足。emit_hdp_flushemit_vm_flush(TLB 失效等待)都用它做同步。

3.6 与页表相关的 opcode:COPYPTEPDE(预告)

除了上面这些通用命令,SDMA 还有专门服务页表更新的 opcode,本章先点名,第 12 章展开:

  • COPY:批量拷贝,vm_copy_pte用它把一批 PTE 从暂存区拷进页表。
  • WRITE:逐条写 PTE(就是 3.2 那个 WRITE,vm_write_pte会用到,支持递增地址)。
  • PTEPDE:页表专用,vm_set_pte_pde用它对连续页表项做批量、带掩码的更新,一条命令顶很多条 WRITE。

这三条正是 SDMA 作为"页表更新执行者"的指令基础。

3.7 常用 opcode 速查(本章出现的)

opcode作用出现在
SDMA_OP_WRITE往内存写数据test_ringvm_write_pte
SDMA_OP_COPY内存拷贝emit_copy_buffervm_copy_pte
SDMA_OP_INDIRECT执行一个 IBemit_ib
SDMA_OP_FENCE写 fence 序号emit_fence
SDMA_OP_TRAP触发中断emit_fence
SDMA_OP_POLL_REGMEM轮询等待条件hdp_flushvm_flush
SDMA_OP_PTEPDE批量更新连续页表项vm_set_pte_pde

完整版见 附录 B 常用 SDMA opcode 速查表。

3.8 本章小结

  • SDMA 每条命令 =header(OP + SUB_OP + 标志)+ 若干参数 dword,用amdgpu_ring_write逐 dword 写入 ring。
  • WRITE(3.2 的写魔数)是理解 packet 的最小闭环,务必吃透。
  • INDIRECT把 ring 引到更大的IB去执行,并携带 VMID。
  • FENCE + TRAP是任务完成通知的标准组合:前者可查询、后者发中断。
  • POLL_REGMEM用于命令流内的等待/同步。
  • COPY/WRITE/PTEPDE是页表更新的指令基础,为第 12 章埋下伏笔。

下一章预告:硬件三章到此结束。从第 4 章起进入软件层,我们先总览 SDMA 在驱动里的数据结构——amdgpu_sdma_instanceamdgpu_sdma——看这些 packet、队列、指针在 C 结构体里是如何被组织起来的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 22:48:01

ESP32墨水屏PC性能监控器:低功耗硬件方案与全栈实践

1. 项目概述&#xff1a;为什么需要一个墨水屏的PC性能监视器&#xff1f;最近在折腾我的主力台式机&#xff0c;机箱侧透&#xff0c;RGB灯效拉满&#xff0c;但总觉得少了点什么。每次想看看CPU温度、内存占用&#xff0c;要么得切到任务管理器&#xff0c;要么得依赖第三方悬…

作者头像 李华
网站建设 2026/8/19 22:47:13

拆解英飞凌最小ToF模组:技术原理、实现与手机面部解锁应用

1. 从MWC 2019看ToF技术的“小”时代 2019年的世界移动通信大会&#xff08;MWC&#xff09;上&#xff0c;英飞凌&#xff08;Infineon&#xff09;发布了一款号称“全球最小”的飞行时间&#xff08;ToF&#xff09;传感器模组。当时&#xff0c;这个新闻在手机供应链和光学传…

作者头像 李华
网站建设 2026/8/19 22:45:00

STM32H750 DMA驱动SPI LCD与AHT21传感器C语言嵌入式开发实践

1. 项目概述&#xff1a;从传感器到屏幕的C语言直连最近在捣鼓一个环境监测的小玩意儿&#xff0c;核心需求很简单&#xff1a;用一块小巧的AHT21温湿度传感器采集数据&#xff0c;然后实时显示在一块RT-Spark开发板自带的LCD屏幕上&#xff0c;所有逻辑都用最纯粹的C语言来实现…

作者头像 李华
网站建设 2026/8/19 22:44:24

【关注可白嫖源码】--课程设计--毕业设计--基于Django框架的房屋租赁系统的设计与实现[编号:project28636](案件分析)

本文仅展示核心实现逻辑与部分代码片段&#xff0c;完整项目源码、配套文档、数据库脚本内容较多&#xff0c;篇幅有限无法全部放出。 有需要完整资源的同学&#xff0c;可以在评论区留言【资料或领源码】&#xff0c;我会一 一回复站内私信&#xff0c;发送完整文件第1章 绪论…

作者头像 李华
网站建设 2026/8/19 22:44:10

Arduino MKR WAN 1310物联网开发板:LoRa远距离通信与低功耗设计实战

1. 项目概述&#xff1a;Arduino MKR WAN 1310&#xff0c;为远距离物联网而生如果你正在寻找一款能让你轻松构建覆盖数公里、甚至十几公里物联网节点的开发板&#xff0c;那么Arduino最新发布的MKR WAN 1310绝对值得你投入全部注意力。这不是一款简单的迭代产品&#xff0c;而…

作者头像 李华