From e1b762b6e53db3f8191da6073542456eb587a6a9 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Sun, 6 Aug 2023 16:27:51 -0700 Subject: [PATCH] Draft commit of generic testbench CLI Signed-off-by: Alex Forencich --- cocotb_test/testbench.py | 109 +++++++++++++++++++++++++++++++++++++++ tests/test_parameters.py | 55 +++++++++++++++++++- tests/test_parameters.v | 8 ++- 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 cocotb_test/testbench.py mode change 100644 => 100755 tests/test_parameters.py diff --git a/cocotb_test/testbench.py b/cocotb_test/testbench.py new file mode 100644 index 0000000..fcef3f7 --- /dev/null +++ b/cocotb_test/testbench.py @@ -0,0 +1,109 @@ +import argparse +import cocotb_test + + +class Testbench: + def __init__(self): + self.template_parameter_list = [] + self.template_parameters = {} + + self.simulator = None + self.toplevel = None + self.module = None + self.work_dir = None + self.python_search = None + self.toplevel_lang = "verilog" + self.verilog_sources = None + self.vhdl_sources = None + self.includes = None + self.defines = None + self.parameters = {} + self.compile_args = None + self.vhdl_compile_args = None + self.verilog_compile_args = None + self.sim_args = None + self.extra_args = None + self.plus_args = None + self.force_compile = False + self.testcase = None + self.sim_build = "sim_build" + self.seed = None + self.extra_env = None + self.compile_only = False + self.waves = None + self.timescale = None + self.gui = False + self.simulation_args = None + self.simulator_kwargs = {} + + def add_template_parameter(self, name, val): + self.template_parameter_list.append(name) + self.template_parameters[name] = val + + def main(self): + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument("--simulator", default=None, help="specify simulator") + parser.add_argument("--waves", default=False, action="store_true", help="enable waveform dumping") + + for name, val in self.template_parameters.items(): + parser.add_argument(f"--param_{name.lower()}", default=str(val), metavar=name, help=f"set module parameter {name}") + + args = parser.parse_args() + + if args.simulator: + self.simulator = args.simulator + + if args.waves: + self.waves = True + + parameters = {} + + for name, val in self.template_parameters.items(): + arg_val = getattr(args, f"param_{name.lower()}") + if arg_val is not None: + parameters[name] = arg_val + + self.run(parameters=parameters) + + def run(self, parameters=None): + + sim_parameters = {} + + for name in self.template_parameters: + if name in parameters: + val = parameters[name] + else: + val = self.template_parameters[name] + + sim_parameters[name] = eval(str(val), {'__builtins__': None}, sim_parameters) + + cocotb_test.simulator.run( + simulator=self.simulator, + toplevel=self.toplevel, + module=self.module, + work_dir=self.work_dir, + python_search=self.python_search, + toplevel_lang=self.toplevel_lang, + verilog_sources=self.verilog_sources, + vhdl_sources=self.vhdl_sources, + includes=self.includes, + defines=self.defines, + parameters=sim_parameters, + compile_args=self.compile_args, + vhdl_compile_args=self.vhdl_compile_args, + verilog_compile_args=self.verilog_compile_args, + sim_args=self.sim_args, + extra_args=self.extra_args, + plus_args=self.plus_args, + force_compile=self.force_compile, + testcase=self.testcase, + sim_build=self.sim_build, + seed=self.seed, + extra_env=self.extra_env, + compile_only=self.compile_only, + waves=self.waves, + timescale=self.timescale, + gui=self.gui, + simulation_args=self.simulation_args + ) diff --git a/tests/test_parameters.py b/tests/test_parameters.py old mode 100644 new mode 100755 index f329bf8..a64f216 --- a/tests/test_parameters.py +++ b/tests/test_parameters.py @@ -1,6 +1,9 @@ +#!/usr/bin/env python3 from cocotb_test.simulator import run +from cocotb_test.testbench import Testbench import pytest import os +import subprocess import cocotb from cocotb.triggers import Timer @@ -14,12 +17,62 @@ def run_test_paramters(dut): yield Timer(1) WIDTH_IN = int(os.environ.get("WIDTH_IN", "8")) - WIDTH_OUT = int(os.environ.get("WIDTH_OUT", "8")) + WIDTH_OUT = int(os.environ.get("WIDTH_OUT", WIDTH_IN)) assert WIDTH_IN == len(dut.data_in) assert WIDTH_OUT == len(dut.data_out) +tb = Testbench() + +dut = "test_parameters" +tb.toplevel = dut +tb.module = os.path.splitext(os.path.basename(__file__))[0] + +tb.verilog_sources = [ + os.path.join(tests_dir, dut+".v"), +] + +tb.add_template_parameter('WIDTH_IN', 8) +tb.add_template_parameter('WIDTH_OUT', 'WIDTH_IN') + +tb.python_search = [tests_dir] +tb.includes = [os.path.join(tests_dir, "includes")] +tb.defines = ["DEFINE=1"] + +tb.force_compile = True + +if __name__ == '__main__': + tb.main() + + +@pytest.mark.skipif(os.getenv("SIM") == "ghdl", reason="Verilog not supported") +@pytest.mark.parametrize( + "parameters", [{"WIDTH_IN": "8", "WIDTH_OUT": "16"}, {"WIDTH_IN": "16"}] +) +def test_testbench_pytest(request, parameters): + tb.sim_build = os.path.join(tests_dir, "sim_build", + request.node.name.replace('[', '-').replace(']', '')) + tb.extra_env = parameters + tb.run(parameters=parameters) + + +@pytest.mark.skipif(os.getenv("SIM") == "ghdl", reason="Verilog not supported") +@pytest.mark.parametrize( + "parameters", [{"WIDTH_IN": "8", "WIDTH_OUT": "16"}, {"WIDTH_IN": "16"}] +) +def test_testbench_cli(request, parameters): + env = {} + for name, val in os.environ.items(): + env[name] = val + args = [__file__] + for name, val in parameters.items(): + args.append(f"--param_{name.lower()}={val}") + env[name] = val + c = subprocess.run(args, env=env) + assert c.returncode == 0 + + @pytest.mark.skipif(os.getenv("SIM") == "ghdl", reason="Verilog not suported") @pytest.mark.parametrize( "parameters", [{"WIDTH_IN": "8", "WIDTH_OUT": "16"}, {"WIDTH_IN": "16"}] diff --git a/tests/test_parameters.v b/tests/test_parameters.v index 47f4b7b..c89b15d 100644 --- a/tests/test_parameters.v +++ b/tests/test_parameters.v @@ -3,11 +3,17 @@ module test_parameters #( parameter WIDTH_IN = 8, - parameter WIDTH_OUT = 8 + parameter WIDTH_OUT = WIDTH_IN ) ( input [WIDTH_IN-1:0] data_in, output [WIDTH_OUT-1:0] data_out, output [`DEFINE-1:0] define_out ); +initial begin + $display("WIDTH_IN: %d", WIDTH_IN); + $display("WIDTH_OUT: %d", WIDTH_OUT); + $display("`DEFINE: %d", `DEFINE); +end + endmodule