214 lines
9.1 KiB
C
214 lines
9.1 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/**
|
|
* test_split_broadcast_geometry.c — the split-broadcast refusals, OFF HARDWARE.
|
|
*
|
|
* These are the checks that keep experiment B (test_multicast_bench.c stage 3d) safe to
|
|
* issue: two senders broadcasting at once into two rectangles, on a card where a wedge
|
|
* costs a whole-card reset plus a link retrain. Two of the refusals protect the card
|
|
* directly and both are easy to violate by picking the wrong index:
|
|
*
|
|
* - a SENDER INSIDE A RECTANGLE. A multicast writes every node in its bounding box, so a
|
|
* sender caught in one has its own staging buffer overwritten mid-round. The failure
|
|
* would show up as a corrupt payload and be blamed on the split.
|
|
* - TWO RECTANGLES THAT OVERLAP. A receiver written twice per round by two senders with
|
|
* no ordering between them.
|
|
*
|
|
* Every check below is written so that deleting the guard it tests makes it go red, and
|
|
* the healthy case is checked first — a gate that refuses everything passes every red.
|
|
*
|
|
* Build and run:
|
|
* make -f Makefile.multicast check
|
|
*/
|
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include "ft_tt_split_broadcast_geometry.h"
|
|
#include "ft_tt_test_report.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_DESTINATIONS 4u
|
|
|
|
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;
|
|
}
|
|
|
|
/** Two rows of four, at y = 5 and y = 6, x = 3..6. The shape experiment B runs. */
|
|
static void fill_two_rows_of_four(ft_tt_noc_coord *receivers)
|
|
{
|
|
uint32_t row;
|
|
uint32_t column;
|
|
|
|
for (row = 0u; row < 2u; row++)
|
|
for (column = 0u; column < 4u; column++)
|
|
receivers[row * 4u + column] = coord(3u + column, 5u + row);
|
|
}
|
|
|
|
static void check_build(const char *what, const ft_tt_noc_coord *receivers, uint32_t rows,
|
|
uint32_t columns, const ft_tt_noc_coord *senders,
|
|
uint32_t sender_count, int wanted_rc)
|
|
{
|
|
ft_tt_split_broadcast_plan plan;
|
|
char why[192];
|
|
int rc = ft_tt_split_broadcast_plan_build(receivers, rows, columns, senders, sender_count,
|
|
MAX_DESTINATIONS, &plan, why, sizeof(why));
|
|
|
|
ft_tt_test_check_fmt(what, rc == wanted_rc, "rc=%d (%s), wanted %d (%s) — %s", rc,
|
|
ft_tt_error_string(rc), wanted_rc, ft_tt_error_string(wanted_rc),
|
|
why);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void check_the_shape_experiment_b_runs_is_accepted(void)
|
|
{
|
|
ft_tt_noc_coord receivers[8];
|
|
ft_tt_noc_coord senders[2];
|
|
ft_tt_split_broadcast_plan plan;
|
|
char why[192];
|
|
int rc;
|
|
|
|
ft_tt_test_step("two rows of four with both senders outside — the shape experiment B "
|
|
"runs, and the reason every refusal below means something");
|
|
|
|
fill_two_rows_of_four(receivers);
|
|
senders[0] = coord(3u, 9u);
|
|
senders[1] = coord(4u, 9u);
|
|
rc = ft_tt_split_broadcast_plan_build(receivers, 2u, 4u, senders, 2u, MAX_DESTINATIONS,
|
|
&plan, why, sizeof(why));
|
|
ft_tt_test_check_fmt("accepted", rc == FT_TT_OK, "rc=%d (%s) — %s", rc,
|
|
ft_tt_error_string(rc), why);
|
|
if (rc != FT_TT_OK)
|
|
return;
|
|
ft_tt_test_check_fmt("two groups, four destinations each, NEITHER over the limit",
|
|
plan.group_count == 2u &&
|
|
plan.rectangle[0].destination_count == 4u &&
|
|
plan.rectangle[1].destination_count == 4u,
|
|
"%u groups, destinations %u and %u", plan.group_count,
|
|
plan.rectangle[0].destination_count,
|
|
plan.rectangle[1].destination_count);
|
|
ft_tt_test_check_fmt("the rows became two rectangles one row apart",
|
|
plan.rectangle[0].y_low == 5u && plan.rectangle[0].y_high == 5u &&
|
|
plan.rectangle[1].y_low == 6u && plan.rectangle[1].y_high == 6u &&
|
|
plan.rectangle[0].x_low == 3u && plan.rectangle[0].x_high == 6u,
|
|
"(%u,%u)..(%u,%u) and (%u,%u)..(%u,%u)", plan.rectangle[0].x_low,
|
|
plan.rectangle[0].y_low, plan.rectangle[0].x_high,
|
|
plan.rectangle[0].y_high, plan.rectangle[1].x_low,
|
|
plan.rectangle[1].y_low, plan.rectangle[1].x_high,
|
|
plan.rectangle[1].y_high);
|
|
ft_tt_test_check("and neither sender is inside either rectangle",
|
|
!plan.rectangle[0].sender_inside && !plan.rectangle[1].sender_inside,
|
|
"sender_inside is false on both");
|
|
ft_tt_test_check("the two rectangles are disjoint",
|
|
!ft_tt_split_broadcast_rectangles_overlap(&plan.rectangle[0],
|
|
&plan.rectangle[1]),
|
|
"row 5 against row 6");
|
|
}
|
|
|
|
/** The two refusals that protect the card. */
|
|
static void check_a_broadcast_that_would_reach_the_wrong_core_is_refused(void)
|
|
{
|
|
ft_tt_noc_coord receivers[8];
|
|
ft_tt_noc_coord senders[2];
|
|
|
|
ft_tt_test_step("a plan whose broadcast would reach a core it does not own is refused");
|
|
|
|
/* Sender 1 sitting in row 0: legal as a coordinate, and it would be written by group
|
|
* 0's broadcast. This is the mistake a wrong FT_TT_MULTICAST_SENDER_B_INDEX makes. */
|
|
fill_two_rows_of_four(receivers);
|
|
senders[0] = coord(3u, 9u);
|
|
senders[1] = coord(5u, 5u);
|
|
check_build("a sender INSIDE the other group's rectangle is refused", receivers, 2u, 4u,
|
|
senders, 2u, FT_TT_ERROR);
|
|
|
|
/* A sender inside its OWN rectangle. Legal for a multicast in general — it just does
|
|
* not receive — but here it means one of the eight receivers is also the feeder, which
|
|
* is a different experiment. */
|
|
senders[0] = coord(4u, 5u);
|
|
senders[1] = coord(4u, 9u);
|
|
check_build("a sender inside its OWN rectangle is refused too", receivers, 2u, 4u,
|
|
senders, 2u, FT_TT_ERROR);
|
|
|
|
/* Both groups handed the SAME row: the rectangles are identical, so every receiver is
|
|
* written twice per round by two senders with no ordering between them. */
|
|
{
|
|
ft_tt_noc_coord doubled[8];
|
|
uint32_t index;
|
|
|
|
fill_two_rows_of_four(doubled);
|
|
for (index = 0u; index < 4u; index++)
|
|
doubled[4u + index] = doubled[index];
|
|
senders[0] = coord(3u, 9u);
|
|
senders[1] = coord(4u, 9u);
|
|
check_build("two groups over the SAME row are refused as overlapping", doubled, 2u,
|
|
4u, senders, 2u, FT_TT_ERROR);
|
|
}
|
|
}
|
|
|
|
/** The limit itself, and the argument checks. */
|
|
static void check_the_destination_limit_and_the_arguments(void)
|
|
{
|
|
ft_tt_noc_coord receivers[10];
|
|
ft_tt_noc_coord senders[2];
|
|
uint32_t row;
|
|
uint32_t column;
|
|
|
|
ft_tt_test_step("the per-packet limit and the argument checks");
|
|
|
|
for (row = 0u; row < 2u; row++)
|
|
for (column = 0u; column < 5u; column++)
|
|
receivers[row * 5u + column] = coord(3u + column, 5u + row);
|
|
senders[0] = coord(3u, 9u);
|
|
senders[1] = coord(4u, 9u);
|
|
check_build("a FIVE-wide group is refused — this is the standing rule, in code",
|
|
receivers, 2u, 5u, senders, 2u, FT_TT_ERROR);
|
|
|
|
check_build("and the same rows at four wide are accepted, so the refusal above is "
|
|
"about the WIDTH and not the rows", receivers, 1u, 4u, senders, 1u,
|
|
FT_TT_OK);
|
|
|
|
check_build("one sender for two groups is refused", receivers, 2u, 4u, senders, 1u,
|
|
FT_TT_EINVAL);
|
|
check_build("zero groups is refused", receivers, 0u, 4u, senders, 0u, FT_TT_EINVAL);
|
|
check_build("zero receivers per group is refused", receivers, 2u, 0u, senders, 2u,
|
|
FT_TT_EINVAL);
|
|
check_build("more groups than the plan holds is refused", receivers,
|
|
FT_TT_SPLIT_MAX_GROUPS + 1u, 1u, senders, FT_TT_SPLIT_MAX_GROUPS + 1u,
|
|
FT_TT_EINVAL);
|
|
check_build("a NULL receiver array is refused", NULL, 2u, 4u, senders, 2u, FT_TT_EINVAL);
|
|
|
|
/* A row that is not contiguous does not FILL its bounding box, so the box contains a
|
|
* core nobody asked for. The multicast module is what catches it; this proves the plan
|
|
* asks it rather than doing its own arithmetic. */
|
|
{
|
|
ft_tt_noc_coord gapped[4];
|
|
|
|
gapped[0] = coord(3u, 5u);
|
|
gapped[1] = coord(4u, 5u);
|
|
gapped[2] = coord(5u, 5u);
|
|
gapped[3] = coord(7u, 5u);
|
|
check_build("a row with a GAP in it is refused: its bounding box holds a core the "
|
|
"group does not own", gapped, 1u, 4u, senders, 1u, FT_TT_ERROR);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int main(void)
|
|
{
|
|
ft_tt_test_step("the split-broadcast geometry — off hardware");
|
|
|
|
check_the_shape_experiment_b_runs_is_accepted();
|
|
check_a_broadcast_that_would_reach_the_wrong_core_is_refused();
|
|
check_the_destination_limit_and_the_arguments();
|
|
|
|
return ft_tt_test_finish();
|
|
}
|