nouveau/mme: Add some generic builder tests

These don't need hardware and instead run entirely in the simulator.
The goal is to have something that we can run in CI and which ensures
consistency between the Turing and Fermi MMEs.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24326>
This commit is contained in:
Faith Ekstrand 2023-01-30 20:12:06 -06:00 committed by Marge Bot
parent 15295e32ec
commit e8032b893d
4 changed files with 302 additions and 0 deletions

View file

@ -109,6 +109,24 @@ idep_nouveau_mme = declare_dependency(
)
if with_tests and not with_platform_android
test('mme_builder',
executable(
'mme_builder_test',
files('tests/mme_runner.cpp', 'tests/mme_builder_test.cpp'),
gnu_symbol_visibility : 'hidden',
include_directories : [inc_include, inc_src],
dependencies : [
dep_libdrm,
idep_gtest,
idep_mesautil,
idep_nvidia_headers,
idep_nouveau_mme,
idep_nouveau_ws
],
),
suite : ['nouveau'],
)
executable(
'mme_fermi_sim_hw_test',
files('tests/mme_runner.cpp', 'tests/mme_fermi_sim_hw_test.cpp'),

View file

@ -0,0 +1,179 @@
#include "mme_runner.h"
#include "mme_tu104_sim.h"
#include <vector>
#include "nvk_clc597.h"
class mme_builder_test : public ::testing::Test {
public:
mme_builder_test();
~mme_builder_test();
std::vector<mme_runner *> sims;
uint32_t expected[DATA_DWORDS];
private:
mme_fermi_sim_runner fermi_sim;
mme_tu104_sim_runner tu104_sim;
};
#define DATA_ADDR 0xc0ffee00
mme_builder_test::mme_builder_test() :
fermi_sim(DATA_ADDR),
tu104_sim(DATA_ADDR)
{
memset(expected, 0, sizeof(expected));
sims.push_back(&fermi_sim);
sims.push_back(&tu104_sim);
}
mme_builder_test::~mme_builder_test()
{ }
#define ASSERT_SIM_DATA(sim) do { \
for (uint32_t i = 0; i < DATA_DWORDS; i++) \
ASSERT_EQ((sim)->data[i], expected[i]); \
} while (0)
TEST_F(mme_builder_test, sanity)
{
const uint32_t canary = 0xc0ffee01;
expected[0] = canary;
for (auto sim : sims) {
mme_builder b;
mme_builder_init(&b, sim->devinfo);
sim->mme_store_data(&b, 0, mme_imm(canary));
auto macro = mme_builder_finish_vec(&b);
std::vector<uint32_t> params;
sim->run_macro(macro, params);
ASSERT_SIM_DATA(sim);
}
}
static uint32_t
merge(uint32_t x, uint32_t y,
uint16_t dst_pos, uint16_t bits, uint16_t src_pos)
{
x &= ~(BITFIELD_MASK(bits) << dst_pos);
y &= (BITFIELD_MASK(bits) << src_pos);
return x | ((y >> src_pos) << dst_pos);
}
static const uint32_t add_cases[] = {
0x00000001,
0xffffffff,
0x0000ffff,
0x00008000,
0x0001ffff,
0xffff8000,
0x00010000,
0x00020000,
0xfffc0000,
0xfffe0000,
};
TEST_F(mme_builder_test, add)
{
for (auto sim : sims) {
mme_builder b;
mme_builder_init(&b, sim->devinfo);
mme_value x = mme_load(&b);
mme_value y = mme_load(&b);
sim->mme_store_data(&b, 0, mme_add(&b, x, y));
auto macro = mme_builder_finish_vec(&b);
for (uint32_t i = 0; i < ARRAY_SIZE(add_cases); i++) {
for (uint32_t j = 0; j < ARRAY_SIZE(add_cases); j++) {
std::vector<uint32_t> params;
params.push_back(add_cases[i]);
params.push_back(add_cases[j]);
sim->run_macro(macro, params);
ASSERT_EQ(sim->data[0], add_cases[i] + add_cases[j]);
}
}
}
}
TEST_F(mme_builder_test, add_imm)
{
for (auto sim : sims) {
mme_builder b;
mme_builder_init(&b, sim->devinfo);
mme_value x = mme_load(&b);
for (uint32_t j = 0; j < ARRAY_SIZE(add_cases); j++) {
mme_value y = mme_imm(add_cases[j]);
sim->mme_store_data(&b, j, mme_add(&b, x, y), true);
}
auto macro = mme_builder_finish_vec(&b);
for (uint32_t i = 0; i < ARRAY_SIZE(add_cases); i++) {
std::vector<uint32_t> params;
params.push_back(add_cases[i]);
sim->run_macro(macro, params);
for (uint32_t j = 0; j < ARRAY_SIZE(add_cases); j++)
ASSERT_EQ(sim->data[j], add_cases[i] + add_cases[j]);
}
}
}
TEST_F(mme_builder_test, merge)
{
static const struct {
uint16_t dst_pos;
uint16_t bits;
uint16_t src_pos;
} cases[] = {
{ 12, 12, 20 },
{ 12, 8, 20 },
{ 8, 12, 20 },
{ 12, 16, 8 },
{ 24, 12, 8 },
};
static const uint32_t x = 0x0c406fe0;
static const uint32_t y = 0x76543210;
for (uint32_t i = 0; i < ARRAY_SIZE(cases); i++) {
expected[i] = merge(x, y, cases[i].dst_pos,
cases[i].bits, cases[i].src_pos);
}
for (auto sim : sims) {
mme_builder b;
mme_builder_init(&b, sim->devinfo);
mme_value xv = mme_load(&b);
mme_value yv = mme_load(&b);
for (uint32_t i = 0; i < ARRAY_SIZE(cases); i++) {
mme_value mv = mme_merge(&b, xv, yv, cases[i].dst_pos,
cases[i].bits, cases[i].src_pos);
sim->mme_store_data(&b, i, mv, true);
}
auto macro = mme_builder_finish_vec(&b);
std::vector<uint32_t> params;
params.push_back(x);
params.push_back(y);
sim->run_macro(macro, params);
ASSERT_SIM_DATA(sim);
}
}

View file

@ -7,6 +7,8 @@
#include "mme_fermi_sim.h"
#include "mme_tu104_sim.h"
#include "nvk_clc597.h"
#include "nouveau_bo.h"
#include "nouveau_context.h"
@ -29,6 +31,13 @@ mme_hw_runner::mme_hw_runner() :
memset(&push, 0, sizeof(push));
}
void
mme_runner::mme_store_data(mme_builder *b, uint32_t dw_idx,
mme_value data, bool free_reg)
{
mme_store_imm_addr(b, data_addr + dw_idx * 4, data, free_reg);
}
mme_hw_runner::~mme_hw_runner()
{
if (push_bo) {
@ -178,3 +187,69 @@ mme_hw_runner::run_macro(const std::vector<uint32_t>& macro,
submit_push();
}
mme_fermi_sim_runner::mme_fermi_sim_runner(uint64_t data_addr)
{
memset(&info, 0, sizeof(info));
info.cls_eng3d = FERMI_A;
memset(data_store, 0, sizeof(data_store));
this->devinfo = &info;
this->data_addr = data_addr,
this->data = data_store;
}
mme_fermi_sim_runner::~mme_fermi_sim_runner()
{ }
void
mme_fermi_sim_runner::run_macro(const std::vector<uint32_t>& macro,
const std::vector<uint32_t>& params)
{
std::vector<mme_fermi_inst> insts(macro.size());
mme_fermi_decode(&insts[0], &macro[0], macro.size());
/* First, make a copy of the data and simulate the macro */
mme_fermi_sim_mem sim_mem = {
.addr = data_addr,
.data = data,
.size = DATA_BO_SIZE,
};
mme_fermi_sim(insts.size(), &insts[0],
params.size(), &params[0],
1, &sim_mem);
}
mme_tu104_sim_runner::mme_tu104_sim_runner(uint64_t data_addr)
{
memset(&info, 0, sizeof(info));
info.cls_eng3d = TURING_A;
memset(data_store, 0, sizeof(data_store));
this->devinfo = &info;
this->data_addr = data_addr,
this->data = data_store;
}
mme_tu104_sim_runner::~mme_tu104_sim_runner()
{ }
void
mme_tu104_sim_runner::run_macro(const std::vector<uint32_t>& macro,
const std::vector<uint32_t>& params)
{
std::vector<mme_tu104_inst> insts(macro.size());
mme_tu104_decode(&insts[0], &macro[0], macro.size());
/* First, make a copy of the data and simulate the macro */
mme_tu104_sim_mem sim_mem = {
.addr = data_addr,
.data = data,
.size = DATA_BO_SIZE,
};
mme_tu104_sim(insts.size(), &insts[0],
params.size(), &params[0],
1, &sim_mem);
}

View file

@ -11,6 +11,7 @@ struct nouveau_ws_device;
#include "nvk_cl9097.h"
#define DATA_BO_SIZE 4096
#define DATA_DWORDS 1024
class mme_runner {
public:
@ -20,6 +21,9 @@ public:
virtual void run_macro(const std::vector<uint32_t>& macro,
const std::vector<uint32_t>& params) = 0;
void mme_store_data(mme_builder *b, uint32_t dw_idx,
mme_value data, bool free_reg = false);
const nv_device_info *devinfo;
uint64_t data_addr;
uint32_t *data;
@ -49,6 +53,32 @@ private:
struct nv_push push;
};
class mme_fermi_sim_runner : public mme_runner {
public:
mme_fermi_sim_runner(uint64_t data_addr);
virtual ~mme_fermi_sim_runner();
virtual void run_macro(const std::vector<uint32_t>& macro,
const std::vector<uint32_t>& params);
private:
struct nv_device_info info;
uint32_t data_store[DATA_DWORDS];
};
class mme_tu104_sim_runner : public mme_runner {
public:
mme_tu104_sim_runner(uint64_t data_addr);
virtual ~mme_tu104_sim_runner();
virtual void run_macro(const std::vector<uint32_t>& macro,
const std::vector<uint32_t>& params);
private:
struct nv_device_info info;
uint32_t data_store[DATA_DWORDS];
};
inline std::vector<uint32_t>
mme_builder_finish_vec(mme_builder *b)
{