486 lines
21 KiB
C
486 lines
21 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/**
|
|
* @file test_udrv_tlb_encode.c
|
|
* @brief Gate for ft_tt_udrv_tlb_encode.c — the CONFIGURE_TLB register words.
|
|
*
|
|
* The primary gate is 96 reference vectors whose expected words are computed
|
|
* bit by bit from the public field table (tests/reference/
|
|
* generate_tlb2m_reference.c). Nothing in this file transcribes the bit
|
|
* layout, and the generator does not share the encoder's method.
|
|
*
|
|
* The register OFFSETS are pinned literally, on purpose. `0x1FC009D8` for
|
|
* window 0's strided register is 210 * 12 past the register file base — the
|
|
* TOTAL window count including the eight 4 GiB windows this board does not
|
|
* have. Sizing it from 202 gives 0x1FC00978 and sizing it from 214 gives
|
|
* 0x1FC00A08; both look right and both would zero the wrong register.
|
|
*
|
|
* @status Off-hardware. 100% of what it covers is arithmetic; nothing here
|
|
* touches a device.
|
|
* @issues None
|
|
* @todo None
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "ft_tt_test_report.h"
|
|
#include "ft_tt_udrv.h"
|
|
#include "ft_tt_udrv_tlb_encode.h"
|
|
#include "reference/ft_tt_tlb2m_reference_vectors.h"
|
|
|
|
/** Turn a reference vector's fields into the encoder's config struct. */
|
|
static void config_from_vector(const ft_tt_tlb2m_reference_vector *vector,
|
|
ft_tt_udrv_tlb2m_config *out_config)
|
|
{
|
|
memset(out_config, 0, sizeof(*out_config));
|
|
out_config->addr = vector->addr;
|
|
out_config->x_end = vector->x_end;
|
|
out_config->y_end = vector->y_end;
|
|
out_config->x_start = vector->x_start;
|
|
out_config->y_start = vector->y_start;
|
|
out_config->noc = vector->noc;
|
|
out_config->multicast = vector->multicast;
|
|
out_config->ordering = vector->ordering;
|
|
out_config->linked = vector->linked;
|
|
out_config->use_static_vc = vector->use_static_vc;
|
|
out_config->stream_header = vector->stream_header;
|
|
out_config->static_vc_id = vector->static_vc;
|
|
}
|
|
|
|
static void stage_reference_vectors(void)
|
|
{
|
|
size_t index;
|
|
|
|
ft_tt_test_step("Stage 1 — %d reference vectors from the public field table",
|
|
FT_TT_TLB2M_REF_VECTOR_COUNT);
|
|
|
|
for (index = 0u; index < FT_TT_TLB2M_REF_VECTOR_COUNT; index++) {
|
|
const ft_tt_tlb2m_reference_vector *vector = &ft_tt_tlb2m_reference_vectors[index];
|
|
ft_tt_udrv_tlb2m_config config;
|
|
uint32_t words[3] = {0u, 0u, 0u};
|
|
char what[96];
|
|
int rc;
|
|
|
|
config_from_vector(vector, &config);
|
|
rc = ft_tt_udrv_tlb2m_encode(&config, words);
|
|
|
|
(void)snprintf(what, sizeof(what), "vector %02zu %s encodes", index, vector->name);
|
|
|
|
if (rc != FT_TT_UDRV_OK) {
|
|
ft_tt_test_check_fmt(what, false, "encode returned %d", rc);
|
|
continue;
|
|
}
|
|
|
|
ft_tt_test_check_fmt(what,
|
|
words[0] == vector->words[0] && words[1] == vector->words[1] &&
|
|
words[2] == vector->words[2],
|
|
"got %08x %08x %08x, reference says %08x %08x %08x", words[0], words[1],
|
|
words[2], vector->words[0], vector->words[1], vector->words[2]);
|
|
}
|
|
}
|
|
|
|
static void stage_straddle_is_real(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config config = {0};
|
|
uint32_t low[3] = {0u, 0u, 0u};
|
|
uint32_t high[3] = {0u, 0u, 0u};
|
|
|
|
ft_tt_test_step("Stage 2 — y_start really does straddle the mid32/high32 boundary");
|
|
|
|
/* If y_start did not straddle, one of these two would leave the other
|
|
* word untouched. This is the single property the whole file exists for,
|
|
* so it is asserted directly rather than left implicit in the vectors. */
|
|
config.y_start = 7u; /* bits 61..63 of the low word */
|
|
(void)ft_tt_udrv_tlb2m_encode(&config, low);
|
|
|
|
config.y_start = 56u; /* bits 64..66, i.e. entirely in the high word */
|
|
(void)ft_tt_udrv_tlb2m_encode(&config, high);
|
|
|
|
ft_tt_test_check_fmt("y_start=7 lands only in mid32", low[1] != 0u && low[2] == 0u,
|
|
"mid32=%08x high32=%08x", low[1], low[2]);
|
|
ft_tt_test_check_fmt("y_start=56 lands only in high32", high[1] == 0u && high[2] != 0u,
|
|
"mid32=%08x high32=%08x", high[1], high[2]);
|
|
|
|
config.y_start = 63u;
|
|
(void)ft_tt_udrv_tlb2m_encode(&config, low);
|
|
ft_tt_test_check_fmt("y_start=63 lands in BOTH words", low[1] != 0u && low[2] != 0u,
|
|
"mid32=%08x high32=%08x", low[1], low[2]);
|
|
}
|
|
|
|
static void stage_alignment_refusal(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config config = {0};
|
|
uint32_t words[3] = {0u, 0u, 0u};
|
|
|
|
ft_tt_test_step("Stage 3 — the kernel's own refusals");
|
|
|
|
config.addr = (1ull << 21) + 4u;
|
|
ft_tt_test_check_fmt("a misaligned address is refused",
|
|
ft_tt_udrv_tlb2m_encode(&config, words) == FT_TT_UDRV_EINVAL,
|
|
"addr=0x%llx", (unsigned long long)config.addr);
|
|
|
|
config.addr = 1ull << 20;
|
|
ft_tt_test_check_fmt("a 1 MiB address is refused (windows are 2 MiB)",
|
|
ft_tt_udrv_tlb2m_encode(&config, words) == FT_TT_UDRV_EINVAL, "addr=1MiB");
|
|
|
|
config.addr = 1ull << 21;
|
|
ft_tt_test_check("a 2 MiB address is accepted",
|
|
ft_tt_udrv_tlb2m_encode(&config, words) == FT_TT_UDRV_OK, "addr=2MiB");
|
|
|
|
ft_tt_test_check("a NULL config is refused",
|
|
ft_tt_udrv_tlb2m_encode(NULL, words) == FT_TT_UDRV_EINVAL, "NULL");
|
|
|
|
ft_tt_test_check("a NULL output is refused",
|
|
ft_tt_udrv_tlb2m_encode(&config, NULL) == FT_TT_UDRV_EINVAL, "NULL");
|
|
}
|
|
|
|
static void stage_truncation_matches_the_kernel(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config wide = {0};
|
|
ft_tt_udrv_tlb2m_config narrow = {0};
|
|
uint32_t wide_words[3] = {0u, 0u, 0u};
|
|
uint32_t narrow_words[3] = {0u, 0u, 0u};
|
|
|
|
ft_tt_test_step("Stage 4 — out-of-range fields truncate exactly as a bitfield does");
|
|
|
|
/* The kernel assigns straight into a 6-bit bitfield, so 64 becomes 0 and
|
|
* 65 becomes 1 with no diagnostic. An encoder that masked differently —
|
|
* or clamped — would aim a window somewhere else entirely. */
|
|
wide.x_end = 64u;
|
|
narrow.x_end = 0u;
|
|
(void)ft_tt_udrv_tlb2m_encode(&wide, wide_words);
|
|
(void)ft_tt_udrv_tlb2m_encode(&narrow, narrow_words);
|
|
ft_tt_test_check_fmt("x_end=64 encodes as x_end=0",
|
|
memcmp(wide_words, narrow_words, sizeof(wide_words)) == 0,
|
|
"%08x %08x %08x", wide_words[0], wide_words[1], wide_words[2]);
|
|
|
|
memset(&wide, 0, sizeof(wide));
|
|
memset(&narrow, 0, sizeof(narrow));
|
|
wide.y_start = 70u;
|
|
narrow.y_start = 6u;
|
|
(void)ft_tt_udrv_tlb2m_encode(&wide, wide_words);
|
|
(void)ft_tt_udrv_tlb2m_encode(&narrow, narrow_words);
|
|
ft_tt_test_check_fmt("y_start=70 encodes as y_start=6",
|
|
memcmp(wide_words, narrow_words, sizeof(wide_words)) == 0,
|
|
"%08x %08x %08x", wide_words[0], wide_words[1], wide_words[2]);
|
|
|
|
/* ...and the separate validator is what a caller uses to be told. */
|
|
memset(&wide, 0, sizeof(wide));
|
|
wide.x_end = 64u;
|
|
ft_tt_test_check("config_check refuses x_end=64",
|
|
ft_tt_udrv_tlb2m_config_check(&wide) == FT_TT_UDRV_EINVAL, "64 > 6 bits");
|
|
|
|
memset(&wide, 0, sizeof(wide));
|
|
wide.x_end = 63u;
|
|
ft_tt_test_check("config_check accepts x_end=63",
|
|
ft_tt_udrv_tlb2m_config_check(&wide) == FT_TT_UDRV_OK, "63 fits 6 bits");
|
|
|
|
memset(&wide, 0, sizeof(wide));
|
|
wide.static_vc_id = 8u;
|
|
ft_tt_test_check("config_check refuses static_vc_id=8",
|
|
ft_tt_udrv_tlb2m_config_check(&wide) == FT_TT_UDRV_EINVAL, "8 > 3 bits");
|
|
|
|
memset(&wide, 0, sizeof(wide));
|
|
wide.ordering = 4u;
|
|
ft_tt_test_check("config_check refuses ordering=4",
|
|
ft_tt_udrv_tlb2m_config_check(&wide) == FT_TT_UDRV_EINVAL, "4 > 2 bits");
|
|
}
|
|
|
|
static void stage_register_offsets(void)
|
|
{
|
|
uint32_t offset = 0u;
|
|
uint64_t aperture = 0u;
|
|
|
|
ft_tt_test_step("Stage 5 — register and aperture offsets, pinned literally");
|
|
|
|
ft_tt_test_check_fmt("window 0 registers at 0x1FC00000",
|
|
ft_tt_udrv_tlb2m_register_offset(0u, &offset) == FT_TT_UDRV_OK &&
|
|
offset == 0x1FC00000u,
|
|
"0x%08x", offset);
|
|
|
|
ft_tt_test_check_fmt("window 1 registers at 0x1FC0000C (stride 12, not 16)",
|
|
ft_tt_udrv_tlb2m_register_offset(1u, &offset) == FT_TT_UDRV_OK &&
|
|
offset == 0x1FC0000Cu,
|
|
"0x%08x", offset);
|
|
|
|
ft_tt_test_check_fmt("window 201 (tt-kmd's own) registers at 0x1FC0096C",
|
|
ft_tt_udrv_tlb2m_register_offset(201u, &offset) == FT_TT_UDRV_OK &&
|
|
offset == 0x1FC0096Cu,
|
|
"0x%08x", offset);
|
|
|
|
ft_tt_test_check("window 202 is refused",
|
|
ft_tt_udrv_tlb2m_register_offset(202u, &offset) == FT_TT_UDRV_EINVAL,
|
|
"only 202 windows exist");
|
|
|
|
/* THE PIN THAT MATTERS. 210 * 12 = 2520 = 0x9D8. Sizing this from the 2 MiB
|
|
* count alone gives 0x978; a common mis-derivation gives 0xA08. */
|
|
ft_tt_test_check_fmt("window 0 strided register at 0x1FC009D8",
|
|
ft_tt_udrv_tlb2m_strided_register_offset(0u, &offset) ==
|
|
FT_TT_UDRV_OK &&
|
|
offset == 0x1FC009D8u,
|
|
"0x%08x", offset);
|
|
|
|
ft_tt_test_check_fmt("window 31 strided register at 0x1FC00A54",
|
|
ft_tt_udrv_tlb2m_strided_register_offset(31u, &offset) ==
|
|
FT_TT_UDRV_OK &&
|
|
offset == 0x1FC00A54u,
|
|
"0x%08x", offset);
|
|
|
|
ft_tt_test_check("window 32 has NO strided register",
|
|
ft_tt_udrv_tlb2m_strided_register_offset(32u, &offset) == FT_TT_UDRV_ENOTSUP,
|
|
"only the first 32 windows do");
|
|
|
|
ft_tt_test_check("window 201 has NO strided register",
|
|
ft_tt_udrv_tlb2m_strided_register_offset(201u, &offset) ==
|
|
FT_TT_UDRV_ENOTSUP,
|
|
"201 >= 32");
|
|
|
|
ft_tt_test_check_fmt("the strided block stays inside the 4 KiB register file",
|
|
0x9D8u + (32u * 4u) <= FT_TT_UDRV_TLB_REGS_LEN, "%u <= %u",
|
|
0x9D8u + 128u, FT_TT_UDRV_TLB_REGS_LEN);
|
|
|
|
ft_tt_test_check_fmt("window 0 aperture at BAR0 + 0",
|
|
ft_tt_udrv_tlb2m_aperture_offset(0u, &aperture) == FT_TT_UDRV_OK &&
|
|
aperture == 0u,
|
|
"0x%llx", (unsigned long long)aperture);
|
|
|
|
ft_tt_test_check_fmt("window 201 aperture at BAR0 + 0x19200000",
|
|
ft_tt_udrv_tlb2m_aperture_offset(201u, &aperture) == FT_TT_UDRV_OK &&
|
|
aperture == 0x19200000ull,
|
|
"0x%llx", (unsigned long long)aperture);
|
|
|
|
/* Every window's aperture must fit the 512 MiB BAR0 this board has. */
|
|
ft_tt_test_check_fmt("the last window's aperture ends inside a 512 MiB BAR0",
|
|
0x19200000ull + FT_TT_UDRV_TLB_2M_WINDOW_SIZE <= (512ull << 20),
|
|
"0x%llx <= 0x%llx",
|
|
0x19200000ull + FT_TT_UDRV_TLB_2M_WINDOW_SIZE, 512ull << 20);
|
|
}
|
|
|
|
static void stage_unicast_helper(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config config;
|
|
uint32_t helper_words[3] = {0u, 0u, 0u};
|
|
uint32_t manual_words[3] = {0u, 0u, 0u};
|
|
ft_tt_udrv_tlb2m_config manual = {0};
|
|
|
|
ft_tt_test_step("Stage 6 — the unicast helper maps fields the way CONFIGURE_TLB does");
|
|
|
|
ft_tt_test_check("unicast(8, 0, ARC scratch page, noc 0, strict) builds",
|
|
ft_tt_udrv_tlb2m_config_unicast(&config, 8u, 0u, 0x80000000ull, 0u,
|
|
FT_TT_UDRV_TLB_ORDERING_STRICT) ==
|
|
FT_TT_UDRV_OK,
|
|
"");
|
|
|
|
manual.addr = 0x80000000ull;
|
|
manual.x_end = 8u;
|
|
manual.y_end = 0u;
|
|
manual.ordering = FT_TT_UDRV_TLB_ORDERING_STRICT;
|
|
|
|
(void)ft_tt_udrv_tlb2m_encode(&config, helper_words);
|
|
(void)ft_tt_udrv_tlb2m_encode(&manual, manual_words);
|
|
|
|
ft_tt_test_check_fmt("it equals the hand-built unicast config",
|
|
memcmp(helper_words, manual_words, sizeof(helper_words)) == 0,
|
|
"%08x %08x %08x", helper_words[0], helper_words[1], helper_words[2]);
|
|
|
|
ft_tt_test_check_fmt("it leaves every multicast field at zero",
|
|
config.x_start == 0u && config.y_start == 0u && config.multicast == 0u,
|
|
"x_start=%u y_start=%u mcast=%u", config.x_start, config.y_start,
|
|
config.multicast);
|
|
|
|
ft_tt_test_check("a misaligned unicast address is refused",
|
|
ft_tt_udrv_tlb2m_config_unicast(&config, 8u, 0u, 0x1000ull, 0u, 0u) ==
|
|
FT_TT_UDRV_EINVAL,
|
|
"0x1000 is not 2 MiB aligned");
|
|
|
|
ft_tt_test_check("an out-of-range coordinate is refused",
|
|
ft_tt_udrv_tlb2m_config_unicast(&config, 64u, 0u, 0ull, 0u, 0u) ==
|
|
FT_TT_UDRV_EINVAL,
|
|
"x=64 does not fit 6 bits");
|
|
}
|
|
|
|
/**
|
|
* The decoder, gated against the SAME reference vectors as the encoder.
|
|
*
|
|
* Deliberately NOT a round-trip through our own encoder. A decoder checked
|
|
* only by `decode(encode(c)) == c` agrees with whatever the encoder does,
|
|
* including agreeing with it about a field in the wrong place — the exact
|
|
* self-consistency trap DEV_NOTES §3 catalogues. Here the INPUT is the
|
|
* driver's own words and the EXPECTED OUTPUT is the driver's own fields, so
|
|
* the encoder is not in the loop at all.
|
|
*/
|
|
static void stage_decode_against_the_kernels_words(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config sink = {0};
|
|
size_t index;
|
|
|
|
ft_tt_test_step("Stage 7 — the decoder recovers every reference vector's fields from its words");
|
|
|
|
for (index = 0u; index < FT_TT_TLB2M_REF_VECTOR_COUNT; index++) {
|
|
const ft_tt_tlb2m_reference_vector *vector = &ft_tt_tlb2m_reference_vectors[index];
|
|
ft_tt_udrv_tlb2m_config expected;
|
|
ft_tt_udrv_tlb2m_config decoded;
|
|
|
|
config_from_vector(vector, &expected);
|
|
memset(&decoded, 0xA5, sizeof(decoded));
|
|
|
|
if (ft_tt_udrv_tlb2m_decode(vector->words, &decoded) != FT_TT_UDRV_OK) {
|
|
ft_tt_test_check_fmt("decode succeeds", false, "%s", vector->name);
|
|
continue;
|
|
}
|
|
|
|
ft_tt_test_check_fmt(
|
|
vector->name, ft_tt_udrv_tlb2m_config_equal(&expected, &decoded),
|
|
"addr 0x%llx x_end %u y_end %u x_start %u y_start %u noc %u mcast %u ord %u "
|
|
"linked %u vc? %u sh %u vc %u",
|
|
(unsigned long long)decoded.addr, decoded.x_end, decoded.y_end, decoded.x_start,
|
|
decoded.y_start, decoded.noc, decoded.multicast, decoded.ordering, decoded.linked,
|
|
decoded.use_static_vc, decoded.stream_header, decoded.static_vc_id);
|
|
}
|
|
|
|
ft_tt_test_check("a NULL word array is refused",
|
|
ft_tt_udrv_tlb2m_decode(NULL, &sink) == FT_TT_UDRV_EINVAL, "");
|
|
ft_tt_test_check("a NULL output is refused",
|
|
ft_tt_udrv_tlb2m_decode(ft_tt_tlb2m_reference_vectors[0].words, NULL) ==
|
|
FT_TT_UDRV_EINVAL,
|
|
"");
|
|
}
|
|
|
|
/**
|
|
* The comparator itself. A gate built on `config_equal` is worthless if
|
|
* `config_equal` returns true for two configs that differ, so every field gets
|
|
* perturbed by one step and the comparator must notice.
|
|
*/
|
|
static void stage_the_comparator_can_say_no(void)
|
|
{
|
|
static const char *const names[] = {"addr", "x_end", "y_end",
|
|
"x_start", "y_start", "noc",
|
|
"multicast", "ordering", "linked",
|
|
"use_static_vc", "stream_header", "static_vc_id"};
|
|
ft_tt_udrv_tlb2m_config base = {0};
|
|
size_t field;
|
|
|
|
ft_tt_test_step("Stage 8 — config_equal notices a difference in EVERY field");
|
|
|
|
base.addr = 0x200000ull;
|
|
base.x_end = 8u;
|
|
base.y_end = 3u;
|
|
base.x_start = 1u;
|
|
base.y_start = 2u;
|
|
base.noc = 1u;
|
|
base.multicast = 1u;
|
|
base.ordering = 1u;
|
|
base.linked = 1u;
|
|
base.use_static_vc = 1u;
|
|
base.stream_header = 1u;
|
|
base.static_vc_id = 3u;
|
|
|
|
ft_tt_test_check("a config equals itself", ft_tt_udrv_tlb2m_config_equal(&base, &base), "");
|
|
|
|
for (field = 0u; field < sizeof(names) / sizeof(names[0]); field++) {
|
|
ft_tt_udrv_tlb2m_config other = base;
|
|
uint8_t *by_index[] = {NULL,
|
|
&other.x_end,
|
|
&other.y_end,
|
|
&other.x_start,
|
|
&other.y_start,
|
|
&other.noc,
|
|
&other.multicast,
|
|
&other.ordering,
|
|
&other.linked,
|
|
&other.use_static_vc,
|
|
&other.stream_header,
|
|
&other.static_vc_id};
|
|
|
|
if (field == 0u)
|
|
other.addr += (1ull << FT_TT_UDRV_TLB_2M_SHIFT);
|
|
else
|
|
*by_index[field] = (uint8_t)(*by_index[field] + 1u);
|
|
|
|
ft_tt_test_check_fmt("a one-step change is noticed",
|
|
!ft_tt_udrv_tlb2m_config_equal(&base, &other), "%s", names[field]);
|
|
}
|
|
|
|
ft_tt_test_check("NULL never compares equal",
|
|
!ft_tt_udrv_tlb2m_config_equal(&base, NULL) &&
|
|
!ft_tt_udrv_tlb2m_config_equal(NULL, &base),
|
|
"");
|
|
}
|
|
|
|
/**
|
|
* WHAT `FT_TT_UDRV_AIM_VERIFY=word` CAN AND CANNOT SEE, PROVEN ON THE ENCODING.
|
|
*
|
|
* The `word` verification reads register word 1 and nothing else, and it is on record as
|
|
* giving up "the low address bits". THE BIT LAYOUT SAYS OTHERWISE, and this stage is the
|
|
* proof rather than the assertion: the address field is `addr >> 21` at bit 0, 43 bits
|
|
* wide, so **word 0 IS address bits 21..52** and word 1 carries address bits 53..63 —
|
|
* which are zero for every endpoint on this part. `word` reads back the CORE and not one
|
|
* significant address bit.
|
|
*
|
|
* Two endpoints one 2 MiB page apart on the SAME core must therefore encode to an IDENTICAL
|
|
* word 1 and a different word 0. If that ever stops being true this stage fails, and the
|
|
* safety argument against shipping `word` has to be re-derived instead of re-asserted.
|
|
*/
|
|
static void stage_word_verify_cannot_see_the_address(void)
|
|
{
|
|
ft_tt_udrv_tlb2m_config here;
|
|
ft_tt_udrv_tlb2m_config one_page_on;
|
|
ft_tt_udrv_tlb2m_config decoded;
|
|
ft_tt_udrv_tlb2m_config high;
|
|
uint32_t words_here[3];
|
|
uint32_t words_on[3];
|
|
uint32_t highest[3];
|
|
|
|
memset(&here, 0, sizeof(here));
|
|
/* The run-control block the dispatch path aims at, on a Tensix. Its neighbour one page
|
|
* up is what a corrupted word 0 selects. */
|
|
here.addr = 0x0ffa00000ull;
|
|
here.x_end = 18u;
|
|
here.y_end = 11u;
|
|
one_page_on = here;
|
|
one_page_on.addr = here.addr + (1ull << FT_TT_UDRV_TLB_2M_SHIFT);
|
|
|
|
ft_tt_test_check("both aims encode",
|
|
ft_tt_udrv_tlb2m_encode(&here, words_here) == FT_TT_UDRV_OK &&
|
|
ft_tt_udrv_tlb2m_encode(&one_page_on, words_on) == FT_TT_UDRV_OK,
|
|
"");
|
|
ft_tt_test_check("they differ in word 0", words_here[0] != words_on[0],
|
|
"the 2 MiB base lives in word 0");
|
|
/* THE WHOLE POINT. */
|
|
ft_tt_test_check("and word 1 is IDENTICAL", words_here[1] == words_on[1],
|
|
"a word-1 readback cannot tell these two apart");
|
|
ft_tt_test_check("word 2 is identical too", words_here[2] == words_on[2], "");
|
|
ft_tt_test_check("the endpoint really is in word 1",
|
|
ft_tt_udrv_tlb2m_decode(words_here, &decoded) == FT_TT_UDRV_OK &&
|
|
decoded.x_end == 18u && decoded.y_end == 11u,
|
|
"x_end at bit 43 and y_end at bit 49 both land in word 1");
|
|
|
|
/* AND THE ELEVEN ADDRESS BITS WORD 1 DOES CARRY ARE ZERO ON THIS PART. They are
|
|
* non-zero only past 2^53 = 8 PiB; the card is 32 GiB of DRAM and the highest L1 base
|
|
* the dispatch path aims at is 0x0ffa00000. Encode an 8 TiB address — far past
|
|
* anything reachable — and word 1's address bits are still zero. */
|
|
memset(&high, 0, sizeof(high));
|
|
high.addr = 0x7FFFFE00000ull & ~((1ull << FT_TT_UDRV_TLB_2M_SHIFT) - 1ull);
|
|
ft_tt_test_check("an 8 TiB address encodes",
|
|
ft_tt_udrv_tlb2m_encode(&high, highest) == FT_TT_UDRV_OK, "");
|
|
ft_tt_test_check("and word 1's address bits are STILL zero", (highest[1] & 0x7FFu) == 0u,
|
|
"address bits 53..63 are unreachable on a 32 GiB part");
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
stage_reference_vectors();
|
|
stage_word_verify_cannot_see_the_address();
|
|
stage_straddle_is_real();
|
|
stage_alignment_refusal();
|
|
stage_truncation_matches_the_kernel();
|
|
stage_register_offsets();
|
|
stage_unicast_helper();
|
|
stage_decode_against_the_kernels_words();
|
|
stage_the_comparator_can_say_no();
|
|
|
|
return ft_tt_test_finish();
|
|
}
|