diff --git a/tests/acceptance/artificial/vhdl/cfg1.vhd b/tests/acceptance/artificial/vhdl/cfg1.vhd new file mode 100644 index 000000000..6f4caf2c5 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/cfg1.vhd @@ -0,0 +1,18 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +architecture arch1 of ent is +begin + arch <= "arch1"; +end; + +configuration cfg1 of tb_with_vhdl_configuration is + for tb + for ent_inst : ent + use entity work.ent(arch1); + end for; + end for; +end; \ No newline at end of file diff --git a/tests/acceptance/artificial/vhdl/cfg2.vhd b/tests/acceptance/artificial/vhdl/cfg2.vhd new file mode 100644 index 000000000..12e5ea30c --- /dev/null +++ b/tests/acceptance/artificial/vhdl/cfg2.vhd @@ -0,0 +1,18 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +architecture arch2 of ent is +begin + arch <= "arch2"; +end; + +configuration cfg2 of tb_with_vhdl_configuration is + for tb + for ent_inst : ent + use entity work.ent(arch2); + end for; + end for; +end; \ No newline at end of file diff --git a/tests/acceptance/artificial/vhdl/ent.vhd b/tests/acceptance/artificial/vhdl/ent.vhd new file mode 100644 index 000000000..cda1ce325 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/ent.vhd @@ -0,0 +1,9 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +entity ent is + port(arch : out string(1 to 5)); +end; \ No newline at end of file diff --git a/tests/acceptance/artificial/vhdl/run.py b/tests/acceptance/artificial/vhdl/run.py index b0699e7fa..544fd5086 100644 --- a/tests/acceptance/artificial/vhdl/run.py +++ b/tests/acceptance/artificial/vhdl/run.py @@ -94,10 +94,27 @@ def configure_tb_assert_stop_level(ui): test.set_sim_option("vhdl_assert_stop_level", vhdl_assert_stop_level) +def configure_tb_with_vhdl_configuration(ui): + def make_post_check(expected_arch): + def post_check(output_path): + arch = (Path(output_path) / "result.txt").read_text() + if arch[:-1] != expected_arch: + raise RuntimeError(f"Error! Got {arch[: -1]}, expected {expected_arch}") + + return True + + return post_check + + tb = ui.library("lib").entity("tb_with_vhdl_configuration") + tb.add_config(name="cfg1", post_check=make_post_check("arch1"), vhdl_configuration_name="cfg1") + tb.add_config(name="cfg2", post_check=make_post_check("arch2"), vhdl_configuration_name="cfg2") + + configure_tb_with_generic_config() configure_tb_same_sim_all_pass(vu) configure_tb_set_generic(vu) configure_tb_assert_stop_level(vu) +configure_tb_with_vhdl_configuration(vu) lib.entity("tb_no_generic_override").set_generic("g_val", False) lib.entity("tb_ieee_warning").test("pass").set_sim_option("disable_ieee_warnings", True) lib.entity("tb_other_file_tests").scan_tests_from_file(str(root / "other_file_tests.vhd")) diff --git a/tests/acceptance/artificial/vhdl/tb_with_vhdl_configuration.vhd b/tests/acceptance/artificial/vhdl/tb_with_vhdl_configuration.vhd new file mode 100644 index 000000000..9b2d8e303 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/tb_with_vhdl_configuration.vhd @@ -0,0 +1,46 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2023, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; + +use std.textio.all; + +entity tb_with_vhdl_configuration is + generic(runner_cfg : string); +end entity; + +architecture tb of tb_with_vhdl_configuration is + component ent + port(arch : out string(1 to 5)); + end component; + + signal arch : string(1 to 5); + +begin + test_runner : process + file result_fptr : text; + variable result_line : line; + begin + test_runner_setup(runner, runner_cfg); + + file_open(result_fptr, join(output_path(runner_cfg), "result.txt"), write_mode); + write(result_line, arch); + writeline(result_fptr, result_line); + file_close(result_fptr); + + info(arch); + + test_runner_cleanup(runner); + wait; + end process; + + ent_inst : ent + port map( + arch => arch + ); + +end architecture; diff --git a/tests/acceptance/test_artificial.py b/tests/acceptance/test_artificial.py index bc6d28093..8ac0b0217 100644 --- a/tests/acceptance/test_artificial.py +++ b/tests/acceptance/test_artificial.py @@ -245,4 +245,12 @@ def test_exit_0_flag(self): "failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = failure", ), + ( + "passed", + "lib.tb_with_vhdl_configuration.cfg1", + ), + ( + "passed", + "lib.tb_with_vhdl_configuration.cfg2", + ), ) diff --git a/tests/unit/test_incisive_interface.py b/tests/unit/test_incisive_interface.py index fe53217ea..ffbf5b72c 100644 --- a/tests/unit/test_incisive_interface.py +++ b/tests/unit/test_incisive_interface.py @@ -21,6 +21,8 @@ from vunit.ostools import renew_path, write_file, read_file from vunit.test.bench import Configuration from vunit.vhdl_standard import VHDL +from tests.common import create_tempdir +from tests.unit.test_test_bench import Entity class TestIncisiveInterface(unittest.TestCase): @@ -956,6 +958,21 @@ def test_simulate_gui(self, run_command, find_cds_root_irun, find_cds_root_virtu ], ) + @mock.patch("vunit.sim_if.incisive.IncisiveInterface.find_cds_root_virtuoso") + @mock.patch("vunit.sim_if.incisive.IncisiveInterface.find_cds_root_irun") + def test_configuration_and_entity_selection(self, find_cds_root_irun, find_cds_root_virtuoso): + find_cds_root_irun.return_value = "cds_root_irun" + find_cds_root_virtuoso.return_value = None + + with create_tempdir() as tempdir: + design_unit = Entity("tb_entity", file_name=str(Path(tempdir) / "file.vhd")) + design_unit.generic_names = ["runner_cfg"] + config = Configuration("name", design_unit, vhdl_configuration_name="cfg") + simif = IncisiveInterface(prefix="prefix", output_path=self.output_path) + self.assertEqual(simif._select_vhdl_top(config), "cfg") # pylint: disable=protected-access + config = Configuration("name", design_unit) + self.assertEqual(simif._select_vhdl_top(config), "lib.tb_entity:arch") # pylint: disable=protected-access + def setUp(self): self.output_path = str(Path(__file__).parent / "test_incisive_out") renew_path(self.output_path) @@ -985,4 +1002,5 @@ def make_config(sim_options=None, generics=None, verilog=False): cfg.sim_options = {} if sim_options is None else sim_options cfg.generics = {} if generics is None else generics + cfg.vhdl_configuration_name = None return cfg diff --git a/vunit/configuration.py b/vunit/configuration.py index bfccbfe2d..8876fa5d1 100644 --- a/vunit/configuration.py +++ b/vunit/configuration.py @@ -39,12 +39,14 @@ def __init__( # pylint: disable=too-many-arguments pre_config=None, post_check=None, attributes=None, + vhdl_configuration_name=None, ): self.name = name self._design_unit = design_unit self.generics = {} if generics is None else generics self.sim_options = {} if sim_options is None else sim_options self.attributes = {} if attributes is None else attributes + self.vhdl_configuration_name = vhdl_configuration_name self.tb_path = str(Path(design_unit.original_file_name).parent) @@ -64,6 +66,7 @@ def copy(self): pre_config=self.pre_config, post_check=self.post_check, attributes=self.attributes.copy(), + vhdl_configuration_name=self.vhdl_configuration_name, ) @property @@ -102,6 +105,12 @@ def set_attribute(self, name, value): else: raise AttributeException + def set_vhdl_configuration_name(self, name): + """ + Set VHDL configuration name + """ + self.vhdl_configuration_name = name + def set_generic(self, name, value): """ Set generic @@ -248,6 +257,7 @@ def add_config( # pylint: disable=too-many-arguments post_check=None, sim_options=None, attributes=None, + vhdl_configuration_name=None, ): """ Add a configuration copying unset fields from the default configuration: @@ -283,4 +293,7 @@ def add_config( # pylint: disable=too-many-arguments raise AttributeException config.attributes.update(attributes) + if vhdl_configuration_name is not None: + config.vhdl_configuration_name = vhdl_configuration_name + configs[config.name] = config diff --git a/vunit/sim_if/activehdl.py b/vunit/sim_if/activehdl.py index d73f6d175..8cc45ec8c 100644 --- a/vunit/sim_if/activehdl.py +++ b/vunit/sim_if/activehdl.py @@ -239,11 +239,14 @@ def _create_load_function(self, config, output_path): set_generic_name_str, "-lib", config.library_name, - config.entity_name, ] - if config.architecture_name is not None: - vsim_flags.append(config.architecture_name) + if config.vhdl_configuration_name is None: + vsim_flags.append(config.entity_name) + if config.architecture_name is not None: + vsim_flags.append(config.architecture_name) + else: + vsim_flags.append(config.vhdl_configuration_name) if config.sim_options.get("enable_coverage", False): coverage_file_path = str(Path(output_path) / "coverage.acdb") diff --git a/vunit/sim_if/ghdl.py b/vunit/sim_if/ghdl.py index 13f36f7d1..c083d4ff3 100644 --- a/vunit/sim_if/ghdl.py +++ b/vunit/sim_if/ghdl.py @@ -279,7 +279,11 @@ def _get_command(self, config, output_path, elaborate_only, ghdl_e, wave_file): if config.sim_options.get("enable_coverage", False): # Enable coverage in linker cmd += ["-Wl,-lgcov"] - cmd += [config.entity_name, config.architecture_name] + + if config.vhdl_configuration_name is not None: + cmd += [config.vhdl_configuration_name] + else: + cmd += [config.entity_name, config.architecture_name] sim = config.sim_options.get("ghdl.sim_flags", []) for name, value in config.generics.items(): diff --git a/vunit/sim_if/incisive.py b/vunit/sim_if/incisive.py index ede8fce32..e800d80a1 100644 --- a/vunit/sim_if/incisive.py +++ b/vunit/sim_if/incisive.py @@ -277,7 +277,17 @@ def _get_mapped_libraries(self): cds = CDSFile.parse(self._cdslib) return cds - def simulate(self, output_path, test_suite_name, config, elaborate_only=False): # pylint: disable=too-many-locals + @staticmethod + def _select_vhdl_top(config): + "Select VHDL configuration or entity as top." + if config.vhdl_configuration_name is None: + return f"{config.library_name!s}.{config.entity_name!s}:{config.architecture_name!s}" + + return f"{config.vhdl_configuration_name!s}" + + def simulate( + self, output_path, test_suite_name, config, elaborate_only=False + ): # pylint: disable=too-many-locals,too-many-branches """ Elaborates and Simulates with entity as top level using generics """ @@ -336,7 +346,8 @@ def simulate(self, output_path, test_suite_name, config, elaborate_only=False): args += [f"-top {config.library_name!s}.{config.entity_name!s}:sv"] else: # we have a VHDL toplevel: - args += [f"-top {config.library_name!s}.{config.entity_name!s}:{config.architecture_name!s}"] + args += [f"-top {self._select_vhdl_top(config)}"] + argsfile = f"{script_path!s}/irun_{step!s}.args" write_file(argsfile, "\n".join(args)) if not run_command( diff --git a/vunit/sim_if/modelsim.py b/vunit/sim_if/modelsim.py index 0acd589c3..c2fd3d309 100644 --- a/vunit/sim_if/modelsim.py +++ b/vunit/sim_if/modelsim.py @@ -258,6 +258,12 @@ def _create_load_function(self, test_suite_name, config, output_path): coverage_save_cmd = "" coverage_args = "" + simulation_target = ( + config.library_name + "." + config.entity_name + architecture_suffix + if config.vhdl_configuration_name is None + else config.library_name + "." + config.vhdl_configuration_name + ) + vsim_flags = [ f"-wlf {{{fix_path(str(Path(output_path) / 'vsim.wlf'))!s}}}", "-quiet", @@ -266,7 +272,7 @@ def _create_load_function(self, test_suite_name, config, output_path): "-onfinish stop", pli_str, set_generic_str, - config.library_name + "." + config.entity_name + architecture_suffix, + simulation_target, coverage_args, self._vsim_extra_args(config), ] diff --git a/vunit/sim_if/nvc.py b/vunit/sim_if/nvc.py index bb7165af7..c78dedac2 100644 --- a/vunit/sim_if/nvc.py +++ b/vunit/sim_if/nvc.py @@ -235,7 +235,7 @@ def compile_vhdl_file_command(self, source_file): cmd += [source_file.name] return cmd - def simulate(self, output_path, test_suite_name, config, elaborate_only): + def simulate(self, output_path, test_suite_name, config, elaborate_only): # pylint: disable=too-many-branches """ Simulate with entity as top level using generics """ @@ -261,7 +261,10 @@ def simulate(self, output_path, test_suite_name, config, elaborate_only): cmd += ["-e"] cmd += config.sim_options.get("nvc.elab_flags", []) - cmd += [f"{config.entity_name}-{config.architecture_name}"] + if config.vhdl_configuration_name is not None: + cmd += [config.vhdl_configuration_name] + else: + cmd += [f"{config.entity_name}-{config.architecture_name}"] for name, value in config.generics.items(): cmd += [f"-g{name}={value}"] diff --git a/vunit/sim_if/rivierapro.py b/vunit/sim_if/rivierapro.py index cde7dd85b..07015762c 100644 --- a/vunit/sim_if/rivierapro.py +++ b/vunit/sim_if/rivierapro.py @@ -306,12 +306,17 @@ def _create_load_function(self, test_suite_name, config, output_path): # pylint if config.sim_options.get("disable_ieee_warnings", False): vsim_flags.append("-ieee_nowarn") - # Add the the testbench top-level unit last as coverage is - # only collected for the top-level unit specified last - vsim_flags += ["-lib", config.library_name, config.entity_name] + vsim_flags += ["-lib", config.library_name] - if config.architecture_name is not None: - vsim_flags.append(config.architecture_name) + if config.vhdl_configuration_name is None: + # Add the the testbench top-level unit last as coverage is + # only collected for the top-level unit specified last + vsim_flags += [config.entity_name] + + if config.architecture_name is not None: + vsim_flags.append(config.architecture_name) + else: + vsim_flags += [config.vhdl_configuration_name] tcl = """ proc vunit_load {{}} {{ diff --git a/vunit/ui/testbench.py b/vunit/ui/testbench.py index 3ca643063..efb2fc7de 100644 --- a/vunit/ui/testbench.py +++ b/vunit/ui/testbench.py @@ -130,6 +130,7 @@ def add_config( # pylint: disable=too-many-arguments post_check=None, sim_options=None, attributes=None, + vhdl_configuration_name=None, ): """ Add a configuration of this test bench or to all test cases within it by copying the default configuration. @@ -181,6 +182,7 @@ def add_config( # pylint: disable=too-many-arguments post_check=post_check, sim_options=sim_options, attributes=attributes, + vhdl_configuration_name=vhdl_configuration_name, ) def test(self, name):