Foundry/tt/ft_tt_op_replicas.c

141 lines
6.0 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file ft_tt_op_replicas.c
* @brief Implementation of the resident replica table's host half.
*
* @status Written 2026-08-19.
* @issues None
* @todo None
*/
#include "ft_tt_op_replicas.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "ft_tt_device.h"
#include "ft_tt_mailbox.h"
#include "ft_tt_op_l1_layout.h"
/* The table lives in the kernel-config region's own free gap. These are the two facts that
* make that true, and they are asserted rather than described because the L1 map is edited
* for other reasons and nothing else would notice. */
FT_TT_STATIC_ASSERT(FT_TT_REPLICA_TABLE_ADDRESS >=
FT_TT_OP_RTA_BASE + 5u * FT_TT_OP_RTA_STRIDE,
replica_table_is_past_the_runtime_arg_blocks);
FT_TT_STATIC_ASSERT(FT_TT_REPLICA_TABLE_ADDRESS + FT_TT_REPLICA_TABLE_BYTES <=
FT_TT_OP_KERNEL_CONFIG_BASE + FT_TT_OP_TEXT_OFFSET_DM0,
replica_table_is_below_the_first_kernel_text_slab);
FT_TT_STATIC_ASSERT(FT_TT_REPLICA_TABLE_WORDS * 4u <= FT_TT_REPLICA_TABLE_BYTES,
replica_table_words_fit_the_reservation);
/** Two destinations are the same PLACE, which is the one thing a replica must never be. */
static bool replica_destination_equal(const ft_tt_op_replica_destination *a,
const ft_tt_op_replica_destination *b)
{
return a->x == b->x && a->y == b->y && a->base == b->base;
}
int ft_tt_op_replica_table_encode(const ft_tt_op_replica_table *table,
uint32_t out_words[FT_TT_REPLICA_TABLE_WORDS])
{
uint32_t set;
if (!table || !out_words)
return FT_TT_EINVAL;
if (table->set_count > FT_TT_REPLICA_MAX_SETS) {
fprintf(stderr, "ft_tt: %u replica sets is more than the %u the wire format holds\n",
table->set_count, FT_TT_REPLICA_MAX_SETS);
return FT_TT_EINVAL;
}
memset(out_words, 0, FT_TT_REPLICA_TABLE_WORDS * sizeof(out_words[0]));
out_words[0] = FT_TT_REPLICA_TABLE_MAGIC;
out_words[1] = table->set_count;
for (set = 0u; set < table->set_count; set++) {
const ft_tt_op_replica_set *entry = &table->set[set];
const uint32_t record = ftr_record_offset(set);
uint32_t index, other;
if (entry->count < 2u || entry->count > FT_TT_REPLICA_MAX_DESTINATIONS) {
fprintf(stderr, "ft_tt: replica set %u has %u destinations; 2..%u is the range "
"(one destination is not a replica, it is the strip)\n",
set, entry->count, FT_TT_REPLICA_MAX_DESTINATIONS);
return FT_TT_EINVAL;
}
/* A DUPLICATE DESTINATION READS AS A WORKING MAP AND DELIVERS ONE BANK. It is the
* exact failure a bank-cursor bug produces, and nothing downstream can see it: the
* bytes are written twice, both readers get the right answer, and the bandwidth
* the whole mechanism exists for is not there. */
for (index = 0u; index < entry->count; index++)
for (other = index + 1u; other < entry->count; other++)
if (replica_destination_equal(&entry->destination[index],
&entry->destination[other])) {
fprintf(stderr, "ft_tt: replica set %u names (%u,%u)+0x%x twice — that "
"is one bank wearing two names\n",
set, entry->destination[index].x, entry->destination[index].y,
entry->destination[index].base);
return FT_TT_EINVAL;
}
/* Two sets with the same PRIMARY: the writer takes the first hit, so the second is
* silently dead. Caught here because "the map has a dead entry" is invisible. */
for (other = 0u; other < set; other++)
if (replica_destination_equal(&entry->destination[0],
&table->set[other].destination[0])) {
fprintf(stderr, "ft_tt: replica sets %u and %u have the same primary — the "
"second could never be found\n", other, set);
return FT_TT_EINVAL;
}
out_words[record] = entry->count;
for (index = 0u; index < entry->count; index++) {
out_words[record + 1u + index * 3u] = entry->destination[index].x;
out_words[record + 2u + index * 3u] = entry->destination[index].y;
out_words[record + 3u + index * 3u] = entry->destination[index].base;
}
}
return FT_TT_OK;
}
int ft_tt_op_replica_table_write(const ft_tt_noc_access *noc, uint16_t x, uint16_t y,
const ft_tt_op_replica_table *table)
{
uint32_t words[FT_TT_REPLICA_TABLE_WORDS];
int rc;
if (!noc || !noc->write)
return FT_TT_EINVAL;
rc = ft_tt_op_replica_table_encode(table, words);
if (rc != FT_TT_OK)
return rc;
return noc->write(noc->user, x, y, FT_TT_REPLICA_TABLE_ADDRESS, words, sizeof(words));
}
int ft_tt_op_replica_table_verify(const ft_tt_noc_access *noc, uint16_t x, uint16_t y,
const ft_tt_op_replica_table *table)
{
uint32_t want[FT_TT_REPLICA_TABLE_WORDS];
uint32_t got[FT_TT_REPLICA_TABLE_WORDS];
uint32_t index;
int rc;
if (!noc || !noc->read)
return FT_TT_EINVAL;
rc = ft_tt_op_replica_table_encode(table, want);
if (rc != FT_TT_OK)
return rc;
rc = noc->read(noc->user, x, y, FT_TT_REPLICA_TABLE_ADDRESS, got, sizeof(got));
if (rc != FT_TT_OK)
return rc;
for (index = 0u; index < FT_TT_REPLICA_TABLE_WORDS; index++)
if (got[index] != want[index]) {
fprintf(stderr, "ft_tt: the replica table on (%u,%u) differs at word %u: "
"0x%08x, wanted 0x%08x\n", x, y, index, got[index], want[index]);
return FT_TT_ERROR;
}
return FT_TT_OK;
}