Merge branch 'master' of https://spacebeaker.googlesource.com/shodan/sw/vec into vector_test Change-Id: I13979c48cda162b959fecc8b97b9df0b5775ba99
diff --git a/scripts/vec_test_helpers/__init__.py b/scripts/vec_test_helpers/__init__.py index 87f457e..4aa284e 100644 --- a/scripts/vec_test_helpers/__init__.py +++ b/scripts/vec_test_helpers/__init__.py
@@ -14,6 +14,7 @@ VECTOR = enum.auto() SCALAR = enum.auto() IMMEDIATE = enum.auto() + FLOATSCALAR = enum.auto() class OperandWidth(enum.Enum): """RISC-V V operand width type option.""" @@ -37,6 +38,11 @@ OperandWidth.WIDENING: "vi", OperandWidth.NARROWING: "wi", }, + OperandType.FLOATSCALAR : { + OperandWidth.STANDARD: "vf", + OperandWidth.WIDENING: "vf", + OperandWidth.NARROWING: "wf", + }, } """Helper class for providing params for use in templates""" @@ -52,12 +58,14 @@ 8:np.uint8, 16:np.uint16, 32:np.uint32} + self.float_np_types = { + 32:np.float32} @property def op_code(self): """Return the op_code.""" if self._op_code is None: - raise ValueError("SEW was not set.") + raise ValueError("OP CODE was not set.") return self._op_code @op_code.setter @@ -65,6 +73,10 @@ """Set the op_code""" self._op_code = value + def is_floating(self): + """check if a particular op_code is a floating type.""" + return self.op_code[1] == 'f' + @property def sew(self): """Return the selected element width.""" @@ -75,52 +87,68 @@ @sew.setter def sew(self, value): """Set the selected element width.""" + if self.is_floating(): + if not (value == 32): + raise ValueError("Invalid SEW") if not value in (8, 16, 32): raise ValueError("Invalid SEW") self._sew = value def is_widening(self): """Check if a particular op_code is a widening type.""" + if self.is_floating(): + raise ValueError("Widening is not supported with this template for floating point values") return self.op_code[1] == 'w' def is_narrowing(self): """Check if a particular op_code is a narrowing type.""" + if self.is_floating(): + raise ValueError("Narrowing is not supported with this template for floating point values") return self.op_code[1] == 'n' def is_destination_mask_register(self): """Check if a particular op_code has a mask output.""" + if self.is_floating(): + return False int_comparison_ops = ('vmseq', 'vmsne', 'vmsltu', 'vmsleu', 'vmsle', 'vmsgtu', 'vmsgt') return self.op_code in int_comparison_ops def is_unsigned(self): """Check if a particular op_code is a unsigned type.""" + if self.is_floating(): + raise ValueError("Invalid unsigned type op code") return self.op_code[-1] == 'u' def get_sews(self): """Given an op_code return a list of valid element widths.""" + if self.is_floating(): + return [32] + if self.is_widening() or self.is_narrowing(): return [8, 16] return [8, 16, 32] def get_sew_sizes(self): """Return size of types.""" + """imm is not used for floating point op codes.""" dest_sew = self.sew src2_sew = self.sew src1_sew = self.sew imm_sew = self.sew - if self.is_narrowing(): + if (not self.is_floating()) and self.is_narrowing(): src2_sew = self.sew * 2 - elif self.is_widening(): + elif (not self.is_floating()) and self.is_widening(): dest_sew = self.sew * 2 return dest_sew, src2_sew, src1_sew, imm_sew def get_var_types(self): """Return types for an op_code and element width.""" + """imm_type won't be used for floating point values.""" VarTypes = collections.namedtuple( "VarTypes", ('dest_type', 'src2_type', 'src1_type', 'imm_type')) - type_fmt = "%sint%d_t" - sign_type = "u" if self.is_unsigned() or self.force_unsigned else "" + type_fmt = "%sfloat%d_t" if self.is_floating() else "%sint%d_t" + sign_type = "u" if (not self.is_floating()) and (self.is_unsigned() or self.force_unsigned) else "" dest_sew, src2_sew, src1_sew, imm_sew = self.get_sew_sizes() dest_type = type_fmt % (sign_type, dest_sew) src1_type = type_fmt % (sign_type, src1_sew) @@ -132,16 +160,18 @@ def get_mnemonic(self, operand_type): """Generate the correct mnemonic given a opcode and operand type.""" operand_width = self.OperandWidth.STANDARD - if self.is_narrowing(): + if (not self.is_floating()) and self.is_narrowing(): operand_width = self.OperandWidth.NARROWING - elif self.is_widening(): + elif (not self.is_floating()) and self.is_widening(): operand_width = self.OperandWidth.WIDENING + if self.is_floating() and operand_type == self.OperandType.SCALAR: + operand_type = self.OperandType.FLOATSCALAR op_suffix = self.mnemonic_suffix[operand_type][operand_width] return "%s.%s" % (self.op_code, op_suffix) def get_lmuls(self): """Given an op_code return an iterable of valid lmuls.""" - if self.is_widening() or self.is_narrowing(): + if (not self.is_floating()) and (self.is_widening() or self.is_narrowing()): return [1, 2, 4] return [1, 2, 4, 8] @@ -154,16 +184,20 @@ def get_softrvv_template_data_type(self): """Return types """ var_types = self.get_var_types() - if self.is_narrowing() or self.is_widening(): + if (not self.is_floating()) and (self.is_narrowing() or self.is_widening()): return "%s, %s" % (var_types.dest_type, var_types.src2_type) return var_types.src1_type def get_ref_opcode(self): """Return the name of the reference code in the softrvv library.""" + if self.is_floating(): + return self.op_code return self.op_code[:-1] if self.is_unsigned() else self.op_code def get_imms(self): """Return a list of valid immediate values for a op code.""" + if self.is_floating(): + raise ValueError("imm is not available.") if self.op_code in ['vsll', 'vsrl', 'vsra', 'vnsrl', 'vnsra']: # Left and right shift immediates must be [0,31] return np.linspace(0, 31, 8, dtype=np.int32) @@ -172,6 +206,8 @@ def get_np_dest_type(self): """Return numpy type for destination.""" + if self.is_floating(): + return self.float_np_types[self.sew] if self.force_unsigned: types = self.unsigned_np_types else: @@ -182,6 +218,8 @@ def get_np_src1_type(self): """Return numpy type for src1.""" + if self.is_floating(): + return self.float_np_types[self.sew] if self.force_unsigned: types = self.unsigned_np_types else: @@ -190,6 +228,8 @@ def get_np_src2_type(self): """Return numpy type for src2.""" + if self.is_floating(): + return self.float_np_types[self.sew] if self.force_unsigned: types = self.unsigned_np_types else: @@ -202,12 +242,28 @@ """Return test inputs.""" src1_np_type = self.get_np_src1_type() src2_np_type = self.get_np_src2_type() + src2_np_type = self.get_np_src2_type() + """Return test inputs for floating points.""" + if self.is_floating(): + type_info = np.finfo(src1_np_type) + src1_data = np.random.uniform( + type_info.min/2.0, type_info.max/2.0, n).astype(src1_np_type) + rs1 = self.get_np_src1_type()(np.random.uniform( + type_info.min/2.0, type_info.max/2.0)) + type_info = np.finfo(src2_np_type) + src2_data = np.random.uniform( + type_info.min/2.0, type_info.max/2.0, n).astype(src2_np_type) + if not allow_zero: + src2_data[src2_data==0] = 1 + rs1 = 1.0 if rs1 == 0.0 else rs1 + return src2_data, src1_data, rs1 + + """Return test inputs for integers.""" type_info = np.iinfo(src1_np_type) src1_data = np.random.randint( type_info.min, type_info.max, n).astype(src1_np_type) rs1 = self.get_np_src1_type()(np.random.randint( type_info.min, type_info.max)) - src2_np_type = self.get_np_src2_type() type_info = np.iinfo(src2_np_type) src2_data = np.random.randint( type_info.min, type_info.max, n).astype(src2_np_type) @@ -217,12 +273,13 @@ return src2_data, src1_data, rs1 def pack_dest_mask(self, values): - """Pack values into a single destination register.""" - dest_type = self.get_np_dest_type() - return np.packbits(dest_type(values), bitorder='little') + """Pack values into a single destination register.""" + dest_type = self.get_np_dest_type() + return np.packbits(dest_type(values), bitorder='little') def cast_to_unsigned(arr): """Cast a signed array to an unsigned array.""" + """This should not be called with floating point values.""" udtypes = {np.int8:np.uint8, np.int16:np.uint16, np.int32:np.uint32,
diff --git a/softrvv/include/softrvv.h b/softrvv/include/softrvv.h index d82e7a0..1046aba 100644 --- a/softrvv/include/softrvv.h +++ b/softrvv/include/softrvv.h
@@ -31,6 +31,9 @@ #include "softrvv_vwsub.h" #include "softrvv_vxor.h" +#include "softrvv_vfadd.h" +#include "softrvv_vfsub.h" + namespace softrvv { const uint32_t MSTATUS_VS_FS_ENABLE_BIT =
diff --git a/softrvv/include/softrvv_vfadd.h b/softrvv/include/softrvv_vfadd.h new file mode 100644 index 0000000..86009c8 --- /dev/null +++ b/softrvv/include/softrvv_vfadd.h
@@ -0,0 +1,24 @@ +#ifndef SOFTRVV_VFADD_H +#define SOFTRVV_VFADD_H + +#include <stddef.h> + +namespace softrvv { + +template <typename T> +void vfadd_vf(T *dest, T *src1, const T *src2, int32_t avl) { + for (int32_t idx = 0; idx < avl; idx++) { + dest[idx] = src1[idx] + *src2; + } +} + +template <typename T> +void vfadd_vv(T *dest, T *src1, T *src2, int32_t avl) { + for (int32_t idx = 0; idx < avl; idx++) { + dest[idx] = src1[idx] + src2[idx]; + } +} + +} // namespace softrvv + +#endif // SOFTRVV_VFADD_H
diff --git a/softrvv/include/softrvv_vfsub.h b/softrvv/include/softrvv_vfsub.h new file mode 100644 index 0000000..eea2f26 --- /dev/null +++ b/softrvv/include/softrvv_vfsub.h
@@ -0,0 +1,31 @@ +#ifndef SOFTRVV_VFSUB_H +#define SOFTRVV_VFSUB_H + +#include <stddef.h> + +namespace softrvv { + +template <typename T> +void vfsub_vf(T *dest, T *src1, const T *src2, int32_t avl) { + for (int idx = 0; idx < avl; idx++) { + dest[idx] = src1[idx] - *src2; + } +} + +template <typename T> +void vfsub_vv(T *dest, T *src1, T *src2, int32_t avl) { + for (int32_t idx = 0; idx < avl; idx++) { + dest[idx] = src1[idx] - src2[idx]; + } +} + +template <typename T> +void vfrsub_vx(T *dest, T *src1, const T *src2, int32_t avl) { + for (int32_t idx = 0; idx < avl; idx++) { + dest[idx] = *src2 - src1[idx]; + } +} + +} // namespace softrvv + +#endif // SOFTRVV_VFSUB_H
diff --git a/softrvv/tests/CMakeLists.txt b/softrvv/tests/CMakeLists.txt index 62cbc9a..289f74e 100644 --- a/softrvv/tests/CMakeLists.txt +++ b/softrvv/tests/CMakeLists.txt
@@ -100,6 +100,24 @@ softrvv_vec_cc_generated_test( NAME + vfadd + TEMPLATE + softrvv_vfadd_test.tpl.cpp + LINKOPTS + -Xlinker --defsym=__itcm_length__=128K +) + +softrvv_vec_cc_generated_test( + NAME + vfsub + TEMPLATE + softrvv_vfsub_test.tpl.cpp + LINKOPTS + -Xlinker --defsym=__itcm_length__=128K +) + +softrvv_vec_cc_generated_test( + NAME vadd TEMPLATE softrvv_vadd_test.tpl.cpp
diff --git a/softrvv/tests/templates/base.tpl.cpp b/softrvv/tests/templates/base.tpl.cpp index 586cfff..9fc3bfa 100644 --- a/softrvv/tests/templates/base.tpl.cpp +++ b/softrvv/tests/templates/base.tpl.cpp
@@ -1,16 +1,3 @@ -<%! -import numpy as np - -def get_test_inputs(dtype, N, allow_zero=True): - ii32 = np.iinfo(dtype) - src1_data = np.random.randint(ii32.min, ii32.max, N).astype(dtype) - src2_data = np.random.randint(ii32.min, ii32.max, N).astype(dtype) - rs1 = dtype(np.random.randint(ii32.min, ii32.max)) - if not allow_zero: - src2_data[src2_data==0] = 1 - rs1 = 1 if rs1 == 0 else rs1 - return src1_data, src2_data, rs1 -%> /* File is auto-generated. */ #include <riscv_vector.h> #include <springbok.h>
diff --git a/softrvv/tests/templates/opivf_test.tpl.cpp b/softrvv/tests/templates/opivf_test.tpl.cpp new file mode 100644 index 0000000..87c04c1 --- /dev/null +++ b/softrvv/tests/templates/opivf_test.tpl.cpp
@@ -0,0 +1,52 @@ +<%! +import vec_test_helpers +%>\ +<%def name="test_opivf(template_helper, src2, rs1, ref_vf)"> +<% +src2 = vec_test_helpers.to_carr_str(src2) +ref_vf = vec_test_helpers.to_carr_str(ref_vf) +%>\ +namespace softrvv_${template_helper.op_code}_vf_test { +namespace { +${insert_variable_init(template_helper, src2, rs1, ref_vf)} + +class SoftRvv${template_helper.op_code.capitalize()}Test : public ::testing::Test { + protected: + void SetUp() override { memset(dest, 0, sizeof(dest)); } +}; +${insert_test(template_helper)}\ +} // namespace +} // namespace softrvv_${op}_vf_test\ +</%def>\ + +<%def name="insert_variable_init(template_helper, src2, rs1, ref_vf)"> +<% + var_types = template_helper.get_var_types() +%>\ +${var_types.src2_type} src2[] = {${src2}}; +${var_types.imm_type} rs1 = ${rs1}; +const int kAVL = sizeof(src2)/sizeof(src2[0]); +${var_types.dest_type} dest[kAVL]; + +${var_types.dest_type} ref_vf[kAVL] = {${ref_vf}}; +</%def>\ + +<%def name="insert_test(template_helper)"> +<% +var_types = template_helper.get_var_types() +datatypes = template_helper.get_softrvv_template_data_type() +%>\ + +TEST_F(SoftRvv${template_helper.op_code.capitalize()}Test, VF) { + softrvv::${template_helper.op_code}_vf<${datatypes}>(dest, src2, &rs1, kAVL);\ +${insert_check(template_helper, var_types.dest_type, "ref_vf")}\ +} +</%def>\ + +<%def name="insert_check(template_helper, dest_type, ref_var)"> +% if template_helper.is_destination_mask_register(): + assert_vec_mask_eq<${dest_type}>(kAVL, dest, ${ref_var}); +% else: + assert_vec_elem_eq<${dest_type}>(kAVL, dest, ${ref_var}); +% endif +</%def>\
diff --git a/softrvv/tests/templates/opivv_test.tpl.cpp b/softrvv/tests/templates/opivv_test.tpl.cpp new file mode 100644 index 0000000..08bd2e7 --- /dev/null +++ b/softrvv/tests/templates/opivv_test.tpl.cpp
@@ -0,0 +1,53 @@ +<%! +import vec_test_helpers +%>\ +<%def name="test_opivv(template_helper, src2, src1, ref_vv)"> +<% +src1 = vec_test_helpers.to_carr_str(src1) +src2 = vec_test_helpers.to_carr_str(src2) +ref_vv = vec_test_helpers.to_carr_str(ref_vv) +%>\ +namespace softrvv_${template_helper.op_code}_vv_test { +namespace { +${insert_variable_init(template_helper, src2, src1, ref_vv)} + +class SoftRvv${template_helper.op_code.capitalize()}Test : public ::testing::Test { + protected: + void SetUp() override { memset(dest, 0, sizeof(dest)); } +}; +${insert_test(template_helper)}\ +} // namespace +} // namespace softrvv_${op}_vv_test\ +</%def>\ + +<%def name="insert_variable_init(template_helper, src2, src1, ref_vv)"> +<% + var_types = template_helper.get_var_types() +%>\ +${var_types.src1_type} src1[] = {${src1}}; +${var_types.src2_type} src2[] = {${src2}}; +const int kAVL = sizeof(src1)/sizeof(src1[0]); +${var_types.dest_type} dest[kAVL]; + +${var_types.dest_type} ref_vv[kAVL] = {${ref_vv}}; +</%def>\ + +<%def name="insert_test(template_helper)"> +<% +var_types = template_helper.get_var_types() +datatypes = template_helper.get_softrvv_template_data_type() +%>\ +TEST_F(SoftRvv${template_helper.op_code.capitalize()}Test, VV) { + softrvv::${template_helper.op_code}_vv<${datatypes}>(dest, src2, src1, kAVL);\ +${insert_check(template_helper, var_types.dest_type, "ref_vv")}\ +} + +</%def>\ + +<%def name="insert_check(template_helper, dest_type, ref_var)"> +% if template_helper.is_destination_mask_register(): + assert_vec_mask_eq<${dest_type}>(kAVL, dest, ${ref_var}); +% else: + assert_vec_elem_eq<${dest_type}>(kAVL, dest, ${ref_var}); +% endif +</%def>\
diff --git a/softrvv/tests/templates/softrvv_vfadd_test.tpl.cpp b/softrvv/tests/templates/softrvv_vfadd_test.tpl.cpp new file mode 100644 index 0000000..ec8695b --- /dev/null +++ b/softrvv/tests/templates/softrvv_vfadd_test.tpl.cpp
@@ -0,0 +1,14 @@ +<%inherit file="base.tpl.cpp"/>\ +<%namespace name="tests_vv" file="opivv_test.tpl.cpp"/>\ +<%namespace name="tests_vf" file="opivf_test.tpl.cpp"/>\ +<% +import numpy as np +import vec_test_helpers +template_helper = vec_test_helpers.VecTemplateHelper(op, 32) +src2, src1, rs1 = template_helper.get_test_inputs(n=5) +ref_vv = src2 + src1 +ref_vf = src2 + rs1 +%>\ +${tests_vv.test_opivv(template_helper, src2, src1, ref_vv)} +${tests_vf.test_opivf(template_helper, src2, rs1, ref_vf)} +
diff --git a/softrvv/tests/templates/softrvv_vfsub_test.tpl.cpp b/softrvv/tests/templates/softrvv_vfsub_test.tpl.cpp new file mode 100644 index 0000000..f27fd3a --- /dev/null +++ b/softrvv/tests/templates/softrvv_vfsub_test.tpl.cpp
@@ -0,0 +1,13 @@ +<%inherit file="base.tpl.cpp"/>\ +<%namespace name="tests_vv" file="opivv_test.tpl.cpp"/> +<%namespace name="tests_vf" file="opivf_test.tpl.cpp"/> +<% +import numpy as np +import vec_test_helpers +template_helper = vec_test_helpers.VecTemplateHelper(op, 32) +src2, src1, rs1 = template_helper.get_test_inputs(n=5) +ref_vv = src2 - src1 +ref_vf = src2 - rs1 +%>\ +${tests_vv.test_opivv(template_helper, src2, src1, ref_vv)} +${tests_vf.test_opivf(template_helper, src2, rs1, ref_vf)}
diff --git a/test_v_helpers/include/test_v_helpers.h b/test_v_helpers/include/test_v_helpers.h index ba28e43..66760c0 100644 --- a/test_v_helpers/include/test_v_helpers.h +++ b/test_v_helpers/include/test_v_helpers.h
@@ -8,6 +8,20 @@ #include "pw_unit_test/framework.h" +#ifdef __cplusplus + +using float32_t = float; +using float64_t = double; +using float128_t = long double; + +#else + +typedef float float32_t; +typedef double float64_t; +typedef long double float128_t; + +#endif // __cplusplus + namespace test_v_helpers { const int LMUL_MAX = 8;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2230dd1..e2f55ff 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt
@@ -1,3 +1,12 @@ +# vec_cc_generated_test( +# NAME +# vfadd +# TEMPLATE +# opivv_test.tpl.cpp +# LINKOPTS +# -Xlinker --defsym=__itcm_length__=128K +# ) + vec_cc_generated_test( NAME vsub @@ -391,3 +400,12 @@ LINKOPTS -Xlinker --defsym=__itcm_length__=128K ) + +vec_cc_test( + NAME + vfadd_test + SRCS + vector_vfadd_test.cpp + LINKOPTS + -Xlinker --defsym=__itcm_length__=128K +) \ No newline at end of file
diff --git a/tests/vector_vfadd_test.cpp b/tests/vector_vfadd_test.cpp new file mode 100644 index 0000000..9ec1eac --- /dev/null +++ b/tests/vector_vfadd_test.cpp
@@ -0,0 +1,88 @@ +#include <limits.h> +#include <riscv_vector.h> +#include <softrvv.h> +#include <springbok.h> +#include <stdio.h> +#include <stdlib.h> + +#include <stdint.h> +#include <bit> +#include <tuple> + +#include "pw_unit_test/framework.h" +#include "test_v_helpers.h" + +#ifdef __cplusplus + +using float32_t = float; +using float64_t = double; +using float128_t = long double; + +#else + +typedef float float32_t; +typedef double float64_t; +typedef long double float128_t; + +#endif // __cplusplus + +namespace vfadd_vv_test +{ + namespace + { + using namespace test_v_helpers; + + float src_vector_1[MAXVL_BYTES]; + float src_vector_2[MAXVL_BYTES]; + float dest_vector[MAXVL_BYTES]; + float ref_dest_vector[MAXVL_BYTES]; + + class VfaddTest : public ::testing::Test { + protected: + void SetUp() override { zero_vector_registers(); } + void TearDown() override { zero_vector_registers(); } + }; + + TEST_F(VfaddTest, vfadd_vv32m1) { + for (int i = 0; i < AVL_COUNT; i++) { + int32_t avl = AVLS[i]; + int vlmax; + int vl; + + /* For non narrowing instructions all vectors have same type*/ + std::tie(vlmax, vl) = vector_test_setup<int32_t>( + VLMUL::LMUL_M1, avl, + {dest_vector, ref_dest_vector, src_vector_1}); + + if (avl > vlmax) { + continue; + } + + float *ptr_vec_1 = reinterpret_cast<float *>(src_vector_1); + float *ptr_vec_2 = reinterpret_cast<float *>(src_vector_2); + float *ptr_dest_vec = reinterpret_cast<float *>(dest_vector); + float *ptr_ref_dest_vec = reinterpret_cast<float *>(ref_dest_vector); + + // set up values to test up to index of the AVL + fill_random_vector<float>(ptr_vec_1, avl); + fill_random_vector<float>(ptr_vec_2, avl); + memset(dest_vector, 0, MAXVL_BYTES); + memset(ref_dest_vector, 0, MAXVL_BYTES); + + // Generate reference vector + softrvv::vfadd_vv<float>(ptr_ref_dest_vec, ptr_vec_2, ptr_vec_1, avl); + // Load vector registers + __asm__ volatile("vle32.v v8, (%0)" : : "r"(ptr_vec_1)); + __asm__ volatile("vle32.v v16, (%0)" : : "r"(ptr_vec_2)); + + // Run target instruction + __asm__ volatile("vfadd.vv v24, v16, v8"); + + // Store result vector register + __asm__ volatile("vse32.v v24, (%0)" : : "r"(ptr_dest_vec)); + // Check vector elements + assert_vec_elem_eq<float>(vlmax, dest_vector, ref_dest_vector); + } + } + } +}