Add vmv.s.x test
This test saves a register into vd[0] element.
This is important for the vector reduction operations (vred*)
where the result of the operation (e.g. sum of all elements)
is placed into the 0th element of vd[0].
The complication of this operation when the scalar reg value (which is
always e32) will be saved over a vector element of a different SEW.
For e32 > SEW, there must be proper truncation, and for e32 < SEW
there must be proper sign extension.
Due to this having a unique direction (scalar to vector), creating a
custom test instead of the typical softrvv approach.
Note: there is a inverse operation which places vd[0] back into
a scalar register called `vmv.x.s`, and will be added in a separate
commit.
Change-Id: Ib35b3f56d2ca3e05ea0e98705bf90ebe59a5ff0e
diff --git a/softrvv/include/softrvv.h b/softrvv/include/softrvv.h
index 5595634..d82e7a0 100644
--- a/softrvv/include/softrvv.h
+++ b/softrvv/include/softrvv.h
@@ -10,20 +10,21 @@
#include "softrvv_vdiv.h"
#include "softrvv_vmax.h"
#include "softrvv_vmin.h"
-#include "softrvv_vmul_vmulh.h"
#include "softrvv_vmseq.h"
-#include "softrvv_vmsgt.h"
#include "softrvv_vmsge.h"
+#include "softrvv_vmsgt.h"
#include "softrvv_vmsle.h"
#include "softrvv_vmslt.h"
#include "softrvv_vmsne.h"
+#include "softrvv_vmul_vmulh.h"
+#include "softrvv_vmv_s_x.h"
#include "softrvv_vnsra.h"
#include "softrvv_vnsrl.h"
#include "softrvv_vor.h"
#include "softrvv_vrem.h"
#include "softrvv_vsext_vzext.h"
-#include "softrvv_vsra.h"
#include "softrvv_vsll.h"
+#include "softrvv_vsra.h"
#include "softrvv_vsrl.h"
#include "softrvv_vsub.h"
#include "softrvv_vwadd.h"
diff --git a/softrvv/include/softrvv_vmv_s_x.h b/softrvv/include/softrvv_vmv_s_x.h
new file mode 100644
index 0000000..5e249a0
--- /dev/null
+++ b/softrvv/include/softrvv_vmv_s_x.h
@@ -0,0 +1,15 @@
+#ifndef SOFTRVV_VMV_S_X_H
+#define SOFTRVV_VMV_S_X_H
+
+#include <stddef.h>
+
+namespace softrvv {
+
+template <typename T1, typename T2>
+void vmv_s_x(T1 *vd, const T2 *rs1) {
+ vd[0] = static_cast<T1>(*rs1);
+}
+
+} // namespace softrvv
+
+#endif // SOFTRVV_VMV_S_X_H
diff --git a/softrvv/tests/CMakeLists.txt b/softrvv/tests/CMakeLists.txt
index fd87980..62cbc9a 100644
--- a/softrvv/tests/CMakeLists.txt
+++ b/softrvv/tests/CMakeLists.txt
@@ -292,3 +292,15 @@
LINKOPTS
-Xlinker --defsym=__itcm_length__=128K
)
+
+vec_cc_test(
+ NAME
+ softrvv_vmv_s_x
+ SRCS
+ softrvv_vmv_s_x_test.cpp
+ DEPS
+ softrvv
+ LINKOPTS
+ -Xlinker --defsym=__itcm_length__=128K
+)
+
diff --git a/softrvv/tests/softrvv_vmv_s_x_test.cpp b/softrvv/tests/softrvv_vmv_s_x_test.cpp
new file mode 100644
index 0000000..fd10f46
--- /dev/null
+++ b/softrvv/tests/softrvv_vmv_s_x_test.cpp
@@ -0,0 +1,55 @@
+#include <limits.h>
+#include <riscv_vector.h>
+#include <springbok.h>
+#include <stdio.h>
+
+#include "pw_unit_test/framework.h"
+#include "softrvv.h"
+
+namespace softrvv_vand_test {
+namespace {
+
+int8_t dest_e8[] = {-5, 3, 0};
+int16_t dest_e16[] = {-5, 3, 0};
+int32_t dest_e32[] = {-5, 3, 0};
+
+// register value will truncate and replace vd[0]
+int32_t rs1[] = {INT32_MIN, -1, 1, INT32_MAX};
+
+const uint32_t kAVL = sizeof(dest_e8) / sizeof(dest_e8[0]);
+const int32_t num_vx_tests = sizeof(rs1) / sizeof(rs1[0]);
+
+int8_t ref_vx_e8[num_vx_tests][kAVL] = {{static_cast<int8_t>(INT32_MIN), 3, 0},
+ {-1, 3, 0},
+ {1, 3, 0},
+ {static_cast<int8_t>(INT32_MAX), 3, 0}};
+
+int16_t ref_vx_e16[num_vx_tests][kAVL] = {
+ {static_cast<int16_t>(INT32_MIN), 3, 0},
+ {-1, 3, 0},
+ {1, 3, 0},
+ {static_cast<int16_t>(INT32_MAX), 3, 0}};
+
+int32_t ref_vx_e32[num_vx_tests][kAVL] = {
+ {INT32_MIN, 3, 0}, {-1, 3, 0}, {1, 3, 0}, {INT32_MAX, 3, 0}};
+
+class SoftRvvVmvsxTest : public ::testing::Test {
+ protected:
+ void SetUp() override {}
+};
+
+TEST_F(SoftRvvVmvsxTest, S_X) {
+ for (int32_t i = 0; i < num_vx_tests; i++) {
+ softrvv::vmv_s_x<int8_t, int32_t>(dest_e8, &rs1[i]);
+ ASSERT_EQ(memcmp(dest_e8, &ref_vx_e8[i], sizeof(dest_e8)), 0);
+
+ softrvv::vmv_s_x<int16_t, int32_t>(dest_e16, &rs1[i]);
+ ASSERT_EQ(memcmp(dest_e16, &ref_vx_e16[i], sizeof(dest_e16)), 0);
+
+ softrvv::vmv_s_x<int32_t, int32_t>(dest_e32, &rs1[i]);
+ ASSERT_EQ(memcmp(dest_e32, &ref_vx_e32[i], sizeof(dest_e32)), 0);
+ }
+}
+
+} // namespace
+} // namespace softrvv_vand_test
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 58a22df..fc3db8d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -387,6 +387,17 @@
vec_cc_test(
NAME
+ vmv_s_x_test
+ SRCS
+ vmv_s_x_test.cpp
+ LINKOPTS
+ -Xlinker --defsym=__itcm_length__=128K
+ TIMEOUT
+ 40
+)
+
+vec_cc_test(
+ NAME
vlnr_vsnr_test
SRCS
vlnr_vsnr_test.cpp
diff --git a/tests/vmv_s_x_test.cpp b/tests/vmv_s_x_test.cpp
new file mode 100644
index 0000000..f7993cc
--- /dev/null
+++ b/tests/vmv_s_x_test.cpp
@@ -0,0 +1,123 @@
+#include <limits.h>
+#include <riscv_vector.h>
+#include <springbok.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <bit>
+#include <tuple>
+#include <type_traits>
+
+#include "pw_unit_test/framework.h"
+#include "test_v_helpers.h"
+
+namespace vmv_s_x_test {
+namespace {
+
+using namespace test_v_helpers;
+
+uint8_t test_vector_1[MAXVL_BYTES];
+uint8_t reference_vector_1[MAXVL_BYTES];
+
+class VmvsxTest : public ::testing::Test {
+ protected:
+ void SetUp() override { zero_vector_registers(); }
+ void TearDown() override { zero_vector_registers(); }
+};
+
+template <typename T>
+void vmv_s_x_test(const int32_t test_value) {
+ for (int lmul = LMUL_M1; lmul <= LMUL_M8; lmul++) {
+ // TODO(gkielian): increase upper bound to LMUL_MF2 after support is added
+ // TODO(gkielian): skip lmul==4 (unused) on adding fractional lmul to range
+
+ for (int32_t i = 0; i < AVL_COUNT; i++) {
+ int32_t avl = AVLS[i];
+ int32_t vlmax;
+ int32_t vl;
+
+ // reset vector elements to zero and perform appropriate vsetvl
+ std::tie(vlmax, vl) = vector_test_setup<T>(
+ static_cast<VLMUL>(lmul), avl, {test_vector_1, reference_vector_1});
+ if (avl > vlmax) {
+ continue;
+ }
+
+ // recast test and ref vector to appropriate sew
+ T *test_ptr_vec = reinterpret_cast<T *>(test_vector_1);
+ T *ref_ptr_vec = reinterpret_cast<T *>(reference_vector_1);
+
+ // populate test and ref vectors
+ fill_random_vector<T>(ref_ptr_vec, avl);
+
+ // load test_value to scalar register
+ __asm__ volatile("lw x5, (%0)" : : "r"(&test_value));
+
+ switch (sizeof(T)) {
+ case sizeof(int8_t):
+ __asm__ volatile("vle8.v v8, (%0)" : : "r"(ref_ptr_vec));
+ break;
+ case sizeof(int16_t):
+ __asm__ volatile("vle16.v v8, (%0)" : : "r"(ref_ptr_vec));
+ break;
+ case sizeof(int32_t):
+ __asm__ volatile("vle32.v v8, (%0)" : : "r"(ref_ptr_vec));
+ break;
+ default:
+ EXPECT_TRUE(false);
+ break;
+ }
+
+ // load value of x5 into v8[0], truncating x5 to fit into the current SEW
+ __asm__ volatile("vmv.s.x v8, x5");
+
+ switch (sizeof(T)) {
+ case sizeof(int8_t):
+ __asm__ volatile("vse8.v v8, (%0)" : : "r"(test_ptr_vec));
+ break;
+ case sizeof(int16_t):
+ __asm__ volatile("vse16.v v8, (%0)" : : "r"(test_ptr_vec));
+ break;
+ case sizeof(int32_t):
+ __asm__ volatile("vse32.v v8, (%0)" : : "r"(test_ptr_vec));
+ break;
+ default:
+ EXPECT_TRUE(false);
+ break;
+ }
+
+ // perform operations on reference vector
+ ref_ptr_vec[0] = static_cast<T>(test_value);
+
+ assert_vec_elem_eq<T>(vlmax, test_ptr_vec, ref_ptr_vec);
+ }
+ }
+}
+
+TEST_F(VmvsxTest, vmv_s_x_test_e8) {
+ vmv_s_x_test<int8_t>(INT32_MIN);
+ vmv_s_x_test<int8_t>(INT32_MAX);
+ vmv_s_x_test<int8_t>(INT8_MIN);
+ vmv_s_x_test<int8_t>(INT8_MAX);
+ vmv_s_x_test<int8_t>(0);
+}
+
+TEST_F(VmvsxTest, vmv_s_x_test_e16) {
+ vmv_s_x_test<int16_t>(INT32_MIN);
+ vmv_s_x_test<int16_t>(INT32_MAX);
+ vmv_s_x_test<int16_t>(INT8_MIN);
+ vmv_s_x_test<int16_t>(INT8_MAX);
+ vmv_s_x_test<int16_t>(0);
+}
+
+TEST_F(VmvsxTest, vmv_s_x_test_e32) {
+ vmv_s_x_test<int32_t>(INT32_MIN);
+ vmv_s_x_test<int32_t>(INT32_MAX);
+ vmv_s_x_test<int32_t>(INT8_MIN);
+ vmv_s_x_test<int32_t>(INT8_MAX);
+ vmv_s_x_test<int32_t>(0);
+}
+
+} // namespace
+} // namespace vmv_s_x_test
+