/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /** * test_multicast_rectangle.c — the multicast rectangle rules, OFF HARDWARE. * * Everything in ft_tt_multicast.c and the pure half of ft_tt_group_shared_operand.c is * arithmetic, and it is separated out precisely so it can be proven on the Mac while * somebody else owns the silicon. What it is protecting against is not a failed * transaction: a wrong rectangle writes into a core another group is using, and a * coordinate that does not fit the 6-bit node-id field lands on a DIFFERENT live endpoint, * which on this board is a hung NoC and a walk to the machine (DEV_NOTES.md §1). * * The checks that matter here are the REFUSALS, and each one is written so that deleting * the guard it tests makes it go red. In particular: * * - The 64-bit encoding is checked against LITERAL expected values, not against the * kernel-side header. Two implementations that agree with each other and disagree with * the hardware is exactly the failure a round trip cannot see (DEV_NOTES.md §3), so the * expected words are computed by hand from the field layout written out in the comments. * - The NoC-1 corner swap is checked as a DIFFERENT value from NoC 0's, on a rectangle * whose two corners are not equal — a square would make the swap invisible, which is * the "an assert on a shape whose two dimensions happen to be equal is vacuous" trap * (DEV_NOTES.md §3). * - The fill check is given a set that is NOT a rectangle in the exact way a real caller * would produce one: a run of consecutive live indices that wraps a row. * * Build and run: * make -f Makefile.multicast check */ #define _POSIX_C_SOURCE 200809L #include "ft_tt_group_shared_operand.h" #include "ft_tt_multicast.h" #include "ft_tt_test_report.h" #include #include #define L1_ADDRESS 0x00010000u #define PAYLOAD_BYTES 2048u static ft_tt_noc_coord coord(uint32_t x, uint32_t y) { ft_tt_noc_coord result; result.x = x; result.y = y; return result; } /* ------------------------------------------------------------------------- */ static void check_rectangle_from_a_row(void) { ft_tt_noc_coord members[4]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("a 1x4 row of cores, sender outside it"); members[0] = coord(3u, 7u); members[1] = coord(4u, 7u); members[2] = coord(5u, 7u); members[3] = coord(6u, 7u); rc = ft_tt_multicast_rectangle_from_cores(members, 4u, coord(2u, 7u), &rectangle); ft_tt_test_check_fmt("accepted", rc == FT_TT_OK, "rc=%d (%s)", rc, ft_tt_error_string(rc)); if (rc != FT_TT_OK) return; ft_tt_test_check_fmt("the corners are the row's ends", rectangle.x_low == 3u && rectangle.x_high == 6u && rectangle.y_low == 7u && rectangle.y_high == 7u, "(%u,%u)..(%u,%u)", rectangle.x_low, rectangle.y_low, rectangle.x_high, rectangle.y_high); ft_tt_test_check_fmt("4 nodes, and 4 destinations because the sender is outside", rectangle.node_count == 4u && rectangle.destination_count == 4u && !rectangle.sender_inside, "nodes %u, destinations %u, sender %s", rectangle.node_count, rectangle.destination_count, rectangle.sender_inside ? "inside" : "outside"); } static void check_sender_inside_costs_one_destination(void) { ft_tt_noc_coord members[4]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("the sender INSIDE the rectangle — a non-loopback multicast does not " "write to self, so the destination count is one lower"); members[0] = coord(3u, 7u); members[1] = coord(4u, 7u); members[2] = coord(5u, 7u); members[3] = coord(6u, 7u); rc = ft_tt_multicast_rectangle_from_cores(members, 4u, coord(5u, 7u), &rectangle); ft_tt_test_check_fmt("accepted, 4 nodes but 3 destinations", rc == FT_TT_OK && rectangle.node_count == 4u && rectangle.destination_count == 3u && rectangle.sender_inside, "rc=%d, nodes %u, destinations %u, sender %s", rc, rectangle.node_count, rectangle.destination_count, rectangle.sender_inside ? "inside" : "outside"); ft_tt_test_check("this is the number the write BARRIER counts acknowledgements against; " "one too many never returns and one too few returns early", rectangle.destination_count == 3u, "destination_count"); } static void check_a_wrapped_run_is_refused(void) { ft_tt_noc_coord members[4]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("a run of consecutive live indices that WRAPS a row is refused"); /* Exactly what ft_tt_coord_tensix_by_index() produces for indices 110..113 on a * 14-column card: the last two cores of one row and the first two of the next. Their * bounding box is 14 wide and 2 tall — 28 nodes, 24 of which nobody asked for. */ members[0] = coord(13u, 7u); members[1] = coord(14u, 7u); members[2] = coord(1u, 8u); members[3] = coord(2u, 8u); rc = ft_tt_multicast_rectangle_from_cores(members, 4u, coord(12u, 7u), &rectangle); ft_tt_test_check_fmt("REFUSED, because the set does not fill its bounding box", rc == FT_TT_ERROR, "rc=%d (%s) — a multicast writes EVERY node in " "the box, so accepting this would write into cores another group " "owns", rc, ft_tt_error_string(rc)); } static void check_a_duplicate_member_is_refused(void) { ft_tt_noc_coord members[4]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("a duplicated member is refused BEFORE the fill check"); /* Without the distinctness check this passes: four members, a 2x2 box, four nodes — * and one node of the box is not a member at all. The fill test is a COUNT, so a * duplicate is exactly what defeats it. */ members[0] = coord(3u, 7u); members[1] = coord(4u, 7u); members[2] = coord(3u, 8u); members[3] = coord(3u, 8u); rc = ft_tt_multicast_rectangle_from_cores(members, 4u, coord(2u, 7u), &rectangle); ft_tt_test_check_fmt("REFUSED", rc == FT_TT_EINVAL, "rc=%d (%s)", rc, ft_tt_error_string(rc)); } static void check_a_lone_sender_is_refused(void) { ft_tt_noc_coord members[1]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("a rectangle that degenerates onto the SENDER ALONE is refused"); members[0] = coord(5u, 7u); rc = ft_tt_multicast_rectangle_from_cores(members, 1u, coord(5u, 7u), &rectangle); ft_tt_test_check_fmt("REFUSED with zero destinations", rc == FT_TT_ERROR, "rc=%d (%s) — this is the zero-length NoC transfer in different " "clothes, the shape that took the card to `Mem- BusMaster-` on " "2026-08-12", rc, ft_tt_error_string(rc)); } static void check_an_out_of_range_coordinate_is_refused(void) { ft_tt_noc_coord members[2]; ft_tt_multicast_rectangle rectangle; int rc; ft_tt_test_step("a coordinate above the 6-bit node-id field is refused"); members[0] = coord(3u, 7u); members[1] = coord(64u, 7u); rc = ft_tt_multicast_rectangle_from_cores(members, 2u, coord(2u, 7u), &rectangle); ft_tt_test_check_fmt("REFUSED", rc == FT_TT_EINVAL, "rc=%d (%s) — 64 does not fail, it TRUNCATES to 0 and names a " "different live endpoint", rc, ft_tt_error_string(rc)); } static void check_inverted_corners_are_refused(void) { ft_tt_multicast_rectangle rectangle; int rc = ft_tt_multicast_rectangle_from_corners(6u, 7u, 3u, 7u, coord(2u, 7u), &rectangle); ft_tt_test_step("inverted corners are refused"); ft_tt_test_check_fmt("REFUSED", rc != FT_TT_OK, "rc=%d (%s)", rc, ft_tt_error_string(rc)); } /** * The encoding, against literal expected words. * * Field layout (as measured on this card) — * x_start << (36 + 12), y_start << (36 + 18), x_end << 36, y_end << (36 + 6), | addr * and the corner order — NoC 0 takes (low, high) as (start, end), NoC 1 takes * them reversed. * * The rectangle below is 3..6 x 7..9, deliberately NOT square, so the swap changes the * answer. A square rectangle would encode identically on both NoCs and the check would be * vacuous while looking thorough (DEV_NOTES.md §3). */ static void check_the_encoding_and_the_noc1_swap(void) { ft_tt_multicast_rectangle rectangle; uint64_t noc0 = 0u; uint64_t noc1 = 0u; uint64_t expected_noc0; uint64_t expected_noc1; int rc; ft_tt_test_step("the 64-bit encoding, and the NoC-1 corner swap"); rc = ft_tt_multicast_rectangle_from_corners(3u, 7u, 6u, 9u, coord(2u, 7u), &rectangle); if (rc != FT_TT_OK) { ft_tt_test_check("a 4x3 rectangle built", false, ft_tt_error_string(rc)); return; } ft_tt_test_check_fmt("12 nodes, 12 destinations", rectangle.node_count == 12u && rectangle.destination_count == 12u, "nodes %u, destinations %u", rectangle.node_count, rectangle.destination_count); /* NoC 0: start = (3, 7), end = (6, 9). */ expected_noc0 = ((uint64_t)3u << 48) | ((uint64_t)7u << 54) | ((uint64_t)6u << 36) | ((uint64_t)9u << 42) | (uint64_t)L1_ADDRESS; /* NoC 1: start = (6, 9), end = (3, 7). */ expected_noc1 = ((uint64_t)6u << 48) | ((uint64_t)9u << 54) | ((uint64_t)3u << 36) | ((uint64_t)7u << 42) | (uint64_t)L1_ADDRESS; rc = ft_tt_multicast_encode_address(&rectangle, 0u, L1_ADDRESS, &noc0); ft_tt_test_check_fmt("NoC 0 encodes the LOW corner as `start`", rc == FT_TT_OK && noc0 == expected_noc0, "got 0x%016llx, expected 0x%016llx", (unsigned long long)noc0, (unsigned long long)expected_noc0); rc = ft_tt_multicast_encode_address(&rectangle, 1u, L1_ADDRESS, &noc1); ft_tt_test_check_fmt("NoC 1 encodes them REVERSED", rc == FT_TT_OK && noc1 == expected_noc1, "got 0x%016llx, expected 0x%016llx", (unsigned long long)noc1, (unsigned long long)expected_noc1); ft_tt_test_check_fmt("and the two differ, so the swap is load-bearing on this shape", noc0 != noc1, "0x%016llx vs 0x%016llx", (unsigned long long)noc0, (unsigned long long)noc1); } static void check_payload_rules(void) { ft_tt_test_step("payload refusals"); ft_tt_test_check_fmt("a legal payload is accepted", ft_tt_multicast_check_payload(L1_ADDRESS, PAYLOAD_BYTES) == FT_TT_OK, "0x%08x + %u", L1_ADDRESS, PAYLOAD_BYTES); ft_tt_test_check("ZERO BYTES is refused", ft_tt_multicast_check_payload(L1_ADDRESS, 0u) == FT_TT_EINVAL, "the shape that took the card down on 2026-08-12"); ft_tt_test_check("a mis-aligned address is refused", ft_tt_multicast_check_payload(L1_ADDRESS + 8u, PAYLOAD_BYTES) == FT_TT_EINVAL, "L1 wants 16 bytes in address AND length"); ft_tt_test_check("a mis-aligned length is refused", ft_tt_multicast_check_payload(L1_ADDRESS, PAYLOAD_BYTES + 8u) == FT_TT_EINVAL, "16-byte length rule"); ft_tt_test_check("a payload that leaves L1 is refused", ft_tt_multicast_check_payload(1536u * 1024u - 16u, 2048u) == FT_TT_EINVAL, "1536 KiB is the whole of L1"); } /* ------------------------------------------------------------------------- */ static ft_tt_group_shared_operand_layout good_layout(void) { ft_tt_group_shared_operand_layout layout; layout.operand_address = L1_ADDRESS; layout.operand_bytes = PAYLOAD_BYTES; layout.ready_semaphore_address = 0x2040u; layout.arrival_semaphore_address = 0x2050u; layout.valid_word_address = 0x2060u; return layout; } static void check_shared_operand_binding(void) { ft_tt_noc_coord members[4]; ft_tt_group_shared_operand_layout layout = good_layout(); ft_tt_group_shared_operand operand; int rc; ft_tt_test_step("binding a shared operand onto a group"); members[0] = coord(3u, 7u); members[1] = coord(4u, 7u); members[2] = coord(5u, 7u); members[3] = coord(6u, 7u); /* The leader INSIDE the rectangle: the natural cooperating-matmul layout. Four cores, * one of them also reads DRAM, so three receive. */ rc = ft_tt_group_shared_operand_bind(members, 4u, members[0], &layout, &operand); ft_tt_test_check_fmt("a 4-core rectangle whose LEADER is one of them binds at 3 receivers", rc == FT_TT_OK && operand.receiver_count == 3u && operand.member_count == 4u && operand.rectangle.sender_inside, "rc=%d (%s), receivers %u, members %u", rc, ft_tt_error_string(rc), operand.receiver_count, operand.member_count); /* The leader OUTSIDE it: a dedicated feeder. Four cores receive and the group is five. */ rc = ft_tt_group_shared_operand_bind(members, 4u, coord(2u, 7u), &layout, &operand); ft_tt_test_check_fmt("a leader OUTSIDE the rectangle binds at 4 receivers and 5 members", rc == FT_TT_OK && operand.receiver_count == 4u && operand.member_count == 5u && !operand.rectangle.sender_inside, "rc=%d (%s), receivers %u, members %u", rc, ft_tt_error_string(rc), operand.receiver_count, operand.member_count); rc = ft_tt_group_shared_operand_bind(members, 1u, members[0], &layout, &operand); ft_tt_test_check_fmt("ONE receiver that IS the leader is refused", rc == FT_TT_ERROR, "rc=%d (%s) — that rectangle has zero destinations", rc, ft_tt_error_string(rc)); rc = ft_tt_group_shared_operand_bind(members, 0u, members[0], &layout, &operand); ft_tt_test_check_fmt("ZERO receivers is refused", rc == FT_TT_EINVAL, "rc=%d (%s)", rc, ft_tt_error_string(rc)); } static void check_shared_operand_layout_refusals(void) { ft_tt_noc_coord members[2]; ft_tt_group_shared_operand operand; ft_tt_group_shared_operand_layout layout; int rc; ft_tt_test_step("layouts that cannot work are refused"); members[0] = coord(3u, 7u); members[1] = coord(4u, 7u); /* The arrival semaphore inside the operand region. The leader multicasts into BOTH in * the same round, so one silently destroys the other — and the symptom is a receiver * that reads the previous round's bytes, not an error. */ layout = good_layout(); layout.arrival_semaphore_address = L1_ADDRESS + 64u; rc = ft_tt_group_shared_operand_bind(members, 2u, coord(2u, 7u), &layout, &operand); ft_tt_test_check_fmt("an arrival semaphore INSIDE the operand region is refused", rc == FT_TT_ERROR, "rc=%d (%s)", rc, ft_tt_error_string(rc)); layout = good_layout(); layout.valid_word_address = layout.ready_semaphore_address; rc = ft_tt_group_shared_operand_bind(members, 2u, coord(2u, 7u), &layout, &operand); ft_tt_test_check_fmt("two control words at the same address are refused", rc == FT_TT_ERROR, "rc=%d (%s) — on the LEADER all three live in " "one L1, so an alias is a deadlock", rc, ft_tt_error_string(rc)); layout = good_layout(); layout.ready_semaphore_address += 2u; rc = ft_tt_group_shared_operand_bind(members, 2u, coord(2u, 7u), &layout, &operand); ft_tt_test_check_fmt("a semaphore that is not 4-byte aligned is refused", rc == FT_TT_EINVAL, "rc=%d (%s)", rc, ft_tt_error_string(rc)); layout = good_layout(); layout.operand_bytes = 0u; rc = ft_tt_group_shared_operand_bind(members, 2u, coord(2u, 7u), &layout, &operand); ft_tt_test_check_fmt("a ZERO-BYTE operand is refused", rc == FT_TT_EINVAL, "rc=%d (%s)", rc, ft_tt_error_string(rc)); } /* ------------------------------------------------------------------------- */ int main(void) { check_rectangle_from_a_row(); check_sender_inside_costs_one_destination(); check_a_wrapped_run_is_refused(); check_a_duplicate_member_is_refused(); check_a_lone_sender_is_refused(); check_an_out_of_range_coordinate_is_refused(); check_inverted_corners_are_refused(); check_the_encoding_and_the_noc1_swap(); check_payload_rules(); check_shared_operand_binding(); check_shared_operand_layout_refusals(); return ft_tt_test_finish(); }