/* 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 #include #include #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; }