Add support for mako templated tests to softrvv.

Change-Id: Ic2a7fbae0af62882dbc611114fcf921fe2334342
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f2c4bb..5ba1680 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,8 @@
 include(vec_cc_binary)
 include(vec_cc_test)
 include(vec_cc_generated_test)
+include(softrvv_vec_cc_generated_test)
+
 
 add_subdirectory(springbok)
 add_subdirectory(hello_vec)
diff --git a/cmake/softrvv_vec_cc_generated_test.cmake b/cmake/softrvv_vec_cc_generated_test.cmake
new file mode 100644
index 0000000..38893b7
--- /dev/null
+++ b/cmake/softrvv_vec_cc_generated_test.cmake
@@ -0,0 +1,42 @@
+function(softrvv_vec_cc_generated_test)
+  cmake_parse_arguments(
+    _RULE
+    ""
+    "NAME"
+    "TEMPLATE;LINKOPTS;TIMEOUT"
+    ${ARGN}
+  )
+
+set(_TEST_NAME "softrvv_${_RULE_NAME}_test")
+set(_OP_CODE ${_RULE_NAME})
+string(REPLACE ".tpl" "" _TEST_SRC_FILENAME ${_RULE_TEMPLATE})
+
+set(_TEST_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated/${_TEST_SRC_FILENAME})
+find_file(_SOFTRVV_GEN_VEC_SCRIPT generate_softrvv_vector_tests.py ${CMAKE_SOURCE_DIR}/scripts)
+add_custom_command(
+    OUTPUT
+      ${_TEST_SRC}
+    DEPENDS
+     ${CMAKE_CURRENT_SOURCE_DIR}/templates/${_RULE_TEMPLATE}
+     ${CMAKE_CURRENT_SOURCE_DIR}/templates/base.tpl.cpp
+     ${CMAKE_CURRENT_SOURCE_DIR}/templates/opivv_opivx_test.tpl.cpp
+    COMMAND python3 ${_SOFTRVV_GEN_VEC_SCRIPT}
+      --template-path=${CMAKE_CURRENT_SOURCE_DIR}/templates/
+      --template=${_RULE_TEMPLATE}
+      --out-path=${CMAKE_CURRENT_BINARY_DIR}/generated/
+      --op=${_OP_CODE}
+)
+
+vec_cc_test(
+  NAME
+    ${_TEST_NAME}
+  SRCS
+    ${_TEST_SRC}
+  DEPS
+    softrvv
+  LINKOPTS
+    ${_RULE_LINKOPTS}
+  TIMEOUT
+    ${_RULE_TIMEOUT}
+)
+endfunction()
diff --git a/scripts/generate_softrvv_vector_tests.py b/scripts/generate_softrvv_vector_tests.py
new file mode 100644
index 0000000..9e337bd
--- /dev/null
+++ b/scripts/generate_softrvv_vector_tests.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+"""
+Generate tests for softrvv
+TODO(henryherman) Look into combining the different template workflows
+"""
+import os
+import logging
+import argparse
+from collections import namedtuple
+from pathlib import Path
+
+from mako.lookup import TemplateLookup
+import mako.exceptions
+
+parser = argparse.ArgumentParser(
+    description='Generate tests for vector instructions.')
+
+parser.add_argument('--template-path', dest='template_path',
+                    help='path to templates', required=True)
+parser.add_argument('--template', dest='template_name',
+                    help='template name', required=True)
+parser.add_argument('-v', '--verbose', help='increase output verbosity',
+                    action='store_true')
+parser.add_argument('--out-path', dest='out_path',
+                    help='Path to output files', default='.')
+parser.add_argument('--op', dest='op',
+                    help='op instruction', default='')
+
+args = parser.parse_args()
+
+if args.verbose:
+    logging.basicConfig(level=logging.DEBUG)
+
+def main():
+    """ Main routine for generating softrvv tests from templates."""
+    mylookup = TemplateLookup(directories=[args.template_path, "."])
+    try:
+        template = mylookup.get_template(args.template_name)
+    except mako.exceptions.TopLevelLookupException:
+        parser.error("Template does not exist %s" % args.template_name)
+    logging.debug("Template %s found", args.template_name)
+    Path(args.out_path).mkdir(parents=True, exist_ok=True)
+    outfile_name = args.template_name.replace(".tpl", "")
+    full_outfile_path = os.path.join(args.out_path, outfile_name)
+    logging.debug("Generating file %s", full_outfile_path)
+    with open(full_outfile_path, "w+") as outfile:
+        outfile.write(template.render(op=args.op))
+
+if __name__ == "__main__":
+    main()
diff --git a/softrvv/tests/templates/base.tpl.cpp b/softrvv/tests/templates/base.tpl.cpp
new file mode 100644
index 0000000..a9887cb
--- /dev/null
+++ b/softrvv/tests/templates/base.tpl.cpp
@@ -0,0 +1,16 @@
+<%!
+import numpy  as np
+
+def to_carr_str(arr):
+    return ", ".join(("%s" % x for x in arr))
+
+%>
+/* File is auto-generated. */
+#include <riscv_vector.h>
+#include <springbok.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pw_unit_test/framework.h"
+#include "softrvv.h"
+${self.body()}
diff --git a/softrvv/tests/templates/opivv_opivx_test.tpl.cpp b/softrvv/tests/templates/opivv_opivx_test.tpl.cpp
new file mode 100644
index 0000000..7134d91
--- /dev/null
+++ b/softrvv/tests/templates/opivv_opivx_test.tpl.cpp
@@ -0,0 +1,40 @@
+<%def name="test_opivv_opivx(dtype, op, src1, src2, rs1, ref_vv, ref_vx)">
+namespace softrvv_${op}_test {
+namespace {
+
+${dtype} src1[] = {${src1}};
+${dtype} src2[] = {${src2}};
+${dtype} rs1 = ${rs1};
+const ${dtype} kAVL = sizeof(src1)/sizeof(src1[0]);
+${dtype} dest[kAVL];
+
+${dtype} ref_vv[] = {${ref_vv}};
+${dtype} ref_vx[] = {${ref_vx}};
+
+template <typename T>
+void assert_vec_elem_eq(int avl, void *test_vector_1, void *test_vector_2) {
+  T *ptr_vec_1 = reinterpret_cast<T *>(test_vector_1);
+  T *ptr_vec_2 = reinterpret_cast<T *>(test_vector_2);
+  for (int idx = 0; idx < avl; idx++) {
+    ASSERT_EQ(ptr_vec_1[idx], ptr_vec_2[idx]);
+  }
+}
+
+class SoftRvv${op.capitalize()}Test : public ::testing::Test {
+ protected:
+  void SetUp() override { memset(dest, 0, sizeof(dest)); }
+};
+
+TEST_F(SoftRvv${op.capitalize()}Test, VV) {
+  softrvv::${op}_vv<${dtype}>(dest, src1, src2, kAVL);
+  assert_vec_elem_eq<${dtype}>(kAVL, dest, ref_vv);
+}
+
+TEST_F(SoftRvv${op.capitalize()}Test, VX) {
+  softrvv::${op}_vx<${dtype}>(dest, src1, &rs1, kAVL);
+  assert_vec_elem_eq<${dtype}>(kAVL, dest, ref_vx);
+}
+
+}  // namespace
+}  // namespace softrvv_${op}_test
+</%def>