/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /** * @file test_gdn_layout.c * @brief OFF-HARDWARE acceptance for the GATED-DELTA-NET host STAGER's address arithmetic — * the four conversions in `src/accel/tt/pn_tt_gdn_layout.c`. * * Opens no device. Build with `make USE_TT=1 tests/test_gdn_layout` and run it anywhere, * including on the Mac where the code is written. * * --------------------------------------------------------------------------- * WHAT THIS IS FOR, AND WHY IT SIMULATES THE READER * --------------------------------------------------------------------------- * The stager and the two dataflow kernels are the two ends of a contract NOTHING checks at * run time: a stager that disagrees about where a channel or a head sits produces plausible * numbers from the first token instead of an error. `foundry/tt/tests/test_gdn_ops.c` pins * the ACCESSORS the kernels compute their offsets with; this pins what the HOST writes into * the addresses those accessors name. * * So every check below reads the staged bytes back the way the READER would — through * `ftg_delta_head_tile()`, `ftg_conv_plane_tile()`, `ftg_y_tile()` and the face index — and * asserts it recovers the value the CPU ORACLE (`pn_qwen35moe_ssm_recurrence()`) says belongs * there. Nothing here is compared against the function that produced it. * * THE ONE THAT MATTERS MOST is the conv weight. The oracle stores it row-major * `[conv_dim, taps]` and the card wants `taps` TAP-MAJOR PLANES, so the stager TRANSPOSES. * A reshape is the same bytes, the same length and a different answer, and the arm below * asserts that the two are not merely different in principle but disagree at a named channel. * * --------------------------------------------------------------------------- * q | k | v AND y ARE HEAD-MAJOR ROW-0 STRIPS, AND THERE IS NO STAGER LEFT FOR THEM * --------------------------------------------------------------------------- * A head's `state` values are `state/32` CONSECUTIVE tiles, all of the data in ROW 0 of each, * head `h` starting at `pn_tt_gdn_head_tile(h, state)` = `h * state/32`. That is the geometry * `pn_tt_gdn_row0_offset()` already addresses and the geometry PHASE CONV's output already * has, so there is nothing to tilize: `pn_tt_gdn_stage_stream()` and its inverse are GONE and * the traffic is `pn_tt_fp32_row0_move()`, two 64 B chunks a tile. * * They used to be tilized `[heads, state]` matrices with ONE ROW PER HEAD — 32x denser, and it * forced the host to de-tilize conv_out, L2-normalise on the CPU and re-tilize between the two * phases, 49,408 B a layer over PCIe. The arms below therefore test what a MOVE plus the * offsets place, not what a tilizer produces: `strip_write()` here is `pn_tt_fp32_row0_move()` * with the NoC swapped for a memcpy, which is the only part of it a machine without a card can * run. The one-row-per-head arithmetic is asserted to DISAGREE at a named head, the same way * the reshape is for the conv weight. */ #include "../src/accel/tt/pn_tt_gdn_layout.h" #include #include #include #include #include "tt/ft_tt_op_gdn.h" #include "tt/ft_tt_tilize.h" /* The device's L1 pointer attribute is the SFPI GCC fork's, not the contract's; the header * guards it so a host compiler can read the file. Same escape test_gdn_ops.c uses. */ #define FTQ_L1_PTR #include "tt/kernels/resident_gdn_protocol.h" static int g_failed = 0; static int g_checks = 0; #define CHECK(cond, msg) \ do { \ g_checks++; \ if (!(cond)) { \ fprintf(stderr, " FAIL %s (%s:%d)\n", (msg), __FILE__, __LINE__); \ g_failed++; \ } else { \ fprintf(stderr, " ok %s\n", (msg)); \ } \ } while (0) /** Ornith's shape, read off `ft_tt_op_gdn.h`'s own constants rather than retyped. */ static ftg_shape ornith_shape(uint32_t phase) { ftg_shape shape; shape.phase = phase; shape.state_tiles = FT_TT_OP_GDN_QWEN35_STATE / 32u; shape.v_heads = FT_TT_OP_GDN_QWEN35_V_HEADS; shape.k_heads = FT_TT_OP_GDN_QWEN35_K_HEADS; shape.conv_tile_cols = FT_TT_OP_GDN_QWEN35_CONV_DIM / 32u; shape.conv_taps = FT_TT_OP_GDN_QWEN35_CONV_TAPS; shape.tile_bytes = FT_TT_OP_GDN_FP32_TILE_BYTES; shape.tile_count = 0u; return shape; } /** The float a tilized strip holds at [row][column] of tile `tile`. The reader's own * arithmetic, spelled from `ft_tt_tilize.h` rather than from the stager. */ static float tile_element(const unsigned char *tiles, uint32_t tile, uint32_t row, uint32_t column) { const float *base = (const float *)(const void *)tiles; return base[(size_t)tile * 1024u + ft_tt_tile_face_index(row, column)]; } /* ================================================================ * Logical row 0 of a strip of fp32 tiles * ================================================================ */ static void test_row0_offsets(void) { const uint32_t conv_dim = 256u; const uint32_t tiles = conv_dim / 32u; float *source = malloc((size_t)conv_dim * sizeof(float)); unsigned char *staged = calloc((size_t)tiles, PN_TT_GDN_LAYOUT_TILE_BYTES); uint32_t index; int recovered = 1; fprintf(stderr, "\nlogical row 0 of an fp32 tile — the two 64 B pieces\n"); /* Worked out from ft_tt_tilize.h: face 0 is elements [0,256) and face 1 is [256,512), so * in fp32 the two halves of row 0 are 1024 B apart inside a 4096 B tile. */ CHECK(pn_tt_gdn_row0_offset(0u, 0u) == 0u, "tile 0 chunk 0 is at byte 0"); CHECK(pn_tt_gdn_row0_offset(0u, 1u) == 1024u, "tile 0 chunk 1 is 1024 B on, not 512"); CHECK(pn_tt_gdn_row0_offset(1u, 0u) == 4096u, "tile 1 chunk 0 is one whole tile on"); CHECK(pn_tt_gdn_row0_offset(3u, 1u) == 3u * 4096u + 1024u, "tile 3 chunk 1 is 13312"); CHECK(PN_TT_GDN_LAYOUT_ROW0_CHUNK_BYTES == 64u, "an fp32 row-0 chunk is exactly the 64 B DRAM read granule — nothing discarded"); /* And the semantic version: a strip tilized as ONE row, read back through those offsets, * has to reproduce the source. This is what a wrong face stride breaks. */ for (index = 0; index < conv_dim; index++) source[index] = (float)index + 0.5f; if (pn_tt_gdn_stage_plane(source, 0u, 1u, conv_dim, source, staged) != 0) recovered = 0; for (index = 0; index < conv_dim && recovered; index++) { const uint32_t tile = index / 32u; const uint32_t chunk = (index % 32u) / 16u; const float *piece = (const float *)(const void *)(staged + pn_tt_gdn_row0_offset(tile, chunk)); if (piece[index % 16u] != (float)index + 0.5f) recovered = 0; } CHECK(recovered, "row 0 read through the chunk offsets reproduces the strip, element for " "element"); free(source); free(staged); } /* ================================================================ * ANY logical row — the chunk phase-isolation arm's row shuttle * ================================================================ */ /** * The arm that says WHICH chunk kernel owns the measured prefill divergence shuttles row `t` * of a chunk's buffer to row 0 and back, so every one of its answers rests on this offset. * * THE PLAUSIBLE WRONG ANSWER IS A FLAT `row * 64`, which is right for rows 0-15 and wrong for * 16-31 because the tile is four 16x16 FACES, not one 32x32 block. That mistake would read * half of every chunk out of the wrong face and the attribution would be an artefact — so it * is asserted against here, not just avoided. */ static void test_row_offsets(void) { const uint32_t rows = 32u; const uint32_t cols = 128u; const uint32_t tiles = cols / 32u; float *source = malloc((size_t)rows * cols * sizeof(float)); unsigned char *staged = calloc((size_t)tiles, PN_TT_GDN_LAYOUT_TILE_BYTES); uint32_t row; uint32_t col; int recovered = 1; fprintf(stderr, "\nany logical row of an fp32 tile — the phase-isolation arm's shuttle\n"); CHECK(pn_tt_gdn_row_offset(0u, 0u, 0u) == pn_tt_gdn_row0_offset(0u, 0u) && pn_tt_gdn_row_offset(2u, 0u, 1u) == pn_tt_gdn_row0_offset(2u, 1u), "row 0 of the general offset IS pn_tt_gdn_row0_offset, tile and chunk alike"); CHECK(pn_tt_gdn_row_offset(0u, 1u, 0u) == 64u, "row 1 is 64 B down inside face 0"); CHECK(pn_tt_gdn_row_offset(0u, 16u, 0u) == 2048u, "row 16 crosses into FACE 2 — the boundary a flat row*64 misses"); CHECK(pn_tt_gdn_row_offset(0u, 16u, 0u) != 16u * 64u, "and it is not 1024: the flat formula is the wrong answer this test exists for"); CHECK(pn_tt_gdn_row_offset(0u, 31u, 1u) == 3u * 1024u + 15u * 64u, "row 31 chunk 1 is the last 64 B of face 3"); CHECK(pn_tt_gdn_row_offset(2u, 5u, 1u) == 2u * 4096u + 1024u + 5u * 64u, "tile 2 row 5 chunk 1 composes tile, face and in-face row"); /* The semantic version: a REAL 32-row tilization, read back through these offsets, has to * reproduce every element — the row-0 test's argument at 32 rows instead of one. */ for (row = 0; row < rows; row++) for (col = 0; col < cols; col++) source[(size_t)row * cols + col] = (float)(row * 1000u + col) + 0.5f; if (ft_tt_tilize_strip(source, sizeof(float), rows, cols, (size_t)cols * sizeof(float), staged) != FT_TT_TILIZE_OK) recovered = 0; for (row = 0; row < rows && recovered; row++) { for (col = 0; col < cols; col++) { const uint32_t tile = col / 32u; const uint32_t chunk = (col % 32u) / 16u; const float *piece = (const float *)(const void *)(staged + pn_tt_gdn_row_offset(tile, row, chunk)); if (piece[col % 16u] != (float)(row * 1000u + col) + 0.5f) { recovered = 0; break; } } } CHECK(recovered, "every element of a 32x128 tilization is where pn_tt_gdn_row_offset says"); free(source); free(staged); } /* ================================================================ * The conv weight — the TRANSPOSE * ================================================================ */ static void test_conv_weight_transpose(void) { const ftg_shape shape = ornith_shape(FT_TT_OP_GDN_PHASE_CONV); const uint32_t conv_dim = 256u; /* a small stand-in; the arithmetic is per channel */ const uint32_t taps = FT_TT_OP_GDN_QWEN35_CONV_TAPS; const uint32_t tiles = conv_dim / 32u; float *weight = malloc((size_t)conv_dim * taps * sizeof(float)); float *scratch = malloc((size_t)conv_dim * sizeof(float)); unsigned char *planes = calloc((size_t)tiles * taps, PN_TT_GDN_LAYOUT_TILE_BYTES); uint32_t channel, tap; int transposed = 1; int rows_padded = 1; fprintf(stderr, "\nthe conv1d weight — TAP-MAJOR PLANES, which is a TRANSPOSE\n"); /* The oracle's order: `w[c*taps + tap]` — qwen35moe_ssm.c's `cw = w + c*k; cw[tap]`. The * value encodes both indices so a mis-stage cannot land on a coincidence. */ for (channel = 0; channel < conv_dim; channel++) for (tap = 0; tap < taps; tap++) weight[(size_t)channel * taps + tap] = (float)channel * 16.0f + (float)tap; for (tap = 0; tap < taps; tap++) { /* THE TRANSPOSE: stride `taps`, starting at `tap`. */ if (pn_tt_gdn_stage_plane(weight, tap, taps, conv_dim, scratch, planes + (size_t)tap * tiles * PN_TT_GDN_LAYOUT_TILE_BYTES) != 0) transposed = 0; } for (tap = 0; tap < taps && transposed; tap++) { for (channel = 0; channel < conv_dim; channel++) { /* Read it back the way the READER does: plane tile, row 0, column c%32. The * plane base is `ftg_conv_plane_tile()`, which is the accessor the kernel uses. */ const uint32_t tile = tap * tiles + channel / 32u; const float got = tile_element(planes, tile, 0u, channel % 32u); if (got != (float)channel * 16.0f + (float)tap) transposed = 0; } } CHECK(transposed, "plane t, tile c/32, [0][c%32] holds w[c][t] — the oracle's own value"); /* The plane index the reader computes for a NON-zero column, against a literal. */ CHECK(ftg_conv_plane_tile(&shape, 3u, 5u) == 3u * 256u + 5u, "the reader reaches tap 3 column 5 at plane tile 773 (tap-major, not channel-major)"); /* Rows 1-31 are the tile quantum. They must be ZERO: the compute thread multiplies whole * tiles, and a non-zero pad row would come back through SiLU as a non-zero conv_out row, * which is the strip invariant the rest of the forward depends on. */ for (channel = 0; channel < conv_dim && rows_padded; channel++) { uint32_t row; for (row = 1u; row < 32u && rows_padded; row++) { if (tile_element(planes, channel / 32u, row, channel % 32u) != 0.0f) rows_padded = 0; } } CHECK(rows_padded, "rows 1-31 of every plane tile are zero — the tile quantum, staged"); /* AND THE RESHAPE IS A DIFFERENT ANSWER. Not "could be": at channel 1 of plane 1 the * transpose gives w[1][1] = 17 and the reshape gives w[64][1] = 1025. Asserting they * disagree is what makes the distinction a fact rather than a caution. */ { float transposed_value, reshaped_value; (void)pn_tt_gdn_stage_plane(weight, 1u, taps, conv_dim, scratch, planes); transposed_value = tile_element(planes, 0u, 0u, 1u); (void)pn_tt_gdn_stage_plane(weight, 1u * conv_dim, 1u, conv_dim, scratch, planes); reshaped_value = tile_element(planes, 0u, 0u, 1u); CHECK(transposed_value == 1.0f * 16.0f + 1.0f && reshaped_value != transposed_value, "the RESHAPE (first = t*conv_dim, stride 1) disagrees with the transpose at " "channel 1 — the mutation PN_TT_RED=gdn_conv_reshape injects"); } free(weight); free(scratch); free(planes); } /* ================================================================ * The DELTA input — q | k | v, and y coming back. HEAD-MAJOR ROW-0 STRIPS. * ================================================================ */ /** Fill `[heads, state]` with a value that encodes both indices. */ static void fill_matrix(float *matrix, uint32_t heads, uint32_t state, float tag) { uint32_t head, index; for (head = 0; head < heads; head++) for (index = 0; index < state; index++) matrix[(size_t)head * state + index] = tag + (float)head * 1024.0f + (float)index; } /** * `pn_tt_fp32_row0_move()` with the NoC replaced by a memcpy — the ONLY part of that function * a machine with no card can run, and the part the layout lives in. Two 64 B chunks a tile, at * `pn_tt_gdn_row0_offset(first_tile + t, chunk)`. Everything the real one adds is a transport. */ static void strip_write(unsigned char *strip, uint32_t first_tile, const float *values, uint32_t tiles) { uint32_t tile, chunk; for (tile = 0; tile < tiles; tile++) { for (chunk = 0; chunk < PN_TT_GDN_LAYOUT_ROW0_CHUNKS; chunk++) { const float *host = values + (size_t)tile * PN_TT_GDN_LAYOUT_TILE_DIM + (size_t)chunk * PN_TT_GDN_LAYOUT_ROW0_CHUNK_ELEMS; memcpy(strip + pn_tt_gdn_row0_offset(first_tile + tile, chunk), host, PN_TT_GDN_LAYOUT_ROW0_CHUNK_BYTES); } } } /** The read half of the same primitive. */ static void strip_read(const unsigned char *strip, uint32_t first_tile, float *values, uint32_t tiles) { uint32_t tile, chunk; for (tile = 0; tile < tiles; tile++) { for (chunk = 0; chunk < PN_TT_GDN_LAYOUT_ROW0_CHUNKS; chunk++) { float *host = values + (size_t)tile * PN_TT_GDN_LAYOUT_TILE_DIM + (size_t)chunk * PN_TT_GDN_LAYOUT_ROW0_CHUNK_ELEMS; memcpy(host, strip + pn_tt_gdn_row0_offset(first_tile + tile, chunk), PN_TT_GDN_LAYOUT_ROW0_CHUNK_BYTES); } } } /** * `pn_tt_gdn_head_tile()` on its own, and composed with `pn_tt_gdn_row0_offset()` into the * BYTE offset of head `h`'s tile `t` — which is what a caller actually passes to the move. */ static void test_head_tile_offsets(void) { const uint32_t state = FT_TT_OP_GDN_QWEN35_STATE; /* 128 */ const uint32_t per_head = state / 32u; /* 4 */ const ftg_shape shape = ornith_shape(FT_TT_OP_GDN_PHASE_DELTA); uint32_t head; int agrees = 1; int strided = 1; fprintf(stderr, "\nhead-major ROW-0 strips — where head h's four tiles start\n"); CHECK(pn_tt_gdn_head_tile(0u, state) == 0u && pn_tt_gdn_head_tile(1u, state) == 4u && pn_tt_gdn_head_tile(31u, state) == 124u, "head 0, 1 and 31 start at tiles 0, 4 and 124 — h * state/32"); CHECK(pn_tt_gdn_head_tile(32u, state) == 128u && pn_tt_gdn_head_tile(63u, state) == 252u, "and it keeps going past 32 heads: there is no row block to wrap into"); CHECK(pn_tt_gdn_head_tile(2u, 256u) == 16u && pn_tt_gdn_head_tile(2u, 64u) == 4u, "the stride is state/32, so S=256 puts head 2 at tile 16 and S=64 at tile 4"); /* THE BYTE OFFSET, which is the number the move is handed: head 3's tile 2, chunk 1. Head * 3 starts at tile 12, so that is tile 14 chunk 1 = 14*4096 + 1024 = 58368. */ CHECK(pn_tt_gdn_row0_offset(pn_tt_gdn_head_tile(3u, state) + 2u, 1u) == 58368u, "head 3's tile 2, chunk 1 is byte 58368 of the strip"); /* The HOST's head_tile and the DEVICE's accessors are two spellings of one contract: the * y tile the writer computes and the v tile the reader computes must both be the stager's * offset, or the two ends disagree with nothing to catch it. */ for (head = 0; head < shape.v_heads; head++) { if (ftg_y_tile(&shape, head, 0u) != pn_tt_gdn_head_tile(head, state)) agrees = 0; if (ftg_delta_head_tile(&shape, kGdnStreamV, head) != ftg_delta_stream_base(&shape, kGdnStreamV) + pn_tt_gdn_head_tile(head, state)) agrees = 0; } CHECK(agrees, "pn_tt_gdn_head_tile agrees with ftg_y_tile and ftg_delta_head_tile at every " "one of the 32 v-heads — the host and the kernels compute the same tile"); /* AND THE ONE-ROW-PER-HEAD ARITHMETIC IS A DIFFERENT ANSWER. It gave every head of a row * block the SAME `(h/32)*state_tiles` tiles and told them apart by row `h%32`, so its * stride was 0 where this one's is `state/32`. Asserting they disagree at head 1 is what * makes the change a fact rather than a note. */ for (head = 0; head + 1u < 64u; head++) { if (pn_tt_gdn_head_tile(head + 1u, state) - pn_tt_gdn_head_tile(head, state) != per_head) strided = 0; } CHECK(strided && pn_tt_gdn_head_tile(1u, state) != (1u / 32u) * per_head, "consecutive heads are state/32 apart — the old (h/32)*state_tiles packing put " "heads 0 and 1 on the SAME tile and this disagrees with it at head 1"); } static void test_delta_input_layout(void) { const ftg_shape shape = ornith_shape(FT_TT_OP_GDN_PHASE_DELTA); const uint32_t state = FT_TT_OP_GDN_QWEN35_STATE; const uint32_t per_head = state / 32u; const uint32_t k_heads = shape.k_heads; const uint32_t v_heads = shape.v_heads; const uint32_t input_tiles = ftg_delta_input_tiles(&shape); float *q = malloc((size_t)k_heads * state * sizeof(float)); float *k = malloc((size_t)k_heads * state * sizeof(float)); float *v = malloc((size_t)v_heads * state * sizeof(float)); unsigned char *staged = calloc(input_tiles, PN_TT_GDN_LAYOUT_TILE_BYTES); uint32_t head, index; int placed = 1; int repeat_reads = 1; int pad_rows_zero = 1; fprintf(stderr, "\nthe DELTA q | k | v — ONE head-major row-0 strip, q and k UN-repeated\n"); fill_matrix(q, k_heads, state, 100000.0f); fill_matrix(k, k_heads, state, 200000.0f); fill_matrix(v, v_heads, state, 300000.0f); /* THE WHOLE OF THE STAGING, now: three moves at three head-major offsets. No tilize * buffer, no scratch, nothing to get wrong except the offset. */ for (head = 0; head < k_heads; head++) { strip_write(staged, ftg_delta_stream_base(&shape, kGdnStreamQ) + pn_tt_gdn_head_tile(head, state), q + (size_t)head * state, per_head); strip_write(staged, ftg_delta_stream_base(&shape, kGdnStreamK) + pn_tt_gdn_head_tile(head, state), k + (size_t)head * state, per_head); } for (head = 0; head < v_heads; head++) strip_write(staged, ftg_delta_stream_base(&shape, kGdnStreamV) + pn_tt_gdn_head_tile(head, state), v + (size_t)head * state, per_head); /* Read every element back the way `read_one_head()` does: `ftg_delta_head_tile()` for the * head's FIRST tile, then tile `index/32` of the head's own consecutive run, ROW 0. */ for (head = 0; head < v_heads && placed; head++) { for (index = 0; index < state; index++) { const uint32_t tile = ftg_delta_head_tile(&shape, kGdnStreamV, head) + index / 32u; if (tile_element(staged, tile, (uint32_t)kGdnStripRow, index % 32u) != 300000.0f + (float)head * 1024.0f + (float)index) placed = 0; } } CHECK(placed, "v-head h's element j is at ftg_delta_head_tile(V, h) + j/32, row 0, " "column j%32"); /* The heads do not overlap: head h owns tiles [h*state/32, (h+1)*state/32) and NOTHING of * head h+1's run carries head h's tag. Reading the FIRST tile of every head's successor * and finding the successor's own value is what proves the runs are disjoint. */ for (head = 0; head + 1u < v_heads && placed; head++) { const uint32_t next = ftg_delta_head_tile(&shape, kGdnStreamV, head + 1u); if (next - ftg_delta_head_tile(&shape, kGdnStreamV, head) != per_head) placed = 0; if (tile_element(staged, next, (uint32_t)kGdnStripRow, 0u) != 300000.0f + (float)(head + 1u) * 1024.0f) placed = 0; } CHECK(placed, "consecutive v-heads are state/32 tiles apart and head h+1's first tile " "holds head h+1's own element 0 — the runs do not overlap"); /* Rows 1-31 of a strip tile are never written and never read. A move that touched them * would be writing over the neighbouring 64 B granule. */ for (index = 0; index < input_tiles && pad_rows_zero; index++) { uint32_t row; for (row = 1u; row < 32u && pad_rows_zero; row++) { if (tile_element(staged, index, row, 0u) != 0.0f || tile_element(staged, index, row, 31u) != 0.0f) pad_rows_zero = 0; } } CHECK(pad_rows_zero, "rows 1-31 of every strip tile are untouched — the move writes only " "the two 64 B chunks of row 0"); /* The GQA repeat is the READER's modulo, so v-head 17 has to find k-head 1's FOUR TILES in * the UN-repeated k block — 16 heads' worth, not 32. */ for (head = 0; head < v_heads && repeat_reads; head++) { const uint32_t k_head = ftg_k_head_for(head, k_heads); const uint32_t tile = ftg_delta_head_tile(&shape, kGdnStreamK, k_head); if (tile_element(staged, tile, (uint32_t)kGdnStripRow, 7u) != 200000.0f + (float)k_head * 1024.0f + 7.0f) repeat_reads = 0; } CHECK(repeat_reads, "v-head vh reads k-head vh%16's four tiles out of a 16-head k block — " "the repeat is an address, never materialised"); /* And it is NOT the blocked mapping, which is the plausible wrong answer with a recorded * gibberish bug behind it. */ CHECK(ftg_k_head_for(17u, 16u) == 1u && 17u / (32u / 16u) == 8u, "v-head 17 pairs with k-head 1, not with k-head 8"); /* q, k and v do not overlap, and the whole staging buffer is exactly 256 tiles at Ornith: * 16*4 + 16*4 + 32*4. It was 12 as three one-row-per-head matrices. */ CHECK(ftg_delta_stream_base(&shape, kGdnStreamQ) == 0u && ftg_delta_stream_base(&shape, kGdnStreamK) == 64u && ftg_delta_stream_base(&shape, kGdnStreamV) == 128u && input_tiles == 256u, "q | k | v sit at tiles 0, 64, 128 and the staged input is 256 tiles"); /* The last v-head's run ENDS on the buffer's last tile — no hole, nothing off the end. */ CHECK(ftg_delta_head_tile(&shape, kGdnStreamV, v_heads - 1u) + per_head == input_tiles, "v-head 31's run ends exactly at tile 256 — the three blocks tile the buffer"); free(q); free(k); free(v); free(staged); } static void test_y_strip(void) { const ftg_shape shape = ornith_shape(FT_TT_OP_GDN_PHASE_DELTA); const uint32_t state = FT_TT_OP_GDN_QWEN35_STATE; const uint32_t per_head = state / 32u; const uint32_t v_heads = shape.v_heads; const uint32_t tiles = ftg_y_tile(&shape, v_heads - 1u, per_head - 1u) + 1u; float *y = malloc((size_t)v_heads * state * sizeof(float)); float *back = calloc((size_t)v_heads * state, sizeof(float)); unsigned char *staged = calloc(tiles, PN_TT_GDN_LAYOUT_TILE_BYTES); uint32_t head, index; int writer_places = 1; int injective = 1; unsigned char *seen = calloc(tiles, 1u); fprintf(stderr, "\ny — a head-major ROW-0 strip, 128 tiles at Ornith\n"); CHECK(tiles == v_heads * per_head && tiles == 128u, "y is v_heads * state/32 = 128 tiles, not one shared row block of 4"); /* The forward reads y back with the same move it wrote the input with — head h's four * tiles, in order. Round-tripping the whole strip head by head is the de-tilize that used * to be `pn_tt_gdn_unstage_stream()`. */ fill_matrix(y, v_heads, state, 400000.0f); for (head = 0; head < v_heads; head++) strip_write(staged, ftg_y_tile(&shape, head, 0u), y + (size_t)head * state, per_head); for (head = 0; head < v_heads; head++) strip_read(staged, ftg_y_tile(&shape, head, 0u), back + (size_t)head * state, per_head); CHECK(memcmp(y, back, (size_t)v_heads * state * sizeof(float)) == 0, "write then read at ftg_y_tile(h, 0) is the identity on [32, 128] — the forward's " "own read-back path"); /* The WRITER's own placement: head h's tile-row jt goes to `ftg_y_tile(h, jt)`, ROW 0. * Reading the strip at exactly those coordinates has to find y. */ for (head = 0; head < v_heads && writer_places; head++) { for (index = 0; index < state; index++) { if (tile_element(staged, ftg_y_tile(&shape, head, index / 32u), (uint32_t)kGdnStripRow, index % 32u) != 400000.0f + (float)head * 1024.0f + (float)index) writer_places = 0; } } CHECK(writer_places, "the writer's ftg_y_tile(h, jt) / row 0 finds y[h][32*jt + j]"); /* INJECTIVITY, which is the property the old geometry did not have: every head of a row * block used to land on the SAME `state_tiles` tiles and was told apart by its row, so * (h, jt) -> tile was 32-to-1. On a strip there is no row to hide in, and two heads on one * tile would be one head's answer overwriting another's. */ for (head = 0; head < v_heads && injective; head++) { uint32_t row; for (row = 0; row < per_head && injective; row++) { const uint32_t tile = ftg_y_tile(&shape, head, row); if (tile >= tiles || seen[tile]) injective = 0; else seen[tile] = 1u; } } CHECK(injective, "(head, tile_row) -> y tile is INJECTIVE over all 32 x 4 — the old " "one-row-per-head y collapsed all 32 heads onto tiles 0-3"); free(seen); free(y); free(back); free(staged); } /** 64 v-heads — twice Ornith's. Under the old packing this was the case that needed a SECOND * row block; on a strip it is simply twice as long, which is the point. */ static void test_sixty_four_heads(void) { const uint32_t state = 128u; const uint32_t heads = 64u; const uint32_t per_head = state / 32u; ftg_shape shape = ornith_shape(FT_TT_OP_GDN_PHASE_DELTA); float *source = malloc((size_t)heads * state * sizeof(float)); float *back = calloc((size_t)heads * state, sizeof(float)); unsigned char *staged = calloc((size_t)heads * per_head, PN_TT_GDN_LAYOUT_TILE_BYTES); int round_trips = 1; int index; uint32_t head; fprintf(stderr, "\n64 heads — 256 tiles, not two row blocks of 4\n"); shape.v_heads = heads; fill_matrix(source, heads, state, 500000.0f); for (head = 0; head < heads; head++) strip_write(staged, pn_tt_gdn_head_tile(head, state), source + (size_t)head * state, per_head); for (head = 0; head < heads; head++) strip_read(staged, pn_tt_gdn_head_tile(head, state), back + (size_t)head * state, per_head); if (memcmp(source, back, (size_t)heads * state * sizeof(float)) != 0) round_trips = 0; CHECK(round_trips, "write/read round-trips 64 heads across 256 tiles"); /* Head 32 is at tile 128 and holds its OWN values — not row 0 of a second block at tile 4, * which is where the old arithmetic sent it. */ index = 1; for (head = 32u; head < heads && index; head++) { uint32_t element; for (element = 0; element < state; element++) { if (tile_element(staged, ftg_y_tile(&shape, head, element / 32u), (uint32_t)kGdnStripRow, element % 32u) != 500000.0f + (float)head * 1024.0f + (float)element) index = 0; } } CHECK(index && ftg_y_tile(&shape, 32u, 0u) == 128u, "head 32 is at tile 128 with its own values, not at tile 4 as row 0 of a second " "row block"); free(source); free(back); free(staged); } int main(void) { fprintf(stderr, "Gated-Delta-Net host stager — off-hardware layout acceptance (no device " "is opened)\n"); test_row0_offsets(); test_row_offsets(); test_conv_weight_transpose(); test_head_tile_offsets(); test_delta_input_layout(); test_y_strip(); test_sixty_four_heads(); fprintf(stderr, "\n%d checks, %d failed\n", g_checks, g_failed); return g_failed == 0 ? 0 : 1; }