Foundry/tt/ft_tt_op_matmul_resident_mu...

173 lines
7.9 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file ft_tt_op_matmul_resident_multicast.c
* @brief Implementation of ft_tt_op_matmul_resident_multicast.h — see that header for why
* each refusal exists and why the rendezvous words are the one-shot kernel's.
*
* @status Off-hardware except for the three reset writes.
* @issues None
* @todo None
*/
#include "ft_tt_op_matmul_resident_multicast.h"
#include "ft_tt_device.h"
#include "ft_tt_op_matmul_cooperative.h"
int ft_tt_op_matmul_resident_multicast_plan(const ft_tt_noc_coord *members,
uint32_t member_count, uint32_t leader,
uint32_t spin_limit,
ft_tt_op_matmul_resident_multicast *out)
{
ft_tt_op_matmul_resident_multicast plan;
uint32_t index;
int rc;
if (members == NULL || out == NULL)
return FT_TT_EINVAL;
if (member_count < 2u || member_count > FT_TT_RESIDENT_MULTICAST_MAX_MEMBERS) {
ft_tt_log_error("resident multicast: %u members is outside 2..%u", member_count,
FT_TT_RESIDENT_MULTICAST_MAX_MEMBERS);
return FT_TT_EINVAL;
}
if (leader >= member_count) {
ft_tt_log_error("resident multicast: leader %u of %u members", leader, member_count);
return FT_TT_EINVAL;
}
/* 0 would have to mean "spin forever", and a resident kernel with an unbounded wait is
* the one thing this backend may not be configured into: `assert_reset` is then the
* only way to end it, and asserting reset on a blocked triad is how cores were
* wedged. */
if (spin_limit == 0u) {
ft_tt_log_error("resident multicast: a zero rendezvous spin limit is unbounded");
return FT_TT_EINVAL;
}
for (index = 0u; index < member_count; index++)
plan.member[index] = members[index];
for (; index < FT_TT_RESIDENT_MULTICAST_MAX_MEMBERS; index++) {
plan.member[index].x = 0u;
plan.member[index].y = 0u;
}
plan.member_count = member_count;
plan.leader = leader;
plan.receiver_count = member_count - 1u;
plan.spin_limit = spin_limit;
plan.ready_semaphore_address = ft_tt_op_coop_ready_semaphore_address();
plan.arrival_semaphore_address = ft_tt_op_coop_arrival_semaphore_address();
plan.counter_word_address = ft_tt_op_coop_valid_word_address();
rc = ft_tt_group_broadcast_partition_build(members, member_count, members[leader],
FT_TT_GROUP_BROADCAST_HAZARD_LIMIT,
&plan.partition);
if (rc != FT_TT_OK)
return rc;
/* The partition already proves this, and it is checked again HERE because the kernel
* reads the two numbers from different runtime args and compares them itself: a plan
* whose receiver count and destination total disagree would make the leader's write
* barrier either hang or return early, and neither is visible as a wrong answer. */
if (plan.partition.destination_total != plan.receiver_count) {
ft_tt_log_error("resident multicast: %u destinations for %u receivers",
plan.partition.destination_total, plan.receiver_count);
return FT_TT_ERROR;
}
*out = plan;
return FT_TT_OK;
}
int ft_tt_op_matmul_resident_multicast_args(const ft_tt_op_matmul_resident_multicast *plan,
uint32_t index, uint32_t out_args[kArgCoopCount])
{
uint32_t group;
if (plan == NULL || out_args == NULL || index >= plan->member_count)
return FT_TT_EINVAL;
out_args[kArgCoopRole] = index == plan->leader ? FT_TT_RESIDENT_COOP_ROLE_LEADER
: FT_TT_RESIDENT_COOP_ROLE_RECEIVER;
out_args[kArgCoopLeaderX] = plan->member[plan->leader].x;
out_args[kArgCoopLeaderY] = plan->member[plan->leader].y;
out_args[kArgCoopReadySemaphore] = plan->ready_semaphore_address;
out_args[kArgCoopArrivalSemaphore] = plan->arrival_semaphore_address;
out_args[kArgCoopCounterWord] = plan->counter_word_address;
out_args[kArgCoopReceiverCount] = plan->receiver_count;
out_args[kArgCoopSpinLimit] = plan->spin_limit;
/* THE GEOMETRY IS THE LEADER'S ALONE. A receiver given a rectangle it does not use is
* a receiver one bug away from broadcasting, so it is given zeroes — which the kernel
* refuses to issue rather than aiming at (0,0). */
if (index != plan->leader) {
out_args[kArgCoopGroupCount] = 0u;
for (group = 0u; group < FT_TT_RESIDENT_COOP_MAX_GROUPS; group++)
out_args[kArgCoopGroup0 + group] = 0u;
return FT_TT_OK;
}
if (plan->partition.rectangle_count > FT_TT_RESIDENT_COOP_MAX_GROUPS) {
ft_tt_log_error("resident multicast: %u pieces will not fit the %u arg words the "
"kernel reads", plan->partition.rectangle_count,
FT_TT_RESIDENT_COOP_MAX_GROUPS);
return FT_TT_EINVAL;
}
out_args[kArgCoopGroupCount] = plan->partition.rectangle_count;
for (group = 0u; group < FT_TT_RESIDENT_COOP_MAX_GROUPS; group++) {
const ft_tt_multicast_rectangle *piece = &plan->partition.rectangle[group];
uint32_t packed = 0u;
if (group < plan->partition.rectangle_count) {
packed = ftcoop_pack_rectangle(piece->x_low, piece->y_low, piece->x_high,
piece->y_high, piece->destination_count);
/* ftcoop_pack_rectangle() returns 0 — never a legal piece — for anything out
* of range, so this catches a coordinate above the 6-bit node-id field before
* it can TRUNCATE onto a different live endpoint on the card. */
if (packed == 0u) {
ft_tt_log_error("resident multicast: piece %u (%u,%u)..(%u,%u) x%u does not "
"fit the packed rectangle word", group, piece->x_low,
piece->y_low, piece->x_high, piece->y_high,
piece->destination_count);
return FT_TT_EINVAL;
}
}
out_args[kArgCoopGroup0 + group] = packed;
}
return FT_TT_OK;
}
int ft_tt_op_matmul_resident_multicast_reset(const ft_tt_noc_access *noc,
const ft_tt_op_matmul_resident_multicast *plan)
{
const uint32_t zero = 0u;
uint32_t index;
int rc;
if (noc == NULL || noc->write == NULL || plan == NULL)
return FT_TT_EINVAL;
if (plan->member_count < 2u || plan->leader >= plan->member_count)
return FT_TT_EINVAL;
for (index = 0u; index < plan->member_count; index++) {
rc = noc->write(noc->user, (uint16_t)plan->member[index].x,
(uint16_t)plan->member[index].y, plan->arrival_semaphore_address,
&zero, sizeof(zero));
if (rc != FT_TT_OK) {
ft_tt_log_error("resident multicast: clearing the arrival word on member %u at "
"(%u,%u) failed: rc=%d (%s)", index, plan->member[index].x,
plan->member[index].y, rc, ft_tt_error_string(rc));
return rc;
}
}
rc = noc->write(noc->user, (uint16_t)plan->member[plan->leader].x,
(uint16_t)plan->member[plan->leader].y, plan->counter_word_address, &zero,
sizeof(zero));
if (rc == FT_TT_OK)
rc = noc->write(noc->user, (uint16_t)plan->member[plan->leader].x,
(uint16_t)plan->member[plan->leader].y, plan->ready_semaphore_address,
&zero, sizeof(zero));
if (rc != FT_TT_OK)
ft_tt_log_error("resident multicast: staging the leader's control words at (%u,%u) "
"failed: rc=%d (%s)", plan->member[plan->leader].x,
plan->member[plan->leader].y, rc, ft_tt_error_string(rc));
return rc;
}