当前位置: 首页 > news >正文

第 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 结构体里是如何被组织起来的。

http://www.cnnetsun.cn/news/4106681.html

相关文章:

  • 机器人开发中如何平衡稳定性与敏捷性:从硬件选型到控制算法的工程实践
  • ESP32墨水屏PC性能监控器:低功耗硬件方案与全栈实践
  • 拆解英飞凌最小ToF模组:技术原理、实现与手机面部解锁应用
  • STM32H750 DMA驱动SPI LCD与AHT21传感器C语言嵌入式开发实践
  • 【关注可白嫖源码】--课程设计--毕业设计--基于Django框架的房屋租赁系统的设计与实现[编号:project28636](案件分析)
  • Arduino MKR WAN 1310物联网开发板:LoRa远距离通信与低功耗设计实战
  • 数据库与中间件
  • 借助 AI-DLC 完成研发团队转型,传统企业该挑选哪些云上工具和方案?
  • OpsFlash v0.2.0 版本发布:新增多项功能,跨平台桌面运维工具再升级!
  • 突破性DSP语音模组AP-0316引领声学革命
  • 基于合成数据与ESP32-S3的跨语言关键词唤醒模型实战指南
  • AIoT边缘计算硬件选型与推理部署实战指南
  • leetcode 1722. Minimize Hamming Distance After Swap Operations
  • Spring Boot AOP记录用户操作日志
  • 嵌入式开发入门:从LED与传感器控制到物联网系统构建
  • 基于EasyUI与KnockoutJS的通用分页查询与数据导出ViewModel设计
  • 广州微闻网络AI落地技术实践:Agent定制、Token供应与云计算全栈技术解析
  • 在线教育平台开课前三网验收:视频域、直播与 API
  • 多个人同时提问但位置有限
  • UnrealPakViewer 完整上手教程:三步摸清任意 UE4 Pak 文件内部结构
  • Think-a-Tron Mini:从复古玩具到DIY电子项目,探索伪随机数生成与电路设计
  • 102.环形缓冲区之读指针与写指针:原理、实现与完整代码
  • BBDown完整使用手册:让哔哩哔哩视频下载变成一行命令的事
  • 计算机网络学习笔记(六)---网络层与IP协议
  • AI家庭机器人技术解析:从ROS架构到嵌入式开发实践
  • 从零构建智能体:基于Coze平台的可视化AI助手开发实战
  • C语言基础知识-学习笔记
  • 基于大模型与持续学习的人形机器人叠衣系统实战解析
  • 光度立体成像全栈拆解 | 多视角光影法向量求解+梯度积分重建,助力漫反射工件微划痕凹坑褶皱高速高精度2.5D缺陷检测
  • 深挖C语言:深入理解指针(2)