Foundry/tt/ft_tt_group_broadcast_parti...

185 lines
7.7 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file ft_tt_group_broadcast_partition.c
* @brief Implementation of ft_tt_group_broadcast_partition.h — see that header for why
* the partition is by row and why the four-destination limit is a refusal rather than
* a comment. Nothing here touches the device.
*
* @status Off-hardware, driven red by tests/test_group_broadcast_partition.c.
* @issues None
* @todo None
*/
#include "ft_tt_group_broadcast_partition.h"
#include "ft_tt_device.h"
/** Emit one piece covering `[x_first, x_last]` of row `y`, and refuse anything the
* rectangle arithmetic itself would refuse.
*
* `leader` is passed to ft_tt_multicast_rectangle_from_corners as the SENDER so that
* `sender_inside` is computed rather than assumed — a piece that somehow contained the
* leader would come back with `destination_count == node_count - 1` and be caught by the
* check below instead of silently under-counting the barrier. */
static int append_row_run(ft_tt_group_broadcast_partition *partition, uint32_t y,
uint32_t x_first, uint32_t x_last, ft_tt_noc_coord leader)
{
ft_tt_multicast_rectangle rectangle;
int rc;
if (partition->rectangle_count >= FT_TT_GROUP_BROADCAST_MAX_RECTANGLES) {
ft_tt_log_error("broadcast partition: needs more than %u pieces",
FT_TT_GROUP_BROADCAST_MAX_RECTANGLES);
return FT_TT_ERROR;
}
rc = ft_tt_multicast_rectangle_from_corners(x_first, y, x_last, y, leader, &rectangle);
if (rc != FT_TT_OK)
return rc;
if (rectangle.sender_inside) {
ft_tt_log_error("broadcast partition: piece (%u,%u)..(%u,%u) contains the leader "
"(%u,%u); the row split was supposed to remove it",
x_first, y, x_last, y, leader.x, leader.y);
return FT_TT_ERROR;
}
partition->rectangle[partition->rectangle_count] = rectangle;
partition->rectangle_count++;
partition->destination_total += rectangle.destination_count;
return FT_TT_OK;
}
/** Split `[x_first, x_last]` into chunks of at most `max_destinations` and emit each. */
static int append_row_chunks(ft_tt_group_broadcast_partition *partition, uint32_t y,
uint32_t x_first, uint32_t x_last, uint32_t max_destinations,
ft_tt_noc_coord leader)
{
uint32_t x = x_first;
while (x <= x_last) {
const uint32_t remaining = x_last - x + 1u;
const uint32_t chunk = remaining < max_destinations ? remaining : max_destinations;
const int rc = append_row_run(partition, y, x, x + chunk - 1u, leader);
if (rc != FT_TT_OK)
return rc;
x += chunk;
}
return FT_TT_OK;
}
/** One row of the group's bounding box, with the leader's cell removed. A row that does
* not hold the leader is one run; the leader's row is the run left of it, the run right
* of it, or both, or neither when the group is one column wide. */
static int append_row(ft_tt_group_broadcast_partition *partition,
const ft_tt_multicast_rectangle *group, uint32_t y,
uint32_t max_destinations, ft_tt_noc_coord leader)
{
int rc;
if (y != leader.y)
return append_row_chunks(partition, y, group->x_low, group->x_high, max_destinations,
leader);
if (leader.x > group->x_low) {
rc = append_row_chunks(partition, y, group->x_low, leader.x - 1u, max_destinations,
leader);
if (rc != FT_TT_OK)
return rc;
}
if (leader.x < group->x_high)
return append_row_chunks(partition, y, leader.x + 1u, group->x_high, max_destinations,
leader);
return FT_TT_OK;
}
/** Whether `coord` is one of `members`. The leader has to be a member: a sender outside
* the group is a legal multicast shape and a DIFFERENT one, whose barrier and column
* split this module does not describe. */
static bool contains_coordinate(const ft_tt_noc_coord *members, uint32_t member_count,
ft_tt_noc_coord coord)
{
uint32_t index;
for (index = 0u; index < member_count; index++)
if (members[index].x == coord.x && members[index].y == coord.y)
return true;
return false;
}
bool ft_tt_group_broadcast_partition_covers(const ft_tt_group_broadcast_partition *partition,
const ft_tt_noc_coord *members,
uint32_t member_count, ft_tt_noc_coord leader)
{
uint32_t member;
if (partition == NULL || members == NULL)
return false;
for (member = 0u; member < member_count; member++) {
const bool is_leader =
members[member].x == leader.x && members[member].y == leader.y;
uint32_t hits = 0u;
uint32_t piece;
for (piece = 0u; piece < partition->rectangle_count; piece++)
if (ft_tt_multicast_rectangle_contains(&partition->rectangle[piece], members[member]))
hits++;
if (hits != (is_leader ? 0u : 1u))
return false;
}
return true;
}
int ft_tt_group_broadcast_partition_build(const ft_tt_noc_coord *members, uint32_t member_count,
ft_tt_noc_coord leader, uint32_t max_destinations,
ft_tt_group_broadcast_partition *out)
{
ft_tt_multicast_rectangle group;
uint32_t y;
int rc;
if (members == NULL || out == NULL)
return FT_TT_EINVAL;
if (member_count < 2u || member_count > FT_TT_GROUP_BROADCAST_MAX_MEMBERS) {
ft_tt_log_error("broadcast partition: %u members is outside 2..%u; one member has "
"no receivers, which is a zero-destination multicast",
member_count, FT_TT_GROUP_BROADCAST_MAX_MEMBERS);
return FT_TT_EINVAL;
}
if (max_destinations == 0u || max_destinations > FT_TT_GROUP_BROADCAST_HAZARD_LIMIT) {
ft_tt_log_error("broadcast partition: %u destinations per packet is outside 1..%u; "
"five wedges a core at its next firmware boot",
max_destinations, FT_TT_GROUP_BROADCAST_HAZARD_LIMIT);
return FT_TT_EINVAL;
}
/* The pigeonhole fill test AND the duplicate check, both of which this partition
* depends on: a row of the bounding box is only a set of members if the members fill
* the box. The leader is the sender, so `group.destination_count` is already
* member_count - 1 and is checked against the partition's own total below. */
rc = ft_tt_multicast_rectangle_from_cores(members, member_count, leader, &group);
if (rc != FT_TT_OK)
return rc;
if (!contains_coordinate(members, member_count, leader)) {
ft_tt_log_error("broadcast partition: leader (%u,%u) is not one of the %u members",
leader.x, leader.y, member_count);
return FT_TT_ERROR;
}
out->rectangle_count = 0u;
out->destination_total = 0u;
for (y = group.y_low; y <= group.y_high; y++) {
rc = append_row(out, &group, y, max_destinations, leader);
if (rc != FT_TT_OK)
return rc;
}
if (out->destination_total != member_count - 1u) {
ft_tt_log_error("broadcast partition: %u destinations cover %u receivers",
out->destination_total, member_count - 1u);
return FT_TT_ERROR;
}
if (!ft_tt_group_broadcast_partition_covers(out, members, member_count, leader)) {
ft_tt_log_error("broadcast partition: the pieces do not cover every receiver "
"exactly once");
return FT_TT_ERROR;
}
return FT_TT_OK;
}