Add vector duplicate instruction support.
PiperOrigin-RevId: 558295664
diff --git a/sim/kelvin_memory.bin_fmt b/sim/kelvin_memory.bin_fmt
index 37b8154..f3105d6 100644
--- a/sim/kelvin_memory.bin_fmt
+++ b/sim/kelvin_memory.bin_fmt
@@ -113,6 +113,14 @@
vstq_h_sp_xx_m : KelvinV2ArgsType : func2 == 0b01'1110, vs2 != 0, sz == 0b01, m == 0b01, func1 == 0b111, form == 0b11;
vstq_w_sp_xx_m : KelvinV2ArgsType : func2 == 0b01'1110, vs2 != 0, sz == 0b10, m == 0b01, func1 == 0b111, form == 0b11;
+ // vdup
+ vdup_b_x : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b00, m == 0b00, func1 == 0b111, form == 0b11;
+ vdup_h_x : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b01, m == 0b00, func1 == 0b111, form == 0b11;
+ vdup_w_x : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b10, m == 0b00, func1 == 0b111, form == 0b11;
+ vdup_b_x_m : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b00, m == 0b01, func1 == 0b111, form == 0b11;
+ vdup_h_x_m : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b01, m == 0b01, func1 == 0b111, form == 0b11;
+ vdup_w_x_m : KelvinV2ArgsType : func2 == 0b01'0000, vs1 == 0, sz == 0b10, m == 0b01, func1 == 0b111, form == 0b11;
+
// vcget
vcget : KelvinV2ArgsType : func2 == 0b01'0100, vs2 == 0, vs1 == 0, vd == 48, func1 == 0b111, form == 0b11;
diff --git a/sim/kelvin_memory.isa b/sim/kelvin_memory.isa
index 88a380e..bc83d3e 100644
--- a/sim/kelvin_memory.isa
+++ b/sim/kelvin_memory.isa
@@ -481,6 +481,24 @@
vstq_w_sp_xx_m{: vd, vs1, vs2 : vs1},
disasm: "vstq.w.s.xx.m", "%vd, %vs1, %vs2",
semfunc: "absl::bind_front(&KelvinVStQ<int32_t>, /*strip_mine*/ true)";
+ vdup_b_x{: vs2 : vd },
+ disasm: "vdup.b.x", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int8_t>, /*strip_mine*/ false)";
+ vdup_h_x{: vs2 : vd },
+ disasm: "vdup.h.x", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int16_t>, /*strip_mine*/ false)";
+ vdup_w_x{: vs2 : vd },
+ disasm: "vdup.w.x", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int32_t>, /*strip_mine*/ false)";
+ vdup_b_x_m{: vs2 : vd },
+ disasm: "vdup.b.x.m", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int8_t>, /*strip_mine*/ true)";
+ vdup_h_x_m{: vs2 : vd },
+ disasm: "vdup.h.x.m", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int16_t>, /*strip_mine*/ true)";
+ vdup_w_x_m{: vs2 : vd },
+ disasm: "vdup.w.x.m", "%vd, %vs2",
+ semfunc: "absl::bind_front(&KelvinVDup<int32_t>, /*strip_mine*/ true)";
// vcget
vcget{: : vd},
diff --git a/sim/kelvin_vector_memory_instructions.cc b/sim/kelvin_vector_memory_instructions.cc
index c595a20..67c0966 100644
--- a/sim/kelvin_vector_memory_instructions.cc
+++ b/sim/kelvin_vector_memory_instructions.cc
@@ -257,6 +257,34 @@
template void KelvinVSt<int16_t>(bool, bool, bool, Instruction *);
template void KelvinVSt<int32_t>(bool, bool, bool, Instruction *);
+// Duplicate a scalar value into a vector register.
+template <typename T>
+void KelvinVDup(bool strip_mine, Instruction *inst) {
+ auto *state = static_cast<KelvinState *>(inst->state());
+ const int vector_size_in_bytes = state->vector_length() / 8;
+ const uint32_t elts_per_register = vector_size_in_bytes / sizeof(T);
+ const auto num_ops = strip_mine ? 4 : 1;
+
+ // Gets destination register and scalar value.
+ auto *vd = static_cast<RV32VectorDestinationOperand *>(inst->Destination(0));
+ auto value = GetInstructionSource<T>(inst, 0);
+
+ // Fill destination buffer and write to register.
+ for (int op_index = 0; op_index < num_ops; ++op_index) {
+ DataBuffer *dest_db = vd->AllocateDataBuffer(op_index);
+ absl::Span<T> dest_span = dest_db->template Get<T>();
+ for (int dst_element_index = 0; dst_element_index < elts_per_register;
+ ++dst_element_index) {
+ dest_span[dst_element_index] = value;
+ }
+ dest_db->Submit();
+ }
+}
+
+template void KelvinVDup<int8_t>(bool, Instruction *);
+template void KelvinVDup<int16_t>(bool, Instruction *);
+template void KelvinVDup<int32_t>(bool, Instruction *);
+
template <typename T>
void KelvinVStQ(bool strip_mine, Instruction *inst) {
VectorStoreHelper<T>(/*has_length=*/false, /*has_stride=*/true, strip_mine,
diff --git a/sim/kelvin_vector_memory_instructions.h b/sim/kelvin_vector_memory_instructions.h
index ff53da4..19f73db 100644
--- a/sim/kelvin_vector_memory_instructions.h
+++ b/sim/kelvin_vector_memory_instructions.h
@@ -19,6 +19,9 @@
Instruction *inst);
template <typename T>
+void KelvinVDup(bool strip_mine, Instruction *inst);
+
+template <typename T>
void KelvinVStQ(bool strip_mine, Instruction *inst);
template <typename T>
diff --git a/sim/test/kelvin_vector_memory_instructions_test.cc b/sim/test/kelvin_vector_memory_instructions_test.cc
index 49d058d..82d986c 100644
--- a/sim/test/kelvin_vector_memory_instructions_test.cc
+++ b/sim/test/kelvin_vector_memory_instructions_test.cc
@@ -27,6 +27,7 @@
using kelvin::sim::KelvinADwInit;
using kelvin::sim::KelvinGetVl;
using kelvin::sim::KelvinVcGet;
+using kelvin::sim::KelvinVDup;
using kelvin::sim::KelvinVLd;
using kelvin::sim::KelvinVLdRegWrite;
using kelvin::sim::KelvinVSt;
@@ -339,6 +340,62 @@
StoreQuadOpTestHelper<TNext1, TNext...>(name);
}
+ template <typename T>
+ void VdupOpTestHelper() {
+ for (auto strip_mine : {false, true}) {
+ // Setup instruction and its operands.
+ auto instruction = CreateInstruction();
+ const auto name_with_type =
+ absl::StrCat('VDup', KelvinTestTypeSuffix<T>());
+ auto subname = absl::StrCat(name_with_type, strip_mine ? "M" : "");
+ const uint32_t num_ops = strip_mine ? 4 : 1;
+ AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name},
+ {});
+ AppendVectorRegisterOperands(
+ instruction.get(), num_ops, 1 /* src1_widen_factor */, {}, {},
+ false /* widen_dst */, {kelvin::sim::test::kVd});
+
+ instruction->set_semantic_function(
+ absl::bind_front(&KelvinVDup<T>, strip_mine));
+
+ // Sets operand scalar register value.
+ constexpr uint32_t value = 0x12345678;
+ SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, value}});
+
+ // Fills vector registers with random values.
+ const uint32_t vector_length_in_bytes = state_->vector_length() / 8;
+ const uint32_t vd_size = vector_length_in_bytes / sizeof(T);
+ 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)}});
+ }
+
+ instruction->Execute();
+
+ // Verifies all register elements is equal to scalar value.
+ 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>();
+ for (int element_index = 0; element_index < vd_size; element_index++) {
+ EXPECT_EQ(static_cast<T>(value), vreg_span[element_index])
+ << absl::StrCat(subname, ": vreg[", vreg_num, "][", element_index,
+ "] mismatch");
+ }
+ }
+ }
+ }
+
+ template <typename T1, typename TNext1, typename... TNext>
+ void VdupOpTestHelper() {
+ VdupOpTestHelper<T1>();
+ VdupOpTestHelper<TNext1, TNext...>();
+ }
+
protected:
template <typename T>
T GetDefaultMemoryValue(int address) {
@@ -374,6 +431,10 @@
StoreQuadOpTestHelper<int8_t, int16_t, int32_t>("VStQ");
}
+TEST_F(KelvinVectorMemoryInstructionsTest, VDup) {
+ VdupOpTestHelper<int8_t, int16_t, int32_t>();
+}
+
class KelvinGetVlInstructionTest
: public kelvin::sim::test::KelvinVectorInstructionsTestBase {
public: