本章描述着色器程序可见的内核状态。
3.1. 状态概览
下表显示了着色器程序可读或可写的所有硬件状态【注,硬件状态寄存器】。
表2. 可读可写硬件状态
| 缩写 | 名称 | 大小 | 描述 |
|---|---|---|---|
| PC | 程序计数器 | 48位 | 指向下一条要执行的着色器指令的内存地址。 |
| V0-V255 | 向量通用寄存器 | 32位 | 向量通用寄存器。 |
| S0-S103 | 标量通用寄存器 | 32位 | 标量通用寄存器。 |
| LDS | 本地数据共享 | 64kB | 本地数据共享是一块具有内置算术能力的暂存RAM,允许工作组内的线程之间共享数据。 |
| EXEC | 执行掩码 | 64位 | 一个按线程分布的位掩码(每位对应一个线程),应用于向量指令,控制哪些线程执行指令、哪些线程忽略指令。 |
| EXECZ | EXEC 为零 | 1位 | 一个单比特标志,指示 EXEC 掩码是否全为零。 |
| VCC | 向量条件码 | 64位 | 一个按线程分布的位掩码;保存向量比较操作的结果。 |
| VCCZ | VCC 为零 | 1位 | 一个单比特标志,指示 VCC 掩码是否全为零。 |
| SCC | 标量条件码 | 1位 | 标量ALU比较指令的结果。 |
| FLAT_SCRATCH | Flat 暂存地址 | 64位 | 暂存内存的基地址。 |
| XNACK_MASK | 地址转换失败 | 64位 | 地址转换失败的线程的位掩码。 |
| STATUS | 状态 | 32位 | 只读的着色器状态位。 |
| MODE | 模式 | 32位 | 可写的着色器模式位。 |
| M0 | 内存寄存器 | 32位 | 一个具有多种用途的临时寄存器,包括GPR索引和边界检查。 |
| TRAPSTS | 陷阱状态 | 32位 | 保存有关异常和待处理陷阱的信息。 |
| TBA | 陷阱基地址 | 64位 | 保存指向当前陷阱处理程序的指针。 |
| TMA | 陷阱内存地址 | 64位 | 用于着色器操作的临时寄存器。例如,可以保存陷阱处理程序使用的内存指针。 |
| TTMP0-TTMP15 | 陷阱临时SGPR | 32位 | 仅对陷阱处理程序可用的16个SGPR,用于临时存储。 |
| VMCNT | 向量内存指令计数 | 6位 | 统计已发出但尚未完成的向量内存指令的数量。 |
| EXPCNT | 导出计数 | 3位 | 统计已发出但尚未完成的导出和GDS指令数量。也统计尚未将其写入数据发送到纹理缓存器的向量内存写指令。 |
| LGKMCNT | LDS, GDS, 常量和消息计数 | 4位 | 统计已发出但尚未完成的LDS、GDS、常量获取(标量内存读)和消息指令的数量。 |
3.2. 程序计数器
程序计数器是一个字节地址,指向下一条要执行的指令。当创建 wavefront 时,PC被初始化为程序中的第一条指令。
PC 与三条指令交互:S_GET_PC、S_SET_PC、S_SWAP_PC。这些指令在 PC 和偶地址对齐的 SGPR 对之间传输数据。
分支跳转到 (分支指令的下一条指令的PC + 偏移量)。着色器程序不能直接读取或写入 PC。分支、GET_PC 和 SWAP_PC 指令的地址是相对于下一条指令的 PC,而不是当前指令的 PC。S_TRAP 指令保存的是 S_TRAP 指令自身的 PC。
3.3. 执行掩码
执行掩码(64位)决定向量中的哪些线程会被执行:1 = 执行,0 = 不执行。
可以通过标量指令读取和写入 EXEC 寄存器;它也可以作为向量 ALU 比较的结果被写入。此掩码影响向量 ALU、向量内存、LDS 和导出指令。它不影响标量执行或分支。
辅助位 EXECZ 可用作分支条件,以便在 EXEC 为零时跳过代码。
注意:当 EXEC = 0 时,此 GPU 不会进行优化。着色器硬件仍会执行每条指令,浪费指令发射带宽。当 EXEC 掩码可能为零时,应使用 CBRANCH 或 VSKIP 指令来快速跳过代码。
3.4. 状态寄存器
状态寄存器字段可以被着色器读取,但不能写入。这些位在 wavefront 创建时初始化。下表列出并简要描述了状态寄存器字段。
表 3. 状态寄存器字段
| 字段 | 位位置 | 描述 |
|---|---|---|
| SCC | 1 | 标量条件码。用作进位输出位。对于比较指令,此位表示失败或成功。对于逻辑操作,如果结果非零,则此位为 1。 |
| SPI_PRIO | 2:1 | 波前创建时由着色器处理器插值器设置的 wavefront 优先级 。详情见 S_SETPRIO 指令(第 12-49 页)。0 为最低,3 为最高优先级。 |
| WAVE_PRIO | 4:3 | 由着色器程序设置的wavefront 优先级 。详情见 S_SETPRIO 指令(第 12-49 页)。 |
| PRIV | 5 | 特权模式 。仅在陷阱处理程序中可能为活动状态。提供对 TTMP、TMA 和 TBA 寄存器的写入权限。 |
| TRAP_EN | 6 | 指示存在陷阱处理程序。当设置为零时,不处理陷阱。 |
| TTRACE_EN | 7 | 指示是否为该波前启用了线程追踪。如果为零,则忽略任何着色器生成的(指令)线程追踪数据。 |
| EXPORT_RDY | 8 | 此状态位指示是否已分配导出缓冲区空间。着色器会暂停任何导出指令,直到此位变为 1。当导出缓冲区空间分配后,此位被置为 1。像素或顶点着色器在导出之前,硬件会检查此位的状态。如果此位为 1,则可以发出导出指令。如果此位为零,则 wavefront 进入休眠状态,直到导出缓冲区中有可用空间。然后,此位被置为 1,wavefront 恢复执行。 |
| EXECZ | 9 | 执行掩码为零。 |
| VCCZ | 10 | 向量条件码为零。 |
| IN_TG | 11 | 波前是属于一个包含多个 wavefront 的工作组的成员。 |
| IN_BARRIER | 12 | 波前正在屏障处等待。 |
| HALT | 13 | 波前已停止或计划停止 。HALT 可以由主机通过波前控制消息设置,也可以由着色器自身设置。在陷阱处理程序中(PRIV = 1)忽略此位;如果收到主机发起的陷阱(请求进入陷阱处理程序)也忽略此位。 |
| TRAP | 14 | 波前被标记为尽快进入陷阱处理程序。 |
| TTRACE_CU_EN | 15 | 为此计算单元启用/禁用线程追踪。此位允许多个 CU 输出 USERDATA(着色器发起的对线程追踪缓冲区的写入)。请注意,每个着色器阵列每次仅从一个 CU 追踪波前数据。如果此位为零,仍可输出波前用户数据(基于指令)。 |
| VALID | 16 | 波前处于活动状态(已创建且尚未结束)。 |
| ECC_ERR | 17 | 发生了 ECC 错误。 |
| SKIP_EXPORT | 18 | 仅用于顶点着色器 。1 = 此着色器未分配导出缓冲区空间;所有导出指令被忽略(视为 NOP)。以前称为 VS_NO_ALLOC。用于多流输出(对同一 VS 进行多次遍历),以及在 VS 阶段运行的、未产生图元的波前的域着色器。 |
| PERF_EN | 19 | 为此 wavefront 启用了性能计数器。 |
| COND_DBG_USER | 20 | 用户模式的条件调试指示器。 |
| COND_DBG_SYS | 21 | 系统模式的条件调试指示器。 |
| ALLOW_REPLAY | 22 | 指示 ATC 重放已启用。 |
| MUST_EXPORT | 27 | 此波前在终止前必须执行一次带有 Done=1 的导出操作。 |
3.5. 模式寄存器
模式寄存器字段可以通过标量指令被着色器读取和写入。下表列出并简要描述了模式寄存器字段。
表 4. 模式寄存器字段
| 字段 | 位位置 | 描述 |
|---|---|---|
| FP_ROUND | 3:0 | [1:0]:单精度舍入模式。 [3:2]:双精度/半精度舍入模式。舍入模式:0=向最近的偶数舍入,1=向正无穷舍入,2=向负无穷舍入,3=向零舍入。 |
| FP_DENORM | 7:4 | [1:0]:单精度非规格化数模式。 [3:2]:双精度/半精度非规格化数模式 。非规格化数模式: 0 = 将输入和输出的非规格化数刷新为零。 1 = 允许输入非规格化数,将输出的非规格化数刷新为零。 2 = 将输入的非规格化数刷新为零,允许输出非规格化数。 3 = 允许输入和输出非规格化数。 |
| DX10_CLAMP | 8 | 由向量 ALU 使用,强制采用 DX10 风格的 NaN 处理:置位时,将 NaN 钳制为零;否则,直接传递 NaN。 |
| IEEE | 9 | 浮点操作码支持按照 IEEE 754-2008 标准收集异常标志,并使信令 NaN 输入静默和传播 。由于信令 NaN 的传播和静默,min_dx10 和 max_dx10 操作将符合 IEEE 754-2008 标准。 |
| LOD_CLAMPED | 10 | 粘滞位,指示一次或多次纹理访问的细节级别被钳制。 |
| DEBUG | 11 | 强制波前在执行每条指令后(但不在 ENDPGM 后)跳转到异常处理程序 。仅在 TRAP_EN = 1 时有效。 |
| EXCP_EN | 18:12 | 异常启用掩码 。启用意味着如果发生异常且 TRAP_EN == 1,则会触发陷阱。 [12] : 无效操作 [13] : 输入非规格化数 [14] : 浮点除零 [15] : 上溢 [16] : 下溢 [17] : 不精确结果 [18] : 整数除零 [19] : 地址监视 [20] : 内存违规 |
| FP16_OVFL | 23 | 如果置位,溢出的 FP16 结果将被钳制到 +/- MAX_FP16,无论舍入模式如何,同时仍保留真正的 INF 值。 |
| POPS_PACKER0 | 24 | 1 = 此波前与打包器 0 关联 。用户着色器必须将其设置为来自 POPS 初始化 SGPR 的 !PackerID,如果不使用 POPS 则设置为零。 |
| POPS_PACKER1 | 25 | 1 = 此波前与打包器 1 关联 。用户着色器必须将其设置为来自 POPS 初始化 SGPR 的 PackerID,如果不使用 POPS 则设置为零。 |
| DISABLE_PERF | 26 | 1 = 禁用此波前的性能计数。 |
| GPR_IDX_EN | 27 | GPR 索引启用。 |
| VSKIP | 28 | 0 = 正常操作。1 = 跳过(不执行)任何向量指令:valu、vmem、export、lds、gds。"跳过"指令以高速进行(每时钟周期最多可跳过 10 个波前的单条指令)。这比发射并丢弃指令要快得多。 |
| CSP | 31:29 | 条件分支堆栈指针。 |
3.6. 通用寄存器和本地数据共享
本节描述 GPR 和 LDS 空间如何分配给一个波前,以及如何处理越界和未对齐的访问。
3.6.1. 越界行为
本节定义了当源或目标 GPR 或内存地址超出波前的合法范围时的行为。
越界可能由 GPR 索引或错误的编程引起。不允许从一个寄存器类型索引到另一个寄存器类型(例如:从 SGPR 索引到陷阱寄存器或内联常量)。也不允许在内联常量内部进行索引。
以下描述了各种存储类型的越界行为。
-
SGPR
-
源或目标越界 =
(sgpr < 0 || (sgpr >= sgpr_size))。 -
源越界:返回
SGPR0的值(不是数值0)。 -
目标越界:指令不写入任何 SGPR 结果。
-
-
VGPR
-
与 SGPR 类似。不允许从 SGPR 索引到 VGPR,反之亦然。
-
越界 =
(vgpr < 0 || (vgpr >= vgpr_size))。 -
如果源 VGPR 越界,则使用
VGPR0。 -
如果目标 VGPR 越界,则忽略该指令(视为空操作)。
-
-
LDS
-
如果 LDS 地址越界:
(addr < 0 或 > (MIN(lds_size, m0)):-
越界的写入被丢弃;如果 SIZE 不是写入数据大小的倍数,则行为未定义。
-
读取返回值 0。
-
-
如果任何源 VGPR 越界,则使用
VGPR0的值。 -
如果目标 VGPR 越界,则使指令无效(以
exec=0发射)。
-
-
内存、LDS 和 GDS:读取和带返回值的原子操作
-
如果任何源 VGPR 或 SGPR 越界,数据值是未定义的。
-
如果任何目标 VGPR 越界,操作通过以
EXEC掩码被清零(0)的方式发射指令而被取消。-
此越界检查必须检查所有可能返回的 VGPR(例如:
BUFFER_LOAD_DWORDx4指令中的VDST到VDST+3)。 -
此检查还必须包含额外的部分驻留纹理 VGPR,如果此 VGPR 越界,则取消获取,无论纹理系统是否实际返回此值。
-
目标 VGPR 越界的原子操作被取消:指令会发射,但
EXEC掩码为 0。
-
-
对于有多个目标(例如:V_ADDC)的指令:如果任何目标越界,则不写入任何结果。
3.6.2. SGPR 分配与存储
一个波前可以分配 16 到 102 个 SGPR,分配单位是 16 个 GPR(双字)。这些寄存器在逻辑上被视为 SGPR0 到 SGPR101。VCC 物理上存储在波前 SGPR 中编号最高的两个 SGPR(SGPR106 和 SGPR107)里;源/目标 VCC 是这两个 SGPR 的别名。当存在陷阱处理程序时,VCC 之后会额外保留 16 个 SGPR,用于保存陷阱地址、保存的 PC 和陷阱处理程序临时数据。这些都是特权寄存器(除非设置了特权位,否则无法写入)。请注意,如果一个波前分配了 16 个 SGPR,通常有 2 个用作 VCC,剩下的 14 个可供着色器使用。着色器硬件不阻止使用全部 16 个 SGPR。
3.6.3. SGPR 对齐
在以下情况下需要使用偶地址对齐的 SGPR:
-
当使用 64 位数据时。这是与 64 位寄存器(包括 PC)之间进行数据传输所要求的。
-
当标量内存读取的地址基址来自 SGPR 对时(无论是 SGPR 内部还是之间)。
当标量内存读取返回四个或更多双字时,数据 GPR 需要四字节对齐。当 64 位数据存储在 SGPR 中时,最低有效位在 SGPR[n] 中,最高有效位在 SGPR[n+1] 中。
3.6.4. VGPR 分配与对齐
VGPR 以四个双字为一组进行分配。使用 VGPR 对的操作(例如:双精度浮点数)没有对齐限制。在物理上,VGPR 的分配可以环绕 VGPR 内存池。
3.6.5. LDS 分配与钳位
当不使用工作组时,LDS 按工作组或按.wavefront 分配。LDS 空间以连续的 128 双字块为单位,按 128 双字边界对齐的方式分配给工作组或波前。LDS 分配不会环绕 LDS 存储空间。所有对 LDS 的访问都被限制在该波前/工作组分配的空间内。
LDS 读取和写入的钳位由两个大小寄存器控制,它们包含 SPI 分配给此.wavefront 或工作组的 LDS 空间大小,以及 LDS 指令中可能指定的较小值(大小保存在 M0 中)。LDS 操作使用这两个值中较小的一个来确定如何钳位读/写地址。
3.7. M# 内存描述符
每个波前有一个 32 位的 M#(M0)寄存器,可用于:
-
本地数据共享
-
插值:保存
{ 1'b0, new_prim_mask[15:1], parameter_offset[15:0] }// 单位:字节 -
LDS 直接读取偏移和数据类型:
{ 13'b0, DataType[2:0], LDS_address[15:0] }// 地址单位:字节 -
用于内存/Vfetch → LDS 的 LDS 寻址:
{16'h0, lds_offset[15:0]}// 单位:字节
-
-
全局数据共享
{ base[15:0] , size[15:0] }// base 和 size 单位:字节
-
向量和标量指令的间接 GPR 寻址。M0 是一个无符号索引。
-
发送消息值 。
EMIT/CUT使用 M0 和 EXEC 作为发送消息的数据。
3.8. SCC:标量条件码
大多数标量 ALU 指令会设置标量条件码位,表示操作的结果。
-
比较操作:1 = 真
-
算术操作:1 = 进位
-
位/逻辑操作:1 = 结果非零
-
移动指令:不改变 SCC
SCC 可用作扩展精度整数算术的进位输入,也可用作条件移动和分支的选择器。
3.9. 向量比较:VCC 和 VCCZ
向量 ALU 比较指令设置向量条件码寄存器(1=通过,0=失败)。此外,向量比较指令可以选择将 EXEC 设置为 VCC 的值。
还有一个 VCC 汇总位 vccz,当 VCC 结果为零时被设置为 1。这对于提前退出的分支测试很有用。VCC 也会为选定的整数 ALU 操作(进位)而设置。
向量比较指令可以选择将结果写入 VCC(32位指令编码)或任何 SGPR(64位指令编码)。每次更新 VCC 时都会更新 VCCZ:向量比较和对 VCC 的标量写入都会更新。
EXEC 掩码决定哪些线程执行指令。VCC 指示哪些正在执行的线程通过了条件测试,或者哪些线程在整数加法或减法中产生了进位。
V_CMP_* ⇒ VCC[n] = EXEC[n] & (线程[n] 的测试通过)
VCC 会被完整写入;没有部分掩码更新。
VCC 物理上驻留在 SGPR 寄存器文件中,因此当一条指令将 VCC 作为源时,会计入该指令所能引用的 SGPR 总数限制。VCC 物理上位于用户 SGPR 中编号最高的两个。
关于 VCC 的着色器冒险
用户/编译器必须防止在标量 ALU 写入保存 VCC 的 SGPR 之后,立即使用 VCCZ 进行条件分支。硬件无法检测这种情况,因此需要插入一个必需的等待状态(当 SALU 写入 VCC 时硬件可以检测到,只有当 SALU 指令恰好引用了保存 VCC 的 SGPR 时,硬件才无法检测)。
3.10. 陷阱与异常寄存器
每种类型的异常都可以通过设置或清除 TRAPSTS 寄存器的 EXCP_EN 字段中的位来独立启用或禁用。本节描述控制和报告内核异常的寄存器。
所有陷阱临时 SGPR 在写入时都是特权操作------只有在陷阱处理程序中(status.priv = 1)才能写入它们。在非特权状态下,对这些寄存器的写入被忽略。TMA 和 TBA 是只读的;可以通过 S_GETREG_B32 指令访问。
当发生陷阱时(无论是用户发起、异常还是主机发起),着色器硬件会生成一条 S_TRAP 指令。这将陷阱信息加载到一对 SGPR 中:
{TTMP1, TTMP0} = {3'h0, pc_rewind[3:0], HT[0], trapID[7:0], PC[47:0]}。
对于主机发起的陷阱,HT 设为 1;对于用户陷阱或异常,HT 设为 0。TRAP_ID 对于异常为零,对于用户/主机陷阱则为对应的 trapID。进入陷阱处理程序时,出错指令的 PC 将是:(PC - PC_rewind*4)。
STATUS.TRAP_EN - 此位向着色器指示是否存在陷阱处理程序。当不存在时,无论它们是浮点异常、用户还是主机发起的陷阱,都不会被处理。当存在陷阱处理程序时,wavefront 会额外使用 16 个 SGPR 进行陷阱处理。如果 trap_en == 0,所有陷阱和异常都被忽略,s_trap 指令会被硬件转换为 NOP。
MODE.EXCP_EN[8:0] - 浮点异常启用位。定义哪些异常和事件会引发陷阱。
3.10.1. 陷阱状态寄存器
陷阱状态寄存器记录先前见过的陷阱或异常。它可以被内核读取和写入。
表 5. 异常字段位
| 字段 | 位 | 描述 |
|---|---|---|
| EXCP | 8:0 | 记录已发生异常的 状态位。这些位是粘滞 的,会累积结果,直到着色器程序将其清除。无论 EXCP_EN 如何设置,这些位都会累积结果。无需着色器特权即可读取或写入这些位。 位 异常 0 无效操作 1 输入非规格化数 2 除零 3 上溢 4 下溢 5 不精确结果 6 整数除零 7 地址监视 8 内存违规 |
| SAVECTX | 10 | 由主机命令设置的位 ,指示此 wavefront 必须跳转到其陷阱处理程序并保存其上下文。此位必须由陷阱处理程序使用 S_SETREG 指令清除。注意 - 着色器可以将此位置 1 以触发保存上下文的陷阱,由于硬件延迟,着色器在触发陷阱前最多可能再执行 2 条额外指令。 |
| ILLEGAL_INST | 11 | 检测到非法指令。 |
| ADDR_WATCH1-3 | 14:12 | 指示地址监视点 1、2 或 3 已被命中。位 12 是地址监视点 1;位 13 是地址监视点 2;位 14 是地址监视点 3。 |
| EXCP_CYCLE | 21:16 | 当浮点异常发生时,此字段告知陷阱处理程序异常发生在哪个周期 。对于常规浮点操作为 0-3,对于双精度浮点加法为 0-7,对于双精度浮点乘加或超越函数为 0-15。此寄存器记录第一个启用的(未屏蔽的)异常发生的周期号。 EXCP_CYCLE[1:0] 相位 :线程 0-15 处于相位 0,线程 48-63 处于相位 3。 EXCP_CYCLE[3:2] 多槽阶段 。 EXCP_CYCLE[5:4] 混合阶段:用于以较低速率运行的机器。 |
| DP_RATE | 31:29 | 决定着色器如何解释 TRAP_STS.cycle。不同的向量着色器处理器以不同的速率处理指令。 |
3.11. 内存违规
内存违规在以下情况被报告:
-
LDS 对齐错误。
-
内存读/写/原子操作对齐错误。
-
Flat 访问地址无效(不在任何孔径内)。
-
写入只读表面。
-
GDS 对齐或地址范围错误。
-
GWS 操作中止(信号量或屏障未执行)。
指令或标量数据访问不报告内存违规。
如果 LDS 地址越界,内存缓冲区到 LDS 的操作不会返回内存违规,但会屏蔽掉会越界的线程对应的 EXEC 位。
当内存访问违规时,相应的内存(LDS 或纹理缓存)会向 wavefront 返回 MEM_VIOL。这被存储在波前的 TRAPSTS.mem_viol 位中。此位是粘滞的,一旦设置为 1,它将保持为 1,直到用户清除它。
有一个对应的异常启用位。如果在此位置 1 时内存返回违规,wavefront 会跳转到陷阱处理程序。
内存违规报告不是精确的。当 LDS 或纹理缓存处理地址时报告违规;在此期间,wavefront 可能已经处理了更多指令。当报告 mem_viol 时,保存的程序计数器是下一条要执行的指令的 PC;它与引发错误的指令没有关联。