blob: e0647858859869e71f2acf7a56ac3b26bf79046a [file] [log] [blame]
#include "sim/kelvin_vector_convolution_instructions.h"
#include <array>
#include <cstdint>
#include <cstring>
#include <functional>
#include <vector>
#include "sim/test/kelvin_vector_instructions_test_base.h"
#include "sim/test/testfiles/kelvin_vector_convolution_testdata.h"
#include "googletest/include/gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "riscv/riscv_state.h"
#include "mpact/sim/generic/instruction.h"
namespace {
using mpact::sim::generic::Instruction;
// Semantic functions.
using kelvin::sim::KelvinVConv;
class KelvinVectorConvolutionInstructionsTest
: public kelvin::sim::test::KelvinVectorInstructionsTestBase {
protected:
void ConvolutionTestHelper(const kelvin::sim::vconv_cmd_t vconv_cmd,
bool expect_fail = false) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
constexpr int kVd = 48;
const uint32_t kVLenInByte = state_->vector_length() / 8;
const uint32_t kVLenInWord = state_->vector_length() / 32;
// Set vs1 and vs3
std::vector<uint8_t> vs1_value(kVLenInWord * kVLenInByte);
auto vs1_span = absl::Span<uint8_t>(vs1_value);
memcpy(vs1_span.data(), kVConvIn1, sizeof(kVConvIn1));
std::vector<uint8_t> vs3_value(kVLenInWord * kVLenInByte);
auto vs3_span = absl::Span<uint8_t>(vs3_value);
memcpy(vs3_span.data(), kVConvIn2, sizeof(kVConvIn2));
for (int i = 0; i < kVLenInWord; ++i) {
auto vs1_name = absl::StrCat("v", kVs1 + i);
auto vs3_name = absl::StrCat("v", kVs3 + i);
SetVectorRegisterValues<uint8_t>(
{{vs1_name, vs1_span.subspan(i * kVLenInByte, kVLenInByte)},
{vs3_name, vs3_span.subspan(i * kVLenInByte, kVLenInByte)}});
}
uint32_t vconv_cmd_value;
memcpy(&vconv_cmd_value, &vconv_cmd, sizeof(vconv_cmd_value));
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs2Name,
static_cast<uint32_t>(vconv_cmd_value)}});
// Reset accumulation register
for (int i = 0; i < kVLenInWord; ++i) {
auto acc_vec = state_->acc_vec(i);
acc_vec->fill(0);
}
// Call VConv twice with the swapped vs1 and vs3
std::array<InstructionPtr, 2> instructions = {CreateInstruction(),
CreateInstruction()};
instructions[0]->set_semantic_function(KelvinVConv);
AppendVectorRegisterOperands(instructions[0].get(), kVLenInWord,
1 /* src1_widen_factor*/, kVs1, {},
false /* widen_dst*/, {kVd});
AppendRegisterOperands(instructions[0].get(), {kelvin::sim::test::kRs2Name},
{});
AppendVectorRegisterOperands(instructions[0].get(), kVLenInWord,
1 /* src3_widen_factor*/, kVs3, {},
false /* widen_dst*/, {});
instructions[1]->set_semantic_function(KelvinVConv);
AppendVectorRegisterOperands(instructions[1].get(), 1,
kVLenInWord /* src1_widen_factor*/, kVs3, {},
false /* widen_dst*/, {kVd});
AppendRegisterOperands(instructions[1].get(), {kelvin::sim::test::kRs2Name},
{});
AppendVectorRegisterOperands(instructions[1].get(), 1,
kVLenInWord /* src3_widen_factor*/, kVs1, {},
false /* widen_dst*/, {});
execution_fail_ = false;
state_->set_on_trap(trap_call_back_);
instructions[0]->Execute();
if (expect_fail) {
EXPECT_TRUE(execution_fail_);
return;
}
instructions[1]->Execute();
EXPECT_FALSE(execution_fail_);
auto result_acc = state_->acc_register();
for (int i = 0; i < result_acc.size(); ++i) {
for (int j = 0; j < result_acc[i].size(); ++j) {
EXPECT_EQ(result_acc[i][j], kVConvOutRef[i][j])
<< absl::StrCat("acc[", i, "][", j, "] != Ref[", i, "][", j, "]");
}
}
}
private:
bool execution_fail_;
std::function<bool(bool, uint64_t, uint64_t, uint64_t, const Instruction *)>
trap_call_back_ = [this](bool is_interrupt, uint64_t trap_value,
uint64_t exception_code, uint64_t epc,
const Instruction *instruction) {
auto code =
static_cast<mpact::sim::riscv::ExceptionCode>(exception_code);
if (code == mpact::sim::riscv::ExceptionCode::kIllegalInstruction) {
this->execution_fail_ = true;
return true;
}
return false;
};
};
TEST_F(KelvinVectorConvolutionInstructionsTest, VConv) {
// Set the convolution to have 8 filters (starting from index 0), with the
// data bias of 86 (unsigned) and the filter bias of 188 (signed).
kelvin::sim::vconv_cmd_t vconv_cmd{.mode = 0,
.start = 0,
.stop = 7,
.sbias1 = 86,
.sdata1 = false,
.sbias2 = 188,
.sdata2 = true};
ConvolutionTestHelper(vconv_cmd);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VConvWrongMode) {
// Set the convolution to work on 16-bit input/filter (illegal setting).
kelvin::sim::vconv_cmd_t vconv_cmd{.mode = 1,
.start = 0,
.stop = 7,
.sbias1 = 86,
.sdata1 = false,
.sbias2 = 188,
.sdata2 = true};
ConvolutionTestHelper(vconv_cmd, true);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VConvTooLargeStop) {
// Set the convolution to work on 9 filters (too many filters).
kelvin::sim::vconv_cmd_t vconv_cmd{.mode = 0,
.start = 0,
.stop = 8,
.sbias1 = 86,
.sdata1 = false,
.sbias2 = 188,
.sdata2 = true};
ConvolutionTestHelper(vconv_cmd, true);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VConvWrongStop) {
// Set the convolution to start from filter 7 and to stop at filter 5 (reverse
// order).
kelvin::sim::vconv_cmd_t vconv_cmd{.mode = 0,
.start = 7,
.stop = 5,
.sbias1 = 86,
.sdata1 = false,
.sbias2 = 188,
.sdata2 = true};
ConvolutionTestHelper(vconv_cmd, true);
}
} // namespace