Internal change
PiperOrigin-RevId: 548872652
diff --git a/sim/test/BUILD b/sim/test/BUILD
index 735c876..c365fd9 100644
--- a/sim/test/BUILD
+++ b/sim/test/BUILD
@@ -107,6 +107,23 @@
)
cc_test(
+ name = "kelvin_vector_memory_instructions_test",
+ srcs = ["kelvin_vector_memory_instructions_test.cc"],
+ copts = [
+ "-Werror",
+ "-Wvla-extension",
+ ],
+ deps = [
+ ":kelvin_vector_instructions_test_base",
+ "//sim:kelvin_instructions",
+ "@com_google_absl//absl/functional:bind_front",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ "@com_google_mpact-sim//mpact/sim/generic:instruction",
+ ],
+)
+
+cc_test(
name = "kelvin_log_instructions_test",
srcs = ["kelvin_log_instructions_test.cc"],
copts = [
diff --git a/sim/test/kelvin_vector_instructions_test.cc b/sim/test/kelvin_vector_instructions_test.cc
index 250ffab..f4c4c0b 100644
--- a/sim/test/kelvin_vector_instructions_test.cc
+++ b/sim/test/kelvin_vector_instructions_test.cc
@@ -10,7 +10,6 @@
#include <utility>
#include <vector>
-#include "sim/kelvin_vector_memory_instructions.h"
#include "sim/test/kelvin_vector_instructions_test_base.h"
#include "googletest/include/gtest/gtest.h"
#include "absl/functional/bind_front.h"
@@ -18,14 +17,13 @@
#include "absl/strings/string_view.h"
#include "mpact/sim/generic/instruction.h"
-// This file contains the tests for testing kelvin vector instructions.
+// This file contains the tests for testing kelvin vector binary instructions.
namespace {
using mpact::sim::generic::Instruction;
// Semantic functions.
-using kelvin::sim::KelvinGetVl;
using kelvin::sim::KelvinVAbsd;
using kelvin::sim::KelvinVAcc;
using kelvin::sim::KelvinVAdd;
@@ -45,8 +43,6 @@
using kelvin::sim::KelvinVGt;
using kelvin::sim::KelvinVHadd;
using kelvin::sim::KelvinVHsub;
-using kelvin::sim::KelvinVLd;
-using kelvin::sim::KelvinVLdRegWrite;
using kelvin::sim::KelvinVLe;
using kelvin::sim::KelvinVLt;
using kelvin::sim::KelvinVMacc;
@@ -78,8 +74,6 @@
using kelvin::sim::KelvinVSra;
using kelvin::sim::KelvinVSrans;
using kelvin::sim::KelvinVSrl;
-using kelvin::sim::KelvinVSt;
-using kelvin::sim::KelvinVStQ;
using kelvin::sim::KelvinVSub;
using kelvin::sim::KelvinVSubs;
using kelvin::sim::KelvinVSubsu;
@@ -106,23 +100,6 @@
class KelvinVectorInstructionsTest
: public kelvin::sim::test::KelvinVectorInstructionsTestBase {
public:
- template <typename T>
- absl::string_view KelvinTestTypeSuffix() {
- absl::string_view type_suffix = "Unknown";
- switch (sizeof(T)) {
- case 4:
- type_suffix = "W";
- break;
- case 2:
- type_suffix = "H";
- break;
- case 1:
- type_suffix = "B";
- break;
- }
- return type_suffix;
- }
-
template <template <typename, typename, typename> class F, typename TD,
typename TS1, typename TS2>
void KelvinVectorBinaryOpHelper(absl::string_view name) {
@@ -477,7 +454,9 @@
KelvinVAbsd<Vs1>(scalar, strip_mine, inst);
}
};
-TEST_F(KelvinVectorInstructionsTest, VAbsd) {
+
+// TODO(b/291683818): Check the implementation and re-enable the logic.
+TEST_F(KelvinVectorInstructionsTest, DISABLED_VAbsd) {
KelvinVectorBinaryOpHelper<VAbsdOp, uint8_t, int8_t, int8_t, uint16_t,
int16_t, int16_t, uint32_t, int32_t, int32_t>(
"VAbsd");
@@ -1682,391 +1661,4 @@
KelvinShuffleOpHelper<VZipOp, int8_t, int16_t, int32_t>("VZip", kWidenDst);
}
-class KelvinVectorInstructionsMemoryTest : public KelvinVectorInstructionsTest {
- public:
- template <typename T>
- void MemoryLoadStoreOpTestHelper(absl::string_view name, bool has_length,
- bool has_stride, bool strip_mine,
- bool post_increment, bool x_variant,
- bool is_load, bool is_quad) {
- InstructionPtr child_instruction(
- new Instruction(next_instruction_address_, state_),
- [](Instruction *inst) { inst->DecRef(); });
- child_instruction->set_size(4);
- auto instruction = CreateInstruction();
-
- if (is_load) {
- child_instruction->set_semantic_function(
- absl::bind_front(&KelvinVLdRegWrite<T>, strip_mine));
- instruction->set_semantic_function(
- absl::bind_front(&KelvinVLd<T>, has_length, has_stride, strip_mine));
- instruction->AppendChild(child_instruction.get());
- } else {
- if (is_quad) {
- instruction->set_semantic_function(
- absl::bind_front(&KelvinVStQ<T>, strip_mine));
- } else {
- instruction->set_semantic_function(absl::bind_front(
- &KelvinVSt<T>, has_length, has_stride, strip_mine));
- }
- }
-
- // Setup source and child instruction operands.
- const uint32_t num_ops = strip_mine ? 4 : 1;
- if (is_load) {
- AppendVectorRegisterOperands(
- child_instruction.get(), num_ops, 1 /* src1_widen_factor */, {}, {},
- false /* widen_dst */, {kelvin::sim::test::kVd});
- } else { // Store
- AppendVectorRegisterOperands(
- instruction.get(), num_ops, 1 /* src1_widen_factor */,
- kelvin::sim::test::kVd, {}, false /* widen_dst */, {});
- }
- AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name},
- {});
- if (!x_variant) {
- AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs2Name},
- {});
- }
-
- if (post_increment) {
- AppendRegisterOperands(instruction.get(), {},
- {kelvin::sim::test::kRs1Name});
- }
-
- // x variant can't have length or stride fields.
- if (x_variant && (has_length || has_stride)) {
- GTEST_FAIL();
- }
-
- // xx variant can't have no length, no stride, and no post_increment
- // encoding
- if (!x_variant && !has_length && !has_stride && !post_increment) {
- GTEST_FAIL();
- }
-
- // length and stride fields can't coexist without post_increment
- if (has_length && has_stride && !post_increment) {
- GTEST_FAIL();
- }
-
- // Quad store need to have stride specified and no length
- if (is_quad && is_load) {
- GTEST_FAIL();
- }
- if ((is_quad && has_length) || (is_quad && !has_stride)) {
- GTEST_FAIL();
- }
- const uint32_t vector_length_in_bytes = state_->vector_length() / 8;
- const uint32_t vd_size = vector_length_in_bytes / sizeof(T);
- const uint32_t len_or_strides[] = {0, 1, vd_size - 1,
- vd_size, 2 * vd_size, 4 * vd_size};
-
- // Check with different values for length and stride if applicable.
- for (int test = 0;
- test < (has_length || has_stride ? std::size(len_or_strides) : 1);
- test++) {
- // Store stride can't be smaller than vd_size
- if ((is_quad && len_or_strides[test] < vd_size / 4) ||
- (!is_load && has_stride && len_or_strides[test] < vd_size)) {
- continue;
- }
- // Set input register values.
- SetRegisterValues<uint32_t>(
- {{kelvin::sim::test::kRs1Name, kelvin::sim::test::kDataLoadAddress}});
-
- if (!x_variant) {
- SetRegisterValues<uint32_t>(
- {{kelvin::sim::test::kRs2Name, len_or_strides[test]}});
- }
-
- // Fill vector register(s) with random values.
- std::vector<T> vd_value(vector_length_in_bytes / sizeof(T) * num_ops);
- auto vd_span = absl::Span<T>(vd_value);
- FillArrayWithRandomValues<T>(vd_span);
- for (int i = 0; i < num_ops; i++) {
- auto vd_name = absl::StrCat("v", kelvin::sim::test::kVd + i);
- SetVectorRegisterValues<T>(
- {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
- }
-
- // Execute instruction.
- instruction->Execute();
-
- // Compute memory values. For load test it is the expected output; for
- // store test it is the actual output.
- std::vector<T> memory_values(vd_size * num_ops);
- uint32_t addr = kelvin::sim::test::kDataLoadAddress;
- uint32_t rs2_value = len_or_strides[test];
- uint32_t count = vd_size * num_ops;
- if (has_length) {
- count = std::min(count, rs2_value);
- }
- uint32_t left = count;
- for (int op_num = 0; op_num < num_ops; op_num++) {
- const int n = std::min(vd_size, left);
- if (is_quad) {
- const uint32_t quad_size = vd_size / 4;
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < quad_size; ++j) {
- memory_values[op_num * vd_size + i * quad_size + j] =
- GetSavedMemoryValue<T>(addr +
- (i * quad_size + j) * sizeof(T));
- }
- // Stride increase per quad_size.
- addr += rs2_value * sizeof(T);
- }
- } else {
- for (int i = 0; i < vd_size; ++i) {
- if (is_load) {
- memory_values[op_num * vd_size + i] =
- i < n ? GetDefaultMemoryValue<T>(addr + i * sizeof(T)) : 0;
- } else {
- memory_values[op_num * vd_size + i] =
- i < n ? GetSavedMemoryValue<T>(addr + i * sizeof(T)) : 0;
- }
- }
- left -= n;
- if (has_stride) {
- addr += rs2_value * sizeof(T);
- } else {
- addr += n * sizeof(T);
- }
- }
- }
-
- uint32_t expected_rs1_value = kelvin::sim::test::kDataLoadAddress;
- if (post_increment && count) {
- if (has_length && has_stride) { // .tp
- expected_rs1_value += vd_size * sizeof(T);
- } else if (!has_length && !has_stride && x_variant) { // .p.x
- expected_rs1_value += vd_size * sizeof(T) * num_ops;
- } else if (has_length) { // .lp
- expected_rs1_value += count * sizeof(T);
- } else if (has_stride) { // .sp
- const uint32_t quad_scale = is_quad ? 4 : 1;
- expected_rs1_value += rs2_value * sizeof(T) * num_ops * quad_scale;
- } else { // .p.xx
- expected_rs1_value += rs2_value * sizeof(T);
- }
- }
-
- // Check result
- left = count;
- for (int op_num = 0; op_num < num_ops; op_num++) {
- auto vreg_num = kelvin::sim::test::kVd + op_num;
- auto test_vreg = vreg_[vreg_num];
- auto vreg_span = test_vreg->data_buffer()->Get<T>();
- if (is_load) {
- for (int element_index = 0; element_index < vd_size;
- element_index++) {
- auto vreg_element_index = op_num * vd_size + element_index;
- EXPECT_EQ(memory_values[vreg_element_index],
- vreg_span[element_index])
- << absl::StrCat(name, "[", vreg_element_index, "] != reg[",
- vreg_num, "*", element_index, "]");
- }
- } else { // Store
- const int n = std::min(vd_size, left);
- for (int element_index = 0;
- element_index < vd_size && element_index < n; element_index++) {
- auto total_element_index = op_num * vd_size + element_index;
- EXPECT_EQ(memory_values[total_element_index],
- vreg_span[element_index])
- << absl::StrCat(name, " mem at ", total_element_index,
- " != vreg[", vreg_num, "][", element_index,
- "]");
- }
- left -= n;
- }
- }
-
- if (post_increment) {
- // Check rs1 value.
- auto *reg = state_
- ->GetRegister<kelvin::sim::test::RV32Register>(
- kelvin::sim::test::kRs1Name)
- .first;
- EXPECT_EQ(expected_rs1_value, reg->data_buffer()->Get<uint32_t>()[0])
- << absl::StrCat(name, " post incremented rs1 is incorrect.");
- }
- }
- }
-
- template <typename T>
- void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
- constexpr bool kNoLength = false;
- constexpr bool kLength = true;
- constexpr bool kNoStride = false;
- constexpr bool kStride = true;
- constexpr bool kPostIncrement = true;
- constexpr bool kXVariant = true;
- constexpr bool kNotXVariant = false;
- constexpr bool kNotQuad = false;
-
- const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
-
- for (auto strip_mine : {false, true}) {
- for (auto post_increment : {false, true}) {
- // .x variants.
- auto subname = absl::StrCat(name_with_type, post_increment ? "P" : "",
- "X", strip_mine ? "M" : "");
- MemoryLoadStoreOpTestHelper<T>(subname, kNoLength, kNoStride,
- strip_mine, post_increment, kXVariant,
- is_load, kNotQuad);
- }
- // .xx variants
- for (auto len_stride_post :
- {std::tuple(false, false, true), std::tuple(false, true, false),
- std::tuple(false, true, true), std::tuple(true, false, false),
- std::tuple(true, false, true)}) {
- auto has_length = std::get<0>(len_stride_post);
- auto has_stride = std::get<1>(len_stride_post);
- auto post_increment = std::get<2>(len_stride_post);
- auto subname = absl::StrCat(name_with_type,
- has_length ? "L"
- : has_stride ? "S"
- : "",
- post_increment ? "P" : "", "XX",
- strip_mine ? "M" : "");
- MemoryLoadStoreOpTestHelper<T>(subname, has_length, has_stride,
- strip_mine, post_increment, kNotXVariant,
- is_load, kNotQuad);
- }
-
- // .tp variants.
- auto subname =
- absl::StrCat(name_with_type, "TP", "XX", strip_mine ? "M" : "");
- MemoryLoadStoreOpTestHelper<T>(subname, kLength, kStride, strip_mine,
- kPostIncrement, kNotXVariant, is_load,
- kNotQuad);
- }
- }
-
- template <typename T>
- void StoreQuadOpTestHelper(absl::string_view name) {
- const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
- constexpr bool kNotLength = false;
- constexpr bool kStride = true;
- constexpr bool kNotXVariant = false;
- constexpr bool kNotLoad = false;
- constexpr bool kIsQuad = true;
- for (auto strip_mine : {false, true}) {
- for (auto post_increment : {false, true}) {
- auto subname =
- absl::StrCat(name_with_type, "S", post_increment ? "P" : "", "XX",
- strip_mine ? "M" : "");
- MemoryLoadStoreOpTestHelper<T>(subname, kNotLength, kStride, strip_mine,
- post_increment, kNotXVariant, kNotLoad,
- kIsQuad);
- }
- }
- }
-
- template <typename T1, typename TNext1, typename... TNext>
- void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
- MemoryLoadStoreOpTestHelper<T1>(name, is_load);
- MemoryLoadStoreOpTestHelper<TNext1, TNext...>(name, is_load);
- }
-
- template <typename T1, typename TNext1, typename... TNext>
- void StoreQuadOpTestHelper(absl::string_view name) {
- StoreQuadOpTestHelper<T1>(name);
- StoreQuadOpTestHelper<TNext1, TNext...>(name);
- }
-
- protected:
- template <typename T>
- T GetDefaultMemoryValue(int address) {
- T value = 0;
- uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
- for (int j = 0; j < sizeof(T); j++) {
- ptr[j] = (address + j) & 0xff;
- }
- return value;
- }
-
- template <typename T>
- T GetSavedMemoryValue(int address) {
- auto *db = state_->db_factory()->Allocate<T>(1);
- memory_->Load(address, db, nullptr, nullptr);
- T data = db->template Get<T>(0);
- db->DecRef();
- return data;
- }
-};
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VLd) {
- MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VLd",
- /*is_load=*/true);
-}
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VSt) {
- MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VSt",
- /*is_load=*/false);
-}
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VStQ) {
- StoreQuadOpTestHelper<int8_t, int16_t, int32_t>("VStQ");
-}
-
-class KelvinGetVlInstructionTest : public KelvinVectorInstructionsTest {
- public:
- template <typename T>
- void GetVlTestHelper() {
- constexpr char kRdName[] = "x8";
- constexpr uint32_t kMaxVlenInBytes = kelvin::sim::kVectorLengthInBits / 8;
- auto instruction = CreateInstruction();
- AppendRegisterOperands(
- instruction.get(),
- {kelvin::sim::test::kRs1Name, kelvin::sim::test::kRs2Name}, {kRdName});
- for (auto strip_mine : {false, true}) {
- for (auto is_rs1 : {false, true}) {
- for (auto is_rs2 : {false, true}) {
- uint32_t rs1_value = RandomValue();
- uint32_t rs2_value = RandomValue();
- SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, rs1_value},
- {kelvin::sim::test::kRs2Name, rs2_value},
- {kRdName, UINT32_MAX}});
- instruction->set_semantic_function(
- absl::bind_front(&KelvinGetVl<T>, strip_mine, is_rs1, is_rs2));
- uint32_t expected_vlen =
- kMaxVlenInBytes / sizeof(T) * (strip_mine ? 4 : 1);
- if (is_rs1) {
- expected_vlen = std::min(expected_vlen, rs1_value);
- }
- if (is_rs2) {
- expected_vlen = std::min(expected_vlen, rs2_value);
- }
- // Execute instruction.
- instruction->Execute(nullptr);
- EXPECT_EQ(xreg_[8]->data_buffer()->Get<uint32_t>(0), expected_vlen)
- << "Test failed with type "
- << (sizeof(T) == 4 ? "W" : (sizeof(T) == 2 ? "H" : "B"))
- << ", strip_mine: " << strip_mine << ", rs1_set: " << is_rs1
- << ", rs2_set: " << is_rs2;
- }
- }
- }
- }
-
- template <typename T1, typename TNext1, typename... TNext>
- void GetVlTestHelper() {
- GetVlTestHelper<T1>();
- GetVlTestHelper<TNext1, TNext...>();
- }
-
- protected:
- // Create a random value in the valid range for the type.
- uint32_t RandomValue() {
- return absl::Uniform(absl::IntervalClosed, bitgen_,
- std::numeric_limits<uint32_t>::lowest(),
- std::numeric_limits<uint32_t>::max());
- }
-};
-
-TEST_F(KelvinGetVlInstructionTest, GetVl) {
- GetVlTestHelper<int8_t, int16_t, int32_t>();
-}
-
} // namespace
diff --git a/sim/test/kelvin_vector_instructions_test_base.h b/sim/test/kelvin_vector_instructions_test_base.h
index 9afde0b..7335a28 100644
--- a/sim/test/kelvin_vector_instructions_test_base.h
+++ b/sim/test/kelvin_vector_instructions_test_base.h
@@ -76,6 +76,23 @@
}
}
+ template <typename T>
+ absl::string_view KelvinTestTypeSuffix() {
+ absl::string_view type_suffix = "Unknown";
+ switch (sizeof(T)) {
+ case 4:
+ type_suffix = "W";
+ break;
+ case 2:
+ type_suffix = "H";
+ break;
+ case 1:
+ type_suffix = "B";
+ break;
+ }
+ return type_suffix;
+ }
+
template <typename Vd, typename Vs1, typename Vs2>
static std::pair<Vs1, Vs2> CommonBinaryOpArgsGetter(
int num_ops, int op_num, int dest_reg_sub_index, int element_index,
diff --git a/sim/test/kelvin_vector_memory_instructions_test.cc b/sim/test/kelvin_vector_memory_instructions_test.cc
new file mode 100644
index 0000000..4b4fc82
--- /dev/null
+++ b/sim/test/kelvin_vector_memory_instructions_test.cc
@@ -0,0 +1,420 @@
+#include "sim/kelvin_vector_memory_instructions.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "sim/test/kelvin_vector_instructions_test_base.h"
+#include "googletest/include/gtest/gtest.h"
+#include "absl/functional/bind_front.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/generic/instruction.h"
+
+// This file contains the tests for testing kelvin vector memory instructions.
+
+namespace {
+
+using mpact::sim::generic::Instruction;
+
+// Semantic functions.
+using kelvin::sim::KelvinGetVl;
+using kelvin::sim::KelvinVLd;
+using kelvin::sim::KelvinVLdRegWrite;
+using kelvin::sim::KelvinVSt;
+using kelvin::sim::KelvinVStQ;
+
+class KelvinVectorMemoryInstructionsTest
+ : public kelvin::sim::test::KelvinVectorInstructionsTestBase {
+ public:
+ template <typename T>
+ void MemoryLoadStoreOpTestHelper(absl::string_view name, bool has_length,
+ bool has_stride, bool strip_mine,
+ bool post_increment, bool x_variant,
+ bool is_load, bool is_quad) {
+ InstructionPtr child_instruction(
+ new Instruction(next_instruction_address_, state_),
+ [](Instruction *inst) { inst->DecRef(); });
+ child_instruction->set_size(4);
+ auto instruction = CreateInstruction();
+
+ if (is_load) {
+ child_instruction->set_semantic_function(
+ absl::bind_front(&KelvinVLdRegWrite<T>, strip_mine));
+ instruction->set_semantic_function(
+ absl::bind_front(&KelvinVLd<T>, has_length, has_stride, strip_mine));
+ instruction->AppendChild(child_instruction.get());
+ } else {
+ if (is_quad) {
+ instruction->set_semantic_function(
+ absl::bind_front(&KelvinVStQ<T>, strip_mine));
+ } else {
+ instruction->set_semantic_function(absl::bind_front(
+ &KelvinVSt<T>, has_length, has_stride, strip_mine));
+ }
+ }
+
+ // Setup source and child instruction operands.
+ const uint32_t num_ops = strip_mine ? 4 : 1;
+ if (is_load) {
+ AppendVectorRegisterOperands(
+ child_instruction.get(), num_ops, 1 /* src1_widen_factor */, {}, {},
+ false /* widen_dst */, {kelvin::sim::test::kVd});
+ } else { // Store
+ AppendVectorRegisterOperands(
+ instruction.get(), num_ops, 1 /* src1_widen_factor */,
+ kelvin::sim::test::kVd, {}, false /* widen_dst */, {});
+ }
+ AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name},
+ {});
+ if (!x_variant) {
+ AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs2Name},
+ {});
+ }
+
+ if (post_increment) {
+ AppendRegisterOperands(instruction.get(), {},
+ {kelvin::sim::test::kRs1Name});
+ }
+
+ // x variant can't have length or stride fields.
+ if (x_variant && (has_length || has_stride)) {
+ GTEST_FAIL();
+ }
+
+ // xx variant can't have no length, no stride, and no post_increment
+ // encoding
+ if (!x_variant && !has_length && !has_stride && !post_increment) {
+ GTEST_FAIL();
+ }
+
+ // length and stride fields can't coexist without post_increment
+ if (has_length && has_stride && !post_increment) {
+ GTEST_FAIL();
+ }
+
+ // Quad store need to have stride specified and no length
+ if (is_quad && is_load) {
+ GTEST_FAIL();
+ }
+ if ((is_quad && has_length) || (is_quad && !has_stride)) {
+ GTEST_FAIL();
+ }
+ const uint32_t vector_length_in_bytes = state_->vector_length() / 8;
+ const uint32_t vd_size = vector_length_in_bytes / sizeof(T);
+ const uint32_t len_or_strides[] = {0, 1, vd_size - 1,
+ vd_size, 2 * vd_size, 4 * vd_size};
+
+ // Check with different values for length and stride if applicable.
+ for (int test = 0;
+ test < (has_length || has_stride ? std::size(len_or_strides) : 1);
+ test++) {
+ // Store stride can't be smaller than vd_size
+ if ((is_quad && len_or_strides[test] < vd_size / 4) ||
+ (!is_load && has_stride && len_or_strides[test] < vd_size)) {
+ continue;
+ }
+ // Set input register values.
+ SetRegisterValues<uint32_t>(
+ {{kelvin::sim::test::kRs1Name, kelvin::sim::test::kDataLoadAddress}});
+
+ if (!x_variant) {
+ SetRegisterValues<uint32_t>(
+ {{kelvin::sim::test::kRs2Name, len_or_strides[test]}});
+ }
+
+ // Fill vector register(s) with random values.
+ std::vector<T> vd_value(vector_length_in_bytes / sizeof(T) * num_ops);
+ auto vd_span = absl::Span<T>(vd_value);
+ FillArrayWithRandomValues<T>(vd_span);
+ for (int i = 0; i < num_ops; i++) {
+ auto vd_name = absl::StrCat("v", kelvin::sim::test::kVd + i);
+ SetVectorRegisterValues<T>(
+ {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+ }
+
+ // Execute instruction.
+ instruction->Execute();
+
+ // Compute memory values. For load test it is the expected output; for
+ // store test it is the actual output.
+ std::vector<T> memory_values(vd_size * num_ops);
+ uint32_t addr = kelvin::sim::test::kDataLoadAddress;
+ uint32_t rs2_value = len_or_strides[test];
+ uint32_t count = vd_size * num_ops;
+ if (has_length) {
+ count = std::min(count, rs2_value);
+ }
+ uint32_t left = count;
+ for (int op_num = 0; op_num < num_ops; op_num++) {
+ const int n = std::min(vd_size, left);
+ if (is_quad) {
+ const uint32_t quad_size = vd_size / 4;
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < quad_size; ++j) {
+ memory_values[op_num * vd_size + i * quad_size + j] =
+ GetSavedMemoryValue<T>(addr +
+ (i * quad_size + j) * sizeof(T));
+ }
+ // Stride increase per quad_size.
+ addr += rs2_value * sizeof(T);
+ }
+ } else {
+ for (int i = 0; i < vd_size; ++i) {
+ if (is_load) {
+ memory_values[op_num * vd_size + i] =
+ i < n ? GetDefaultMemoryValue<T>(addr + i * sizeof(T)) : 0;
+ } else {
+ memory_values[op_num * vd_size + i] =
+ i < n ? GetSavedMemoryValue<T>(addr + i * sizeof(T)) : 0;
+ }
+ }
+ left -= n;
+ if (has_stride) {
+ addr += rs2_value * sizeof(T);
+ } else {
+ addr += n * sizeof(T);
+ }
+ }
+ }
+
+ uint32_t expected_rs1_value = kelvin::sim::test::kDataLoadAddress;
+ if (post_increment && count) {
+ if (has_length && has_stride) { // .tp
+ expected_rs1_value += vd_size * sizeof(T);
+ } else if (!has_length && !has_stride && x_variant) { // .p.x
+ expected_rs1_value += vd_size * sizeof(T) * num_ops;
+ } else if (has_length) { // .lp
+ expected_rs1_value += count * sizeof(T);
+ } else if (has_stride) { // .sp
+ const uint32_t quad_scale = is_quad ? 4 : 1;
+ expected_rs1_value += rs2_value * sizeof(T) * num_ops * quad_scale;
+ } else { // .p.xx
+ expected_rs1_value += rs2_value * sizeof(T);
+ }
+ }
+
+ // Check result
+ left = count;
+ for (int op_num = 0; op_num < num_ops; op_num++) {
+ auto vreg_num = kelvin::sim::test::kVd + op_num;
+ auto test_vreg = vreg_[vreg_num];
+ auto vreg_span = test_vreg->data_buffer()->Get<T>();
+ if (is_load) {
+ for (int element_index = 0; element_index < vd_size;
+ element_index++) {
+ auto vreg_element_index = op_num * vd_size + element_index;
+ EXPECT_EQ(memory_values[vreg_element_index],
+ vreg_span[element_index])
+ << absl::StrCat(name, "[", vreg_element_index, "] != reg[",
+ vreg_num, "*", element_index, "]");
+ }
+ } else { // Store
+ const int n = std::min(vd_size, left);
+ for (int element_index = 0;
+ element_index < vd_size && element_index < n; element_index++) {
+ auto total_element_index = op_num * vd_size + element_index;
+ EXPECT_EQ(memory_values[total_element_index],
+ vreg_span[element_index])
+ << absl::StrCat(name, " mem at ", total_element_index,
+ " != vreg[", vreg_num, "][", element_index,
+ "]");
+ }
+ left -= n;
+ }
+ }
+
+ if (post_increment) {
+ // Check rs1 value.
+ auto *reg = state_
+ ->GetRegister<kelvin::sim::test::RV32Register>(
+ kelvin::sim::test::kRs1Name)
+ .first;
+ EXPECT_EQ(expected_rs1_value, reg->data_buffer()->Get<uint32_t>()[0])
+ << absl::StrCat(name, " post incremented rs1 is incorrect.");
+ }
+ }
+ }
+
+ template <typename T>
+ void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
+ constexpr bool kNoLength = false;
+ constexpr bool kLength = true;
+ constexpr bool kNoStride = false;
+ constexpr bool kStride = true;
+ constexpr bool kPostIncrement = true;
+ constexpr bool kXVariant = true;
+ constexpr bool kNotXVariant = false;
+ constexpr bool kNotQuad = false;
+
+ const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
+
+ for (auto strip_mine : {false, true}) {
+ for (auto post_increment : {false, true}) {
+ // .x variants.
+ auto subname = absl::StrCat(name_with_type, post_increment ? "P" : "",
+ "X", strip_mine ? "M" : "");
+ MemoryLoadStoreOpTestHelper<T>(subname, kNoLength, kNoStride,
+ strip_mine, post_increment, kXVariant,
+ is_load, kNotQuad);
+ }
+ // .xx variants
+ for (auto len_stride_post :
+ {std::tuple(false, false, true), std::tuple(false, true, false),
+ std::tuple(false, true, true), std::tuple(true, false, false),
+ std::tuple(true, false, true)}) {
+ auto has_length = std::get<0>(len_stride_post);
+ auto has_stride = std::get<1>(len_stride_post);
+ auto post_increment = std::get<2>(len_stride_post);
+ auto subname = absl::StrCat(name_with_type,
+ has_length ? "L"
+ : has_stride ? "S"
+ : "",
+ post_increment ? "P" : "", "XX",
+ strip_mine ? "M" : "");
+ MemoryLoadStoreOpTestHelper<T>(subname, has_length, has_stride,
+ strip_mine, post_increment, kNotXVariant,
+ is_load, kNotQuad);
+ }
+
+ // .tp variants.
+ auto subname =
+ absl::StrCat(name_with_type, "TP", "XX", strip_mine ? "M" : "");
+ MemoryLoadStoreOpTestHelper<T>(subname, kLength, kStride, strip_mine,
+ kPostIncrement, kNotXVariant, is_load,
+ kNotQuad);
+ }
+ }
+
+ template <typename T>
+ void StoreQuadOpTestHelper(absl::string_view name) {
+ const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
+ constexpr bool kNotLength = false;
+ constexpr bool kStride = true;
+ constexpr bool kNotXVariant = false;
+ constexpr bool kNotLoad = false;
+ constexpr bool kIsQuad = true;
+ for (auto strip_mine : {false, true}) {
+ for (auto post_increment : {false, true}) {
+ auto subname =
+ absl::StrCat(name_with_type, "S", post_increment ? "P" : "", "XX",
+ strip_mine ? "M" : "");
+ MemoryLoadStoreOpTestHelper<T>(subname, kNotLength, kStride, strip_mine,
+ post_increment, kNotXVariant, kNotLoad,
+ kIsQuad);
+ }
+ }
+ }
+
+ template <typename T1, typename TNext1, typename... TNext>
+ void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
+ MemoryLoadStoreOpTestHelper<T1>(name, is_load);
+ MemoryLoadStoreOpTestHelper<TNext1, TNext...>(name, is_load);
+ }
+
+ template <typename T1, typename TNext1, typename... TNext>
+ void StoreQuadOpTestHelper(absl::string_view name) {
+ StoreQuadOpTestHelper<T1>(name);
+ StoreQuadOpTestHelper<TNext1, TNext...>(name);
+ }
+
+ protected:
+ template <typename T>
+ T GetDefaultMemoryValue(int address) {
+ T value = 0;
+ uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
+ for (int j = 0; j < sizeof(T); j++) {
+ ptr[j] = (address + j) & 0xff;
+ }
+ return value;
+ }
+
+ template <typename T>
+ T GetSavedMemoryValue(int address) {
+ auto *db = state_->db_factory()->Allocate<T>(1);
+ memory_->Load(address, db, nullptr, nullptr);
+ T data = db->template Get<T>(0);
+ db->DecRef();
+ return data;
+ }
+};
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VLd) {
+ MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VLd",
+ /*is_load=*/true);
+}
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VSt) {
+ MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VSt",
+ /*is_load=*/false);
+}
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VStQ) {
+ StoreQuadOpTestHelper<int8_t, int16_t, int32_t>("VStQ");
+}
+
+class KelvinGetVlInstructionTest
+ : public kelvin::sim::test::KelvinVectorInstructionsTestBase {
+ public:
+ template <typename T>
+ void GetVlTestHelper() {
+ constexpr char kRdName[] = "x8";
+ constexpr uint32_t kMaxVlenInBytes = kelvin::sim::kVectorLengthInBits / 8;
+ auto instruction = CreateInstruction();
+ AppendRegisterOperands(
+ instruction.get(),
+ {kelvin::sim::test::kRs1Name, kelvin::sim::test::kRs2Name}, {kRdName});
+ for (auto strip_mine : {false, true}) {
+ for (auto is_rs1 : {false, true}) {
+ for (auto is_rs2 : {false, true}) {
+ uint32_t rs1_value = RandomValue();
+ uint32_t rs2_value = RandomValue();
+ SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, rs1_value},
+ {kelvin::sim::test::kRs2Name, rs2_value},
+ {kRdName, UINT32_MAX}});
+ instruction->set_semantic_function(
+ absl::bind_front(&KelvinGetVl<T>, strip_mine, is_rs1, is_rs2));
+ uint32_t expected_vlen =
+ kMaxVlenInBytes / sizeof(T) * (strip_mine ? 4 : 1);
+ if (is_rs1) {
+ expected_vlen = std::min(expected_vlen, rs1_value);
+ }
+ if (is_rs2) {
+ expected_vlen = std::min(expected_vlen, rs2_value);
+ }
+ // Execute instruction.
+ instruction->Execute(nullptr);
+ EXPECT_EQ(xreg_[8]->data_buffer()->Get<uint32_t>(0), expected_vlen)
+ << "Test failed with type "
+ << (sizeof(T) == 4 ? "W" : (sizeof(T) == 2 ? "H" : "B"))
+ << ", strip_mine: " << strip_mine << ", rs1_set: " << is_rs1
+ << ", rs2_set: " << is_rs2;
+ }
+ }
+ }
+ }
+
+ template <typename T1, typename TNext1, typename... TNext>
+ void GetVlTestHelper() {
+ GetVlTestHelper<T1>();
+ GetVlTestHelper<TNext1, TNext...>();
+ }
+
+ protected:
+ // Create a random value in the valid range for the type.
+ uint32_t RandomValue() {
+ return absl::Uniform(absl::IntervalClosed, bitgen_,
+ std::numeric_limits<uint32_t>::lowest(),
+ std::numeric_limits<uint32_t>::max());
+ }
+};
+
+TEST_F(KelvinGetVlInstructionTest, GetVl) {
+ GetVlTestHelper<int8_t, int16_t, int32_t>();
+}
+
+} // namespace