Refactor templates to use a single strategy.

Change-Id: I7157a8a66f2fd784f99148e41a91ed9ca9d36b95
diff --git a/.gitignore b/.gitignore
index 378eac2..6f97ca1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 build
+*.pyc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ba1680..5f2c4bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,8 +30,6 @@
 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
deleted file mode 100644
index 38893b7..0000000
--- a/cmake/softrvv_vec_cc_generated_test.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
-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/cmake/vec_cc_generated_test.cmake b/cmake/vec_cc_generated_test.cmake
index b51f974..5e21a70 100644
--- a/cmake/vec_cc_generated_test.cmake
+++ b/cmake/vec_cc_generated_test.cmake
@@ -1,40 +1,81 @@
+# vec_cc_generated_test()
+#
+# CMake function to generate vector test from a given template.
+#
+# Parameters:
+# NAME: Name of the op_code the test will verify
+# TEMPLATE: mako template to use in generating the CPP file
+# PREFIX: prefix to add to the target
+# LINKOPTS: additional link options
+# TIMEOUT: test specific timeout
+#
+# Note:
+#  This will generate an elf file that can be executed on target
+#  It also adds a test that can be run via CTest
+
 function(vec_cc_generated_test)
   cmake_parse_arguments(
     _RULE
     ""
     "NAME"
-    "OPFMT;LINKOPTS;TIMEOUT"
+    "TEMPLATE;PREFIX;LINKOPTS;TIMEOUT"
     ${ARGN}
   )
 
-set(_OPCODE "${_RULE_NAME}")
+set(_TEST_NAME "${_RULE_PREFIX}${_RULE_NAME}_test")
+set(_OP_CODE ${_RULE_NAME})
+string(REPLACE ".tpl" "" _TEST_SRC_FILENAME ${_RULE_TEMPLATE})
 
-foreach(_OPFMT ${_RULE_OPFMT})
-string(TOLOWER ${_OPFMT} _LOWER_OPFMT)
-set(_TEST_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated/${_OPCODE}/${_OPCODE}_${_LOWER_OPFMT}_test.cpp)
+set(_TEST_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated/${_OP_CODE}/${_TEST_SRC_FILENAME})
 find_file(_GEN_VEC_SCRIPT generate_vector_tests.py ${CMAKE_SOURCE_DIR}/scripts)
+file(GLOB_RECURSE TEMPLATES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.tpl")
 add_custom_command(
     OUTPUT
       ${_TEST_SRC}
     DEPENDS
-     ${CMAKE_CURRENT_SOURCE_DIR}/templates/${_LOWER_OPFMT}_test.tpl.cpp
+     ${CMAKE_CURRENT_SOURCE_DIR}/templates/${_RULE_TEMPLATE}
+     ${TEMPLATES}
     COMMAND python3 ${_GEN_VEC_SCRIPT}
       --template-path=${CMAKE_CURRENT_SOURCE_DIR}/templates/
-      --instruction-format ${_OPFMT}
-      --op-code ${_OPCODE}
-      --out-path=${CMAKE_CURRENT_BINARY_DIR}/generated/${_OPCODE}
+      --template=${_RULE_TEMPLATE}
+      --out-path=${CMAKE_CURRENT_BINARY_DIR}/generated/${_OP_CODE}
+      --op=${_OP_CODE}
 )
-list (APPEND _TEST_SRCS "${_TEST_SRC}")
-endforeach()
 
 vec_cc_test(
   NAME
-    ${_OPCODE}_test
+    ${_TEST_NAME}
   SRCS
-    ${_TEST_SRCS}
+    ${_TEST_SRC}
+  DEPS
+    softrvv
   LINKOPTS
     ${_RULE_LINKOPTS}
   TIMEOUT
     ${_RULE_TIMEOUT}
 )
 endfunction()
+
+function(softrvv_vec_cc_generated_test)
+  cmake_parse_arguments(
+    _RULE
+    ""
+    "NAME"
+    "TEMPLATE;LINKOPTS;TIMEOUT"
+    ${ARGN}
+  )
+
+vec_cc_generated_test(
+  NAME
+    ${_RULE_NAME}
+  PREFIX
+    softrvv_
+  TEMPLATE
+    ${_RULE_TEMPLATE}
+  LINKOPTS
+    ${_RULE_LINKOPTS}
+  TIMEOUT
+    ${_RULE_TIMEOUT}
+)
+
+endfunction()
diff --git a/scripts/generate_softrvv_vector_tests.py b/scripts/generate_softrvv_vector_tests.py
deleted file mode 100644
index 9e337bd..0000000
--- a/scripts/generate_softrvv_vector_tests.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/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/scripts/generate_vector_tests.py b/scripts/generate_vector_tests.py
index 47f39fd..f6fcf07 100644
--- a/scripts/generate_vector_tests.py
+++ b/scripts/generate_vector_tests.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 """
-Generate tests for Vector Instructions
+Generate tests for softrvv
+TODO(henryherman) Look into combining the different template workflows
 """
 import os
 import logging
@@ -16,49 +17,36 @@
 
 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('--op-code', dest='op_code',
-                    help='Op-code', required=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='')
 
-template_name_lookup = {
-    'OPIVV':'opivv_test.tpl.cpp',
-    'OPIVI':'opivi_test.tpl.cpp',
-    'OPIVX':'opivx_test.tpl.cpp',
-    'VXUNARY0':'vxunary0_test.tpl.cpp'
-}
-
-parser.add_argument('--instruction-format',
-    action='append', choices=template_name_lookup.keys(), required=True)
 args = parser.parse_args()
 
 if args.verbose:
     logging.basicConfig(level=logging.DEBUG)
 
 def main():
-    """ Main routine for generating tests from templates."""
+    """ Main routine for generating softrvv tests from templates."""
     mylookup = TemplateLookup(directories=[args.template_path, "."])
-    template_names = [template_name_lookup.get(fmt, None) for fmt in args.instruction_format]
-    template_names = [template for template in template_names if template is not None]
-    template_jobs = []
-    TemplateJob = namedtuple("TemplateJob", "template outfile")
+    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)
-    for opfmt, template_name in template_name_lookup.items():
-        if not opfmt in args.instruction_format:
-            continue
-        try:
-            template = mylookup.get_template(template_name)
-        except mako.exceptions.TopLevelLookupException:
-            parser.error("Template does not exist %s" % template_name)
-        outfile_name = "%s_%s" % (args.op_code, template_name.replace(".tpl", ""))
-        template_jobs.append(TemplateJob(template, outfile_name))
-
-    for template_job in template_jobs:
-        full_outfile_path = os.path.join(args.out_path, template_job.outfile)
-        with open(full_outfile_path, "w+") as outfile:
-            outfile.write(template_job.template.render(op_code=args.op_code))
+    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/scripts/vec_test_helpers/__init__.py b/scripts/vec_test_helpers/__init__.py
new file mode 100644
index 0000000..77cb7ff
--- /dev/null
+++ b/scripts/vec_test_helpers/__init__.py
@@ -0,0 +1,37 @@
+""" vec_test_helpers
+
+This module is for reusable helper functions used by the templates.
+"""
+
+def is_widening(op_code):
+    """Check if a particular op_code is a widening type."""
+    return op_code[1] == 'w'
+
+def is_unsigned(op_code):
+    """Check if a particular op_code is a unsigned type."""
+    return op_code[-1] == 'u'
+
+def get_sews(op_code):
+    """Given an op_code return a list of valid element widths."""
+    return [8, 16] if is_widening(op_code) else [8, 16, 32]
+
+def get_lmuls(op_code):
+    """Given an op_code return an iterable if valid lmuls."""
+    return [1, 2, 4] if is_widening(op_code) else [1, 2, 4, 8]
+
+def get_dest_type(op_code, sew):
+    """Return a destination type for a op_code and element width."""
+    type_fmt = "%sint%d_t"
+    sign_type  = "u" if is_unsigned(op_code) else ""
+    dest_sew = sew * 2 if is_widening(op_code) else sew
+    return type_fmt % (sign_type, dest_sew)
+
+def get_src_type(op_code, sew):
+    """Return a source type for an op_code and element width."""
+    type_fmt = "%sint%d_t"
+    sign_type = "u" if is_unsigned(op_code) else ""
+    return type_fmt % (sign_type, sew)
+
+def get_ref_opcode(op_code):
+    """Return the name of the reference code in the softrvv library."""
+    return op_code[:-1] if is_unsigned(op_code) else op_code
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 710743b..646c272 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -14,9 +14,8 @@
 vec_cc_generated_test(
   NAME
     vsub
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -24,9 +23,8 @@
 vec_cc_generated_test(
   NAME
     vrsub
-  OPFMT
-    OPIVI
-    OPIVX
+  TEMPLATE
+    opivx_opivi_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=256K
 )
@@ -34,10 +32,8 @@
 vec_cc_generated_test(
   NAME
     vadd
-  OPFMT
-    OPIVV
-    OPIVX
-    OPIVI
+  TEMPLATE
+    opivv_opivx_opivi_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=256K
 )
@@ -45,10 +41,8 @@
 vec_cc_generated_test(
   NAME
     vor
-  OPFMT
-    OPIVV
-    OPIVX
-    OPIVI
+  TEMPLATE
+    opivv_opivx_opivi_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=256K
 )
@@ -56,10 +50,8 @@
 vec_cc_generated_test(
   NAME
     vxor
-  OPFMT
-    OPIVV
-    OPIVX
-    OPIVI
+  TEMPLATE
+    opivv_opivx_opivi_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=256K
 )
@@ -67,9 +59,8 @@
 vec_cc_generated_test(
   NAME
     vmin
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -77,9 +68,8 @@
 vec_cc_generated_test(
   NAME
     vminu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -87,9 +77,8 @@
 vec_cc_generated_test(
   NAME
     vmul
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -97,9 +86,8 @@
 vec_cc_generated_test(
   NAME
     vmulh
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -107,9 +95,8 @@
 vec_cc_generated_test(
   NAME
     vmulhu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -117,9 +104,8 @@
 vec_cc_generated_test(
   NAME
     vdiv
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -127,9 +113,8 @@
 vec_cc_generated_test(
   NAME
     vdivu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -137,9 +122,8 @@
 vec_cc_generated_test(
   NAME
     vrem
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -147,9 +131,8 @@
 vec_cc_generated_test(
   NAME
     vremu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -157,9 +140,8 @@
 vec_cc_generated_test(
   NAME
     vwadd
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -167,9 +149,8 @@
 vec_cc_generated_test(
   NAME
     vwaddu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -177,9 +158,8 @@
 vec_cc_generated_test(
   NAME
     vwsub
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -187,9 +167,8 @@
 vec_cc_generated_test(
   NAME
     vwsubu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -197,9 +176,8 @@
 vec_cc_generated_test(
   NAME
     vmax
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -208,10 +186,8 @@
 vec_cc_generated_test(
   NAME
     vand
-  OPFMT
-    OPIVV
-    OPIVX
-    OPIVI
+  TEMPLATE
+    opivv_opivx_opivi_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=200K
 )
@@ -219,9 +195,8 @@
 vec_cc_generated_test(
   NAME
     vmaxu
-  OPFMT
-    OPIVV
-    OPIVX
+  TEMPLATE
+    opivv_opivx_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -229,8 +204,8 @@
 vec_cc_generated_test(
   NAME
     vsext
-  OPFMT
-    VXUNARY0
+  TEMPLATE
+    vxunary0_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
@@ -238,8 +213,8 @@
 vec_cc_generated_test(
   NAME
     vzext
-  OPFMT
-    VXUNARY0
+  TEMPLATE
+    vxunary0_test.tpl.cpp
   LINKOPTS
    -Xlinker --defsym=__itcm_length__=128K
 )
diff --git a/tests/templates/base.tpl.cpp b/tests/templates/base.tpl.cpp
index f6047a1..df861ff 100644
--- a/tests/templates/base.tpl.cpp
+++ b/tests/templates/base.tpl.cpp
@@ -1,32 +1,3 @@
-<%!
-
-def is_widening(op_code):
-    return op_code[1] == 'w'
-
-def is_unsigned(op_code):
-    return op_code[-1] == 'u'
-
-def get_sews(op_code):
-    return [8, 16] if is_widening(op_code) else [8, 16, 32]
-
-def get_lmuls(op_code):
-    return [1, 2, 4] if is_widening(op_code) else [1, 2, 4, 8]
-
-def get_dest_type(op_code, sew):
-    type_fmt = "%sint%d_t"
-    sign_type  = "u" if is_unsigned(op_code) else ""
-    dest_sew = sew * 2 if is_widening(op_code) else sew
-    return type_fmt % (sign_type, dest_sew)
-
-def get_src_type(op_code, sew):
-    type_fmt = "%sint%d_t"
-    sign_type = "u" if is_unsigned(op_code) else ""
-    return type_fmt % (sign_type, sew)
-
-def get_ref_opcode(op_code):
-    return op_code[:-1] if is_unsigned(op_code) else op_code
-
-%>
 /* Automatically generated file */
 #include <limits.h>
 #include <riscv_vector.h>
diff --git a/tests/templates/opivi_test.tpl.cpp b/tests/templates/base_opivi_test.tpl.cpp
similarity index 97%
rename from tests/templates/opivi_test.tpl.cpp
rename to tests/templates/base_opivi_test.tpl.cpp
index a831c1e..39343cb 100644
--- a/tests/templates/opivi_test.tpl.cpp
+++ b/tests/templates/base_opivi_test.tpl.cpp
@@ -1,5 +1,4 @@
-<%inherit file="base.tpl.cpp"/>\
-
+<%def name="test_opivi(op_code)">
 namespace ${op_code}_vi_test {
 namespace {
 
@@ -64,3 +63,5 @@
 
 }  // namespace
 }  // namespace ${op_code}_vi_test
+</%def>
+
diff --git a/tests/templates/opivv_test.tpl.cpp b/tests/templates/base_opivv_test.tpl.cpp
similarity index 86%
rename from tests/templates/opivv_test.tpl.cpp
rename to tests/templates/base_opivv_test.tpl.cpp
index 2075de2..35b92b8 100644
--- a/tests/templates/opivv_test.tpl.cpp
+++ b/tests/templates/base_opivv_test.tpl.cpp
@@ -1,5 +1,4 @@
-<%inherit file="base.tpl.cpp"/>\
-
+<%def name="test_opivv(op_code)">
 namespace ${op_code}_vv_test {
 namespace {
 
@@ -16,18 +15,18 @@
   void TearDown() override { zero_vector_registers(); }
 };
 <%
-widening = parent.module.is_widening(op_code)
-sews = parent.module.get_sews(op_code)
-lmuls = parent.module.get_lmuls(op_code)
-ref_opcode = parent.module.get_ref_opcode(op_code)
+import vec_test_helpers
+sews = vec_test_helpers.get_sews(op_code)
+lmuls = vec_test_helpers.get_lmuls(op_code)
 %>\
 % for sew in sews:
 % for lmul in lmuls:
 <%
-dest_type = parent.module.get_dest_type(op_code, sew)
-src_type = parent.module.get_src_type(op_code, sew)
+dest_type = vec_test_helpers.get_dest_type(op_code, sew)
+src_type = vec_test_helpers.get_src_type(op_code, sew)
+ref_opcode = vec_test_helpers.get_ref_opcode(op_code)
+widening = vec_test_helpers.is_widening(op_code)
 %>\
-
 TEST_F(${op_code.capitalize()}Test, ${op_code.lower()}_vv${sew}m${lmul}) {
   for (int i = 0; i < AVL_COUNT; i++) {
     int32_t avl = AVLS[i];
@@ -78,3 +77,5 @@
 
 }  // namespace
 }  // namespace ${op_code}_vv_test
+</%def>
+
diff --git a/tests/templates/opivx_test.tpl.cpp b/tests/templates/base_opivx_test.tpl.cpp
similarity index 85%
rename from tests/templates/opivx_test.tpl.cpp
rename to tests/templates/base_opivx_test.tpl.cpp
index c10f9c7..8feaeb4 100644
--- a/tests/templates/opivx_test.tpl.cpp
+++ b/tests/templates/base_opivx_test.tpl.cpp
@@ -1,5 +1,4 @@
-<%inherit file="base.tpl.cpp"/>\
-
+<%def name="test_opivx(op_code)">
 namespace ${op_code}_vx_test {
 namespace {
 
@@ -15,18 +14,18 @@
   void TearDown() override { zero_vector_registers(); }
 };
 <%
-widening = parent.module.is_widening(op_code)
-sews = parent.module.get_sews(op_code)
-lmuls = parent.module.get_lmuls(op_code)
+import vec_test_helpers
+sews = vec_test_helpers.get_sews(op_code)
+lmuls = vec_test_helpers.get_lmuls(op_code)
 %>\
 % for sew in sews:
 % for lmul in lmuls:
 <%
-dest_type = parent.module.get_dest_type(op_code, sew)
-src_type = parent.module.get_src_type(op_code, sew)
-ref_opcode = parent.module.get_ref_opcode(op_code)
+dest_type = vec_test_helpers.get_dest_type(op_code, sew)
+src_type = vec_test_helpers.get_src_type(op_code, sew)
+ref_opcode = vec_test_helpers.get_ref_opcode(op_code)
+widening = vec_test_helpers.is_widening(op_code)
 %>\
-
 TEST_F(${op_code.capitalize()}Test, ${op_code.lower()}_vx${sew}m${lmul}) {
   for (int i = 0; i < AVL_COUNT; i++) {
     int32_t avl = AVLS[i];
@@ -74,3 +73,5 @@
 
 }  // namespace
 }  // namespace ${op_code}_vx_test
+</%def>
+
diff --git a/tests/templates/base_vxunary0_test.tpl.cpp b/tests/templates/base_vxunary0_test.tpl.cpp
new file mode 100644
index 0000000..438571b
--- /dev/null
+++ b/tests/templates/base_vxunary0_test.tpl.cpp
@@ -0,0 +1,59 @@
+<%def name="test_vxunary0(op_code)">
+namespace ${op_code}_test {
+namespace {
+
+using namespace test_v_helpers;
+
+uint8_t src_vector_1[MAXVL_BYTES];
+uint8_t dest_vector[MAXVL_BYTES];
+uint8_t ref_dest_vector[MAXVL_BYTES];
+
+class ${op_code.capitalize()}Test : public ::testing::Test {
+ protected:
+  void SetUp() override { zero_vector_registers(); }
+  void TearDown() override { zero_vector_registers(); }
+};
+% for fraction, sew in [(4, 32), (2, 16)]:
+% for lmul in [1, 2, 4, 8]:
+<%
+sign_type = "u" if "z" in op_code else ""
+src_type = "%sint%d_t" % (sign_type, sew/fraction)
+dest_type = "%sint%d_t" % (sign_type, sew)
+%>\
+TEST_F(${op_code.capitalize()}Test, VF${fraction}e${sew}m${lmul}) {
+  for (int i = 0; i < AVL_COUNT; i++) {
+    int32_t avl = AVLS[i];
+    int vlmax;
+    int vl;
+    std::tie(vlmax, vl) = vector_test_setup<${dest_type}>(
+        VLMUL::LMUL_M${lmul}, avl,
+        {src_vector_1, dest_vector, ref_dest_vector});
+    if (avl > vlmax) {
+      continue;
+    }
+    ${src_type} *ptr_vec_1 = reinterpret_cast<${src_type} *>(src_vector_1);
+    ${dest_type} *ptr_dest_vec = reinterpret_cast<${dest_type} *>(dest_vector);
+    ${dest_type} *ptr_ref_dest_vec = reinterpret_cast<${dest_type} *>(ref_dest_vector);
+    fill_random_vector<${src_type}>(ptr_vec_1, avl);
+    memset(dest_vector, 0, sizeof(dest_vector));
+    memset(ref_dest_vector, 0, sizeof(ref_dest_vector));
+    softrvv::${op_code}_v<${dest_type}, ${src_type}>(ptr_ref_dest_vec, ptr_vec_1, avl);
+    // Load vector registers
+    __asm__ volatile("vle${sew//fraction}.v v8, (%0)" : : "r"(ptr_vec_1));
+
+    // Run target instruction
+    __asm__ volatile("${op_code}.vf${fraction} v24, v8" ::);
+
+    // Store result vector register
+    __asm__ volatile("vse${sew}.v v24, (%0)" : : "r"(ptr_dest_vec));
+    // Check vector elements
+    assert_vec_elem_eq<${dest_type}>(vlmax, dest_vector, ref_dest_vector);
+  }
+}
+%endfor
+%endfor
+}  // namespace
+}  // namespace ${op_code}_test
+</%def>
+
+
diff --git a/tests/templates/opivv_opivx_opivi_test.tpl.cpp b/tests/templates/opivv_opivx_opivi_test.tpl.cpp
new file mode 100644
index 0000000..c6509c4
--- /dev/null
+++ b/tests/templates/opivv_opivx_opivi_test.tpl.cpp
@@ -0,0 +1,7 @@
+<%inherit file="base.tpl.cpp"/>\
+<%namespace name="tests_vi" file="base_opivi_test.tpl.cpp"/>\
+<%namespace name="tests_vx" file="base_opivx_test.tpl.cpp"/>\
+<%namespace name="tests_vv" file="base_opivv_test.tpl.cpp"/>\
+${tests_vi.test_opivi(op)}
+${tests_vx.test_opivx(op)}
+${tests_vv.test_opivv(op)}
diff --git a/tests/templates/opivv_opivx_test.tpl.cpp b/tests/templates/opivv_opivx_test.tpl.cpp
new file mode 100644
index 0000000..7567e3b
--- /dev/null
+++ b/tests/templates/opivv_opivx_test.tpl.cpp
@@ -0,0 +1,5 @@
+<%inherit file="base.tpl.cpp"/>\
+<%namespace name="tests_vx" file="base_opivx_test.tpl.cpp"/>\
+<%namespace name="tests_vv" file="base_opivv_test.tpl.cpp"/>\
+${tests_vx.test_opivx(op)}
+${tests_vv.test_opivv(op)}
diff --git a/tests/templates/opivx_opivi_test.tpl.cpp b/tests/templates/opivx_opivi_test.tpl.cpp
new file mode 100644
index 0000000..1b25072
--- /dev/null
+++ b/tests/templates/opivx_opivi_test.tpl.cpp
@@ -0,0 +1,5 @@
+<%inherit file="base.tpl.cpp"/>\
+<%namespace name="tests_vx" file="base_opivx_test.tpl.cpp"/>\
+<%namespace name="tests_vi" file="base_opivi_test.tpl.cpp"/>\
+${tests_vx.test_opivx(op)}
+${tests_vi.test_opivi(op)}
diff --git a/tests/templates/vxunary0_test.tpl.cpp b/tests/templates/vxunary0_test.tpl.cpp
index b43e968..b840e21 100644
--- a/tests/templates/vxunary0_test.tpl.cpp
+++ b/tests/templates/vxunary0_test.tpl.cpp
@@ -1,57 +1,4 @@
 <%inherit file="base.tpl.cpp"/>\
+<%namespace name="tests_vxunary0" file="base_vxunary0_test.tpl.cpp"/>\
+${tests_vxunary0.test_vxunary0(op)}
 
-namespace ${op_code}_test {
-namespace {
-
-using namespace test_v_helpers;
-
-uint8_t src_vector_1[MAXVL_BYTES];
-uint8_t dest_vector[MAXVL_BYTES];
-uint8_t ref_dest_vector[MAXVL_BYTES];
-
-class ${op_code.capitalize()}Test : public ::testing::Test {
- protected:
-  void SetUp() override { zero_vector_registers(); }
-  void TearDown() override { zero_vector_registers(); }
-};
-% for fraction, sew in [(4, 32), (2, 16)]:
-% for lmul in [1, 2, 4, 8]:
-<%
-sign_type = "u" if "z" in op_code else ""
-src_type = "%sint%d_t" % (sign_type, sew/fraction)
-dest_type = "%sint%d_t" % (sign_type, sew)
-%>\
-TEST_F(${op_code.capitalize()}Test, VF${fraction}e${sew}m${lmul}) {
-  for (int i = 0; i < AVL_COUNT; i++) {
-    int32_t avl = AVLS[i];
-    int vlmax;
-    int vl;
-    std::tie(vlmax, vl) = vector_test_setup<${dest_type}>(
-        VLMUL::LMUL_M${lmul}, avl,
-        {src_vector_1, dest_vector, ref_dest_vector});
-    if (avl > vlmax) {
-      continue;
-    }
-    ${src_type} *ptr_vec_1 = reinterpret_cast<${src_type} *>(src_vector_1);
-    ${dest_type} *ptr_dest_vec = reinterpret_cast<${dest_type} *>(dest_vector);
-    ${dest_type} *ptr_ref_dest_vec = reinterpret_cast<${dest_type} *>(ref_dest_vector);
-    fill_random_vector<${src_type}>(ptr_vec_1, avl);
-    memset(dest_vector, 0, sizeof(dest_vector));
-    memset(ref_dest_vector, 0, sizeof(ref_dest_vector));
-    softrvv::${op_code}_v<${dest_type}, ${src_type}>(ptr_ref_dest_vec, ptr_vec_1, avl);
-    // Load vector registers
-    __asm__ volatile("vle${sew//fraction}.v v8, (%0)" : : "r"(ptr_vec_1));
-
-    // Run target instruction
-    __asm__ volatile("${op_code}.vf${fraction} v24, v8" ::);
-
-    // Store result vector register
-    __asm__ volatile("vse${sew}.v v24, (%0)" : : "r"(ptr_dest_vec));
-    // Check vector elements
-    assert_vec_elem_eq<${dest_type}>(vlmax, dest_vector, ref_dest_vector);
-  }
-}
-%endfor
-%endfor
-}  // namespace
-}  // namespace ${op_code}_test