/** * Proper TMA load test using CUtensorMap for a (128, 16) BF16 tile. * * Step 1: Create CUtensorMap on host * Step 2: Pass to kernel, use cp.async.bulk.tensor.2d to load * Step 3: Verify the loaded data matches the original * Step 4: Use the loaded data with UMMA SW128 descriptor * * This proves the TMA + SW128 pipeline works for FMHA. */ #include #include #include #include #include typedef unsigned short bf16_t; static bf16_t f32_to_bf16_host(float f) { uint32_t u; memcpy(&u,&f,4); return (uint16_t)(u>>16); } static float bf16_to_f32_host(bf16_t h) { uint32_t u=(uint32_t)h<<16; float f; memcpy(&f,&u,4); return f; } constexpr int ROWS = 128, COLS = 16; constexpr int TILE_ROWS = 128, TILE_COLS = 16; // TMA tile dimensions // Kernel: load one tile via TMA, copy to output for verification __global__ void __launch_bounds__(32) test_tma_load_kernel(CUtensorMap* tma_desc, bf16_t* gmem_dst) { extern __shared__ char sbuf[]; uint64_t* sMbar = (uint64_t*)sbuf; bf16_t* sData = (bf16_t*)(((uintptr_t)(sbuf + 8) + 127) & ~(uintptr_t)127); // 128B aligned // Init mbarrier (1 thread) if (threadIdx.x == 0) { asm volatile("mbarrier.init.shared.b64 [%0], %1;" :: "r"((uint32_t)__cvta_generic_to_shared(sMbar)), "r"(1)); } __syncthreads(); // Issue TMA load (1 thread) if (threadIdx.x == 0) { // cp.async.bulk.tensor.2d.shared::cluster.global.mbarrier::complete_tx::bytes // [smem_dst], [tma_desc, {coord_x, coord_y}], [mbarrier] uint32_t smem_addr = (uint32_t)__cvta_generic_to_shared(sData); uint32_t mbar_addr = (uint32_t)__cvta_generic_to_shared(sMbar); // coord: (col=0, row=0) for the first tile asm volatile( "cp.async.bulk.tensor.2d.shared::cluster.global.mbarrier::complete_tx::bytes " "[%0], [%1, {%3, %4}], [%2];" :: "r"(smem_addr), "l"((uint64_t)tma_desc), "r"(mbar_addr), "r"(0), // coord_x (column) "r"(0) // coord_y (row) : "memory" ); } __syncthreads(); // Wait for TMA completion if (threadIdx.x == 0) { int phase = 0; asm volatile( "{\n\t" ".reg .pred p;\n\t" "LOOP:\n\t" "mbarrier.try_wait.parity.shared.b64 p, [%0], %1;\n\t" "@p bra DONE;\n\t" "bra LOOP;\n\t" "DONE:\n\t" "}" :: "r"((uint32_t)__cvta_generic_to_shared(sMbar)), "r"(phase) : "memory" ); } __syncthreads(); // Copy SMEM to GMEM for verification for (int i = threadIdx.x; i < ROWS * COLS; i += 32) { gmem_dst[i] = sData[i]; } } int main() { printf("=== TMA Load Test with CUtensorMap ===\n"); constexpr int TOTAL = ROWS * COLS; constexpr int DATA_BYTES = TOTAL * sizeof(bf16_t); // Allocate host data bf16_t* h_src = (bf16_t*)malloc(DATA_BYTES); bf16_t* h_dst = (bf16_t*)calloc(TOTAL, sizeof(bf16_t)); srand(42); for (int i = 0; i < TOTAL; i++) h_src[i] = f32_to_bf16_host((float)(rand()%100)/100.0f - 0.5f); // Allocate device memory bf16_t *d_src, *d_dst; cudaMalloc(&d_src, DATA_BYTES); cudaMalloc(&d_dst, DATA_BYTES); cudaMemcpy(d_src, h_src, DATA_BYTES, cudaMemcpyHostToDevice); // Create CUtensorMap for a (ROWS, COLS) BF16 tensor // Layout: (rows, cols) = (128, 16) in row-major // TMA tile: (128, 16) — one tile covers the whole matrix uint64_t gdim[] = {(uint64_t)COLS, (uint64_t)ROWS}; uint64_t gstr[] = {1, (uint64_t)COLS}; uint32_t tdim[] = {TILE_COLS, TILE_ROWS}; uint32_t tstr[] = {1, TILE_COLS}; CUtensorMap tma_desc_host; CUresult res = cuTensorMapEncodeTiled( &tma_desc_host, CU_TENSOR_MAP_DATA_TYPE_UINT16, 2, d_src, gdim, gstr, tdim, tstr, CU_TENSOR_MAP_INTERLEAVE_NONE, CU_TENSOR_MAP_SWIZZLE_NONE, // No swizzle for now CU_TENSOR_MAP_L2_PROMOTION_NONE, CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE ); if (res != CUDA_SUCCESS) { printf("cuTensorMapEncodeTiled FAILED: %d\n", res); return 1; } printf("CUtensorMap created successfully\n"); // Copy tensor map to device (must be in GMEM for the kernel to read) CUtensorMap* d_tma_desc; cudaMalloc(&d_tma_desc, sizeof(CUtensorMap)); cudaMemcpy(d_tma_desc, &tma_desc_host, sizeof(CUtensorMap), cudaMemcpyHostToDevice); // Launch kernel int smem = 8 + 128 + DATA_BYTES + 256; // mbar + alignment + data + padding test_tma_load_kernel<<<1, 32, smem>>>(d_tma_desc, d_dst); cudaError_t err = cudaDeviceSynchronize(); if (err != cudaSuccess) { printf("CUDA ERROR: %s\n", cudaGetErrorString(err)); // Try to get more info cudaError_t launch_err = cudaGetLastError(); printf("Last error: %s\n", cudaGetErrorString(launch_err)); return 1; } cudaMemcpy(h_dst, d_dst, DATA_BYTES, cudaMemcpyDeviceToHost); // Verify int mismatches = 0; float max_diff = 0; for (int i = 0; i < TOTAL; i++) { if (h_src[i] != h_dst[i]) mismatches++; float diff = fabsf(bf16_to_f32_host(h_src[i]) - bf16_to_f32_host(h_dst[i])); max_diff = fmaxf(max_diff, diff); } printf("Mismatches: %d / %d, Max diff: %.6f\n", mismatches, TOTAL, max_diff); printf("Test %s\n", mismatches == 0 ? "PASSED" : "FAILED"); cudaFree(d_src); cudaFree(d_dst); cudaFree(d_tma_desc); free(h_src); free(h_dst); return mismatches == 0 ? 0 : 1; }