平面内存指令将数据片段读入或写出VGPR(向量通用寄存器);波前(wavefront)中的每个工作项(work-item)都会独立执行此操作。与缓冲区(buffer)或图像(image)指令不同,平面指令不使用资源常量(resource constant)来定义内存表面的基地址。相反,平面指令使用来自VGPR的单个平面地址;此地址将内存视为单个平面内存空间进行寻址。该内存空间包括显存(video memory)、系统内存(system memory)、LDS(本地数据共享)内存以及暂存(scratch,私有)内存。它不包括GDS(全局数据共享)内存。平面内存空间的某些部分可能不映射到任何真实内存,访问这些区域会产生内存违规错误。地址所映射到的内存空间的确定由一组"内存窗口"(memory aperture)基地址和大小寄存器控制。
9.1. 平面内存指令
平面内存指令允许内核(kernel)读取或写入内存中的数据,或对已存在于内存中的数据执行原子操作。这些操作通过纹理L2缓存进行。指令声明哪个VGPR保存地址(32位或64位,取决于内存配置),哪个VGPR发送数据,哪个VGPR接收数据。平面指令还使用M0寄存器,如下表所述:
表41. 平面、全局和暂存微码格式
| 字段 | 位宽 | 描述 |
|---|---|---|
| OP | 7 | 操作码(Opcode)。可以是平面、暂存或全局指令。见下表。 |
| ADDR | 8 | 保存地址的VGPR。对于64位地址,ADDR包含低有效位(LSB),ADDR+1包含高有效位(MSB)。 |
| DATA | 8 | 保存数据第一个双字(Dword)的VGPR。指令可使用0-4个双字。 |
| VDST | 8 | 数据返回内核的目标VGPR,可来自LOAD指令或GLC=1的原子指令(返回操作前值)。 |
| SLC | 1 | 系统级一致性(System Level Coherent)。与GLC结合使用以确定缓存策略。 |
| GLC | 1 | 全局级一致性(Global Level Coherent)。对于原子指令,GLC:1表示返回操作前值,0表示不返回操作前值。 |
| SEG | 2 | 内存段(Memory Segment):0=FLAT,1=SCRATCH,2=GLOBAL,3=保留。 |
| LDS | 1 | 设置时,数据在LDS和内存之间移动,而不是VGPR和内存之间。仅用于全局和暂存指令;对于平面指令必须为零。 |
| NV | 1 | 非易失性(Non-volatile)。设置时,读/写操作在非易失性内存上进行。 |
| OFFSET | 13 | 地址偏移量。 暂存、全局:13位有符号字节偏移量。 平面:12位无符号偏移量(最高位被忽略)。 |
表42. 平面、全局和暂存操作码
| 平面操作码 | 全局操作码 | 暂存操作码 |
|---|---|---|
| FLAT | GLOBAL | SCRATCH |
| FLAT_LOAD_UBYTE | GLOBAL_LOAD_UBYTE | SCRATCH_LOAD_UBYTE |
| FLAT_LOAD_UBYTE_D16 | GLOBAL_LOAD_UBYTE_D16 | SCRATCH_LOAD_UBYTE_D16 |
| FLAT_LOAD_UBYTE_D16_HI | GLOBAL_LOAD_UBYTE_D16_HI | SCRATCH_LOAD_UBYTE_D16_HI |
| FLAT_LOAD_SBYTE | GLOBAL_LOAD_SBYTE | SCRATCH_LOAD_SBYTE |
| FLAT_LOAD_SBYTE_D16 | GLOBAL_LOAD_SBYTE_D16 | SCRATCH_LOAD_SBYTE_D16 |
| FLAT_LOAD_SBYTE_D16_HI | GLOBAL_LOAD_SBYTE_D16_HI | SCRATCH_LOAD_SBYTE_D16_HI |
| FLAT_LOAD_USHORT | GLOBAL_LOAD_USHORT | SCRATCH_LOAD_USHORT |
| FLAT_LOAD_SSHORT | GLOBAL_LOAD_SSHORT | SCRATCH_LOAD_SSHORT |
| FLAT_LOAD_SHORT_D16 | GLOBAL_LOAD_SHORT_D16 | SCRATCH_LOAD_SHORT_D16 |
| FLAT_LOAD_SHORT_D16_HI | GLOBAL_LOAD_SHORT_D16_HI | SCRATCH_LOAD_SHORT_D16_HI |
| FLAT_LOAD_DWORD | GLOBAL_LOAD_DWORD | SCRATCH_LOAD_DWORD |
| FLAT_LOAD_DWORDX2 | GLOBAL_LOAD_DWORDX2 | SCRATCH_LOAD_DWORDX2 |
| FLAT_LOAD_DWORDX3 | GLOBAL_LOAD_DWORDX3 | SCRATCH_LOAD_DWORDX3 |
| FLAT_LOAD_DWORDX4 | GLOBAL_LOAD_DWORDX4 | SCRATCH_LOAD_DWORDX4 |
| FLAT_STORE_BYTE | GLOBAL_STORE_BYTE | SCRATCH_STORE_BYTE |
| FLAT_STORE_BYTE_D16_HI | GLOBAL_STORE_BYTE_D16_HI | SCRATCH_STORE_BYTE_D16_HI |
| FLAT_STORE_SHORT | GLOBAL_STORE_SHORT | SCRATCH_STORE_SHORT |
| FLAT_STORE_SHORT_D16_HI | GLOBAL_STORE_SHORT_D16_HI | SCRATCH_STORE_SHORT_D16_HI |
| FLAT_STORE_DWORD | GLOBAL_STORE_DWORD | SCRATCH_STORE_DWORD |
| FLAT_STORE_DWORDX2 | GLOBAL_STORE_DWORDX2 | SCRATCH_STORE_DWORDX2 |
| FLAT_STORE_DWORDX3 | GLOBAL_STORE_DWORDX3 | SCRATCH_STORE_DWORDX3 |
| FLAT_STORE_DWORDX4 | GLOBAL_STORE_DWORDX4 | SCRATCH_STORE_DWORDX4 |
| FLAT_ATOMIC_SWAP | GLOBAL_ATOMIC_SWAP | 无 |
| FLAT_ATOMIC_CMPSWAP | GLOBAL_ATOMIC_CMPSWAP | 无 |
| FLAT_ATOMIC_ADD | GLOBAL_ATOMIC_ADD | 无 |
| FLAT_ATOMIC_SUB | GLOBAL_ATOMIC_SUB | 无 |
| FLAT_ATOMIC_SMIN | GLOBAL_ATOMIC_SMIN | 无 |
| FLAT_ATOMIC_UMIN | GLOBAL_ATOMIC_UMIN | 无 |
| FLAT_ATOMIC_SMAX | GLOBAL_ATOMIC_SMAX | 无 |
| FLAT_ATOMIC_UMAX | GLOBAL_ATOMIC_UMAX | 无 |
| FLAT_ATOMIC_AND | GLOBAL_ATOMIC_AND | 无 |
| FLAT_ATOMIC_OR | GLOBAL_ATOMIC_OR | 无 |
| FLAT_ATOMIC_XOR | GLOBAL_ATOMIC_XOR | 无 |
| FLAT_ATOMIC_INC | GLOBAL_ATOMIC_INC | 无 |
| FLAT_ATOMIC_DEC | GLOBAL_ATOMIC_DEC | 无 |
上述原子指令也有"_X2"版本(64位)。
9.2. 指令
FLAT指令集几乎与缓冲区(Buffer)指令集相同,但没有FORMAT(格式)读写操作。与缓冲区指令不同,FLAT指令不能直接将数据返回到LDS,只能返回到VGPRs。
FLAT指令不使用资源常量(V#)或采样器(S#);但是,它们需要一个SGPR对(SGPR-pair)来保存暂存空间信息,以防任何线程的地址解析为暂存空间。详情参见暂存空间部分。
在内部,FLAT指令同时作为LDS指令和缓冲区指令执行;因此,它们会递增VM_CNT和LGKM_CNT计数器,并且只有在两者都被递减后才被认为完成。无法事先确定FLAT指令是否仅使用LDS或TA内存空间。
9.2.1. 顺序性
平面指令之间可以乱序完成。如果一条平面指令在纹理缓存中找到其所有数据,而下一个平面指令在LDS中找到其所有数据,则第二条指令可能先完成。如果两次取数返回数据到同一个VGPR,则结果未知。
9.2.2. 重要的时序考虑
由于FLAT加载的数据可能来自LDS或纹理缓存,并且这些单元具有不同的延迟,因此在VM_CNT和LGKM_CNT计数器方面存在潜在的竞态条件。因此,在FLAT指令之后唯一合理的S_WAITCNT值是零。
9.3. 寻址
FLAT指令支持64位和32位寻址。地址大小使用模式寄存器(PTR32)设置,并且每个波(wave)存储该值的本地副本。
用于窗口检查的地址在32位和64位模式下有所不同;但此处不涵盖此内容。
64位地址的存储方式为:低有效位(LSB)存储在ADDR指定的VGPR中,高有效位(MSB)存储在ADDR+1指定的VGPR中。
对于暂存空间,纹理单元从VGPR获取地址并执行以下操作:
Address = VGPR[addr] + TID_in_wave * Size
- private aperture base (in SH_MEM_BASES)
+ offset (from flat_scratch)
9.4. 全局指令
全局指令类似于平面指令,但程序员必须确保没有线程访问LDS空间;因此,全局指令不使用LDS带宽。
全局指令提供两种寻址类型:
-
内存地址 = VGPR地址 + 指令偏移量。
-
内存地址 = SGPR地址 + VGPR偏移量 + 指令偏移量。
地址组件的大小取决于ADDRESS_MODE:32位或64位指针。VGPR偏移量为32位。
这些指令还允许数据直接在LDS和内存之间移动,而无需经过VGPRs。
由于这些指令不访问LDS,仅使用VM_CNT,而不使用LGKM_CNT。如果全局指令尝试访问LDS,则指令返回MEM_VIOL(内存违规)。
9.5. 暂存指令
暂存指令类似于平面指令,但程序员必须确保没有线程访问LDS空间,并且内存空间是交错(swizzled)的。因此,暂存指令不使用LDS带宽。
暂存指令还支持多双字访问和非对齐访问(尽管非对齐访问速度较慢)。
暂存指令使用以下寻址方式:
-
内存地址 = flat_scratch.addr + swizzle(V/SGPR偏移量 + 指令偏移量, 线程ID)
-
偏移量可以来自SGPR或VGPR,并且是32位无符号字节。
地址组件的大小取决于ADDRESS_MODE:32位或64位指针。VGPR偏移量为32位。
这些指令还允许数据直接在LDS和内存之间移动,而无需经过VGPRs。
由于这些指令不访问LDS,仅使用VM_CNT,而不使用LGKM_CNT。暂存指令不可能访问LDS;因此,不会进行错误或窗口检查。
9.6. 内存错误检查
TA(纹理数组)和LDS都可以报告由于错误地址导致的错误。这可能在以下情况下发生:
-
无效地址(在任何窗口之外)
-
写入只读表面
-
数据未对齐
-
地址越界:
-
LDS访问地址超出范围:[0, MIN(M0, LDS_SIZE)-1]
-
暂存访问地址超出范围:[0, scratch-size -1]
-
对于具有错误地址的线程的策略是:超出此范围的写入不会写入值,读取返回零。
来自LDS或TA的地址错误通过它们各自的"指令完成"总线作为MEM_VIOL返回。这会设置波(wave)的MEM_VIOL TrapStatus位,并在相应的EXCPEN位设置时导致异常(trap)。
9.7. 数据
FLAT指令可以在VGPRs和/或内存中使用零到四个连续的双字数据。DATA字段确定哪些VGPR(如果有)提供源数据,而VDST VGPRs保存返回数据(如果有)。不进行数据格式转换。
9.8. 暂存空间(私有)
暂存空间(线程私有内存)是由窗口寄存器定义的内存区域。当地址落在暂存空间中时,硬件会自动执行额外的地址计算。内核必须提供额外信息(以FLAT_SCRATCH寄存器的形式)以便进行此计算。
FLAT_SCRATCH地址会随着每个FLAT请求自动发送。
FLAT_SCRATCH是一个64位字节地址。着色器通过将两个独立的值相加来构成该值:基地址(可以通过初始化的SGPR传递,或通过常量缓冲区传递)和每波分配偏移量(也在SGPR中初始化)。