blob: 8dbefe651747eaf59c83cc403868c0b8cdf8148c [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sim/kelvin_vector_convolution_instructions.h"
#include <array>
#include <cstdint>
#include <cstring>
#include <functional>
#include <type_traits>
#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/functional/bind_front.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;
using kelvin::sim::KelvinVDwconv;
class KelvinVectorConvolutionInstructionsTest
: public kelvin::sim::test::KelvinVectorInstructionsTestBase {
protected:
// Write [1-32] into register, accounting for internal dwconv swizzle
template <typename T>
void SetRegisterAscending(int reg, T offset) {
std::vector<T> data(32);
for (uint32_t i = 0; i < data.size(); i++) {
uint32_t reg;
switch ((i >> 3) & 0b11) {
case 0:
reg = 0;
break;
case 1:
reg = 2;
break;
case 2:
reg = 1;
break;
case 3:
reg = 3;
break;
}
uint32_t pos = i & 0b111;
uint32_t target = (pos << 2) | reg;
data[target] = i + offset;
}
auto reg_name = absl::StrCat("v", reg);
SetVectorRegisterValues<T>({{reg_name, absl::Span<T>(data)}});
}
template <typename T>
void SetRegisterConstant(int reg, T val) {
std::vector<T> data(32, val);
auto reg_name = absl::StrCat("v", reg);
SetVectorRegisterValues<T>({{reg_name, absl::Span<T>(data)}});
}
void ResetDwAccumulator() { state_->dw_acc_register().fill(0); }
template <bool kWriteAcc = true>
void ExecuteDwconv(bool expect_fail = false) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
constexpr int kVd = 48;
InstructionPtr instruction = CreateInstruction();
instruction->set_semantic_function(
absl::bind_front(KelvinVDwconv, kWriteAcc));
AppendVectorRegisterOperands(instruction.get(), 1, 9, kVs1, {},
false /* widen_dst*/, {});
AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs2Name},
{});
AppendVectorRegisterOperands(instruction.get(), 1, 3, kVs3, {},
false /* widen_dst*/, {});
if (kWriteAcc) {
std::vector<kelvin::sim::test::RegisterBase *> reg_vec;
for (int i = 0; i < 4; i++) {
auto reg_name = absl::StrCat("v", kVd + i);
reg_vec.push_back(
state_->GetRegister<kelvin::sim::test::RVVectorRegister>(reg_name)
.first);
}
auto *op = new kelvin::sim::test::RV32VectorDestinationOperand(
absl::Span<kelvin::sim::test::RegisterBase *>(reg_vec), 0,
absl::StrCat("v", kVd));
instruction->AppendDestination(op);
}
execution_fail_ = false;
state_->set_on_trap(trap_call_back_);
instruction->Execute();
EXPECT_EQ(expect_fail, execution_fail_);
}
template <bool kWriteAcc = true>
void TestAccumulatorAndRegisters(
std::function<void(int /*index*/, int32_t /*value*/)> f) {
constexpr int kVd = 48;
// Check internal accumulator.
auto acc_vec = state_->dw_acc_register();
for (int i = 0; i < 32; i++) {
f(i, acc_vec[i]);
}
// Check Registers
if (kWriteAcc) {
for (int r = 0; r < 4; r++) {
auto reg = state_
->GetRegister<kelvin::sim::test::RVVectorRegister>(
absl::StrCat("v", kVd + r))
.first;
auto reg_data = reg->data_buffer()->Get<int32_t>();
for (int elem = 0; elem < 8; elem++) {
int i = (r * 8) + elem;
int32_t value = reg_data[elem];
f(i, value);
}
}
}
}
void DepthwiseConvolutionBiasTestHelper(uint32_t sbias1, uint32_t sbias2) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
kelvin::sim::vdwconv_u8_t dwconv_cmd;
memset(&dwconv_cmd, 0, sizeof(dwconv_cmd));
dwconv_cmd.sdata1 = 1;
dwconv_cmd.sdata2 = 1;
dwconv_cmd.sbias1 = sbias1;
dwconv_cmd.sbias2 = sbias2;
uint32_t vdwconv_cmd_value;
memcpy(&vdwconv_cmd_value, &dwconv_cmd, sizeof(vdwconv_cmd_value));
SetRegisterValues<uint32_t>(
{{kelvin::sim::test::kRs2Name, vdwconv_cmd_value}});
ResetDwAccumulator();
SetRegisterAscending<int8_t>(kVs1, 1 - sbias1);
SetRegisterConstant<int8_t>(kVs1 + 1, -sbias1);
SetRegisterConstant<int8_t>(kVs1 + 2, -sbias1);
SetRegisterConstant<int8_t>(kVs3, 1 - sbias2);
SetRegisterConstant<int8_t>(kVs3 + 1, -sbias2);
SetRegisterConstant<int8_t>(kVs3 + 2, -sbias2);
ExecuteDwconv();
TestAccumulatorAndRegisters(
[](int i, int32_t value) { EXPECT_EQ(i + 1, value); });
}
template <typename T, bool kWriteAcc = true>
void DepthwiseConvolutionRegbaseTestHelper(int regbase, int prev, int curr,
int next) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
kelvin::sim::vdwconv_u8_t dwconv_cmd;
memset(&dwconv_cmd, 0, sizeof(dwconv_cmd));
dwconv_cmd.regbase = regbase;
if (std::is_signed<T>::value) {
dwconv_cmd.sdata1 = 1;
dwconv_cmd.sdata2 = 1;
}
uint32_t vdwconv_cmd_value;
memcpy(&vdwconv_cmd_value, &dwconv_cmd, sizeof(vdwconv_cmd_value));
SetRegisterValues<uint32_t>(
{{kelvin::sim::test::kRs2Name, vdwconv_cmd_value}});
// Test prev reg
{
ResetDwAccumulator();
SetRegisterAscending<T>(kVs1 + prev, 1);
SetRegisterConstant<T>(kVs1 + curr, 0);
SetRegisterConstant<T>(kVs1 + next, 0);
SetRegisterConstant<T>(kVs3, 1);
SetRegisterConstant<T>(kVs3 + 1, 0);
SetRegisterConstant<T>(kVs3 + 2, 0);
ExecuteDwconv<kWriteAcc>();
TestAccumulatorAndRegisters<kWriteAcc>(
[](int i, int32_t value) { EXPECT_EQ(i + 1, value); });
}
// Test curr reg
{
ResetDwAccumulator();
SetRegisterConstant<T>(kVs1 + prev, 0);
SetRegisterAscending<T>(kVs1 + curr, 1);
SetRegisterConstant<T>(kVs1 + next, 0);
SetRegisterConstant<T>(kVs3, 0);
SetRegisterConstant<T>(kVs3 + 1, 2);
SetRegisterConstant<T>(kVs3 + 2, 0);
ExecuteDwconv<kWriteAcc>();
TestAccumulatorAndRegisters<kWriteAcc>(
[](int i, int32_t value) { EXPECT_EQ(2 * (i + 1), value); });
}
// Test next reg
{
ResetDwAccumulator();
SetRegisterConstant<T>(kVs1 + prev, 0);
SetRegisterConstant<T>(kVs1 + curr, 0);
SetRegisterAscending<T>(kVs1 + next, 1);
SetRegisterConstant<T>(kVs3, 0);
SetRegisterConstant<T>(kVs3 + 1, 0);
SetRegisterConstant<T>(kVs3 + 2, 3);
ExecuteDwconv<kWriteAcc>();
TestAccumulatorAndRegisters<kWriteAcc>(
[](int i, int32_t value) { EXPECT_EQ(3 * (i + 1), value); });
}
}
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);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VDwconvRegbase) {
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(0, 0, 1, 2);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(1, 1, 2, 3);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(2, 2, 3, 4);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(3, 3, 4, 5);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(4, 4, 5, 6);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(5, 5, 6, 7);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(6, 6, 7, 8);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(7, 1, 0, 2);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(8, 1, 2, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(9, 3, 4, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(10, 5, 6, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(11, 7, 8, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(12, 2, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(13, 4, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(14, 6, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, true>(15, 8, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(0, 0, 1, 2);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(1, 1, 2, 3);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(2, 2, 3, 4);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(3, 3, 4, 5);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(4, 4, 5, 6);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(5, 5, 6, 7);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(6, 6, 7, 8);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(7, 1, 0, 2);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(8, 1, 2, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(9, 3, 4, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(10, 5, 6, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(11, 7, 8, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(12, 2, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(13, 4, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(14, 6, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, true>(15, 8, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(0, 0, 1, 2);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(1, 1, 2, 3);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(2, 2, 3, 4);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(3, 3, 4, 5);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(4, 4, 5, 6);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(5, 5, 6, 7);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(6, 6, 7, 8);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(7, 1, 0, 2);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(8, 1, 2, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(9, 3, 4, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(10, 5, 6, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(11, 7, 8, 0);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(12, 2, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(13, 4, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(14, 6, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<uint8_t, false>(15, 8, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(0, 0, 1, 2);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(1, 1, 2, 3);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(2, 2, 3, 4);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(3, 3, 4, 5);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(4, 4, 5, 6);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(5, 5, 6, 7);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(6, 6, 7, 8);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(7, 1, 0, 2);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(8, 1, 2, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(9, 3, 4, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(10, 5, 6, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(11, 7, 8, 0);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(12, 2, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(13, 4, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(14, 6, 0, 1);
DepthwiseConvolutionRegbaseTestHelper<int8_t, false>(15, 8, 0, 1);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VDwconvSignBiases) {
DepthwiseConvolutionBiasTestHelper(2, 0);
DepthwiseConvolutionBiasTestHelper(0, 3);
DepthwiseConvolutionBiasTestHelper(5, 5);
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VDwconvSparsity1) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
kelvin::sim::vdwconv_u8_t dwconv_cmd;
memset(&dwconv_cmd, 0, sizeof(dwconv_cmd));
dwconv_cmd.regbase = 0;
dwconv_cmd.sparsity = 1;
uint32_t vdwconv_cmd_value;
memcpy(&vdwconv_cmd_value, &dwconv_cmd, sizeof(vdwconv_cmd_value));
SetRegisterValues<uint32_t>(
{{kelvin::sim::test::kRs2Name, vdwconv_cmd_value}});
{
ResetDwAccumulator();
SetRegisterConstant<uint8_t>(kVs1, 42);
SetRegisterAscending<uint8_t>(kVs1 + 1, 1);
SetRegisterConstant<uint8_t>(kVs1 + 2, 0);
SetRegisterConstant<uint8_t>(kVs3, 1);
SetRegisterConstant<uint8_t>(kVs3 + 1, 0);
SetRegisterConstant<uint8_t>(kVs3 + 2, 0);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
EXPECT_EQ((i % 8 == 0 ? 42 : i), value)
<< "Incorrect value at index " << i;
});
}
{
ResetDwAccumulator();
SetRegisterConstant<uint8_t>(kVs1, 0);
SetRegisterAscending<uint8_t>(kVs1 + 1, 1);
SetRegisterConstant<uint8_t>(kVs1 + 2, 0);
SetRegisterConstant<uint8_t>(kVs3, 0);
SetRegisterConstant<uint8_t>(kVs3 + 1, 1);
SetRegisterConstant<uint8_t>(kVs3 + 2, 0);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
EXPECT_EQ(i + 1, value) << "Incorrect value at index " << i;
});
}
{
ResetDwAccumulator();
SetRegisterConstant<uint8_t>(kVs1, 0);
SetRegisterAscending<uint8_t>(kVs1 + 1, 0);
SetRegisterConstant<uint8_t>(kVs1 + 2, 42);
SetRegisterConstant<uint8_t>(kVs3, 0);
SetRegisterConstant<uint8_t>(kVs3 + 1, 0);
SetRegisterConstant<uint8_t>(kVs3 + 2, 1);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
EXPECT_EQ((i % 8 == 7 ? 42 : i + 1), value)
<< "Incorrect value at index " << i;
});
}
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VDwconvSparsity2) {
constexpr int kVs1 = 0;
constexpr int kVs3 = 16;
kelvin::sim::vdwconv_u8_t dwconv_cmd;
memset(&dwconv_cmd, 0, sizeof(dwconv_cmd));
dwconv_cmd.regbase = 0;
dwconv_cmd.sparsity = 2;
uint32_t vdwconv_cmd_value;
memcpy(&vdwconv_cmd_value, &dwconv_cmd, sizeof(vdwconv_cmd_value));
SetRegisterValues<uint32_t>(
{{kelvin::sim::test::kRs2Name, vdwconv_cmd_value}});
{
ResetDwAccumulator();
SetRegisterAscending<uint8_t>(kVs1, 1);
SetRegisterConstant<uint8_t>(kVs1 + 1, 0);
SetRegisterConstant<uint8_t>(kVs1 + 2, 0);
SetRegisterConstant<uint8_t>(kVs3, 1);
SetRegisterConstant<uint8_t>(kVs3 + 1, 0);
SetRegisterConstant<uint8_t>(kVs3 + 2, 0);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
EXPECT_EQ(i + 1, value) << "Incorrect value at index " << i;
});
}
{
ResetDwAccumulator();
SetRegisterAscending<uint8_t>(kVs1, 0);
SetRegisterConstant<uint8_t>(kVs1 + 1, 42);
SetRegisterConstant<uint8_t>(kVs1 + 2, 0);
SetRegisterConstant<uint8_t>(kVs3, 0);
SetRegisterConstant<uint8_t>(kVs3 + 1, 1);
SetRegisterConstant<uint8_t>(kVs3 + 2, 0);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
EXPECT_EQ((i % 8 == 7 ? 42 : i + 1), value)
<< "Incorrect value at index " << i;
});
}
{
ResetDwAccumulator();
SetRegisterAscending<uint8_t>(kVs1, 0);
SetRegisterConstant<uint8_t>(kVs1 + 1, 42);
SetRegisterConstant<uint8_t>(kVs1 + 2, 0);
SetRegisterConstant<uint8_t>(kVs3, 0);
SetRegisterConstant<uint8_t>(kVs3 + 1, 0);
SetRegisterConstant<uint8_t>(kVs3 + 2, 1);
ExecuteDwconv();
TestAccumulatorAndRegisters([](int i, int32_t value) {
if (i % 8 >= 6) {
EXPECT_EQ(42, value) << "Incorrect value at index " << i;
} else {
EXPECT_EQ(i + 2, value) << "Incorrect value at index " << i;
}
});
}
}
TEST_F(KelvinVectorConvolutionInstructionsTest, VDwconvSparsity3) {
// Sparsity value of 3 is invalid.
kelvin::sim::vdwconv_u8_t dwconv_cmd;
memset(&dwconv_cmd, 0, sizeof(dwconv_cmd));
dwconv_cmd.regbase = 0;
dwconv_cmd.sparsity = 3;
uint32_t vdwconv_cmd_value;
memcpy(&vdwconv_cmd_value, &dwconv_cmd, sizeof(vdwconv_cmd_value));
SetRegisterValues<uint32_t>(
{{kelvin::sim::test::kRs2Name, vdwconv_cmd_value}});
ExecuteDwconv(/* expect_fail */ true);
}
} // namespace