diff --git a/examples/vhdl/vhdl_configuration/dff.vhd b/examples/vhdl/vhdl_configuration/dff.vhd new file mode 100644 index 000000000..b766c385d --- /dev/null +++ b/examples/vhdl/vhdl_configuration/dff.vhd @@ -0,0 +1,62 @@ +-- 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 ieee; +use ieee.std_logic_1164.all; + +entity dff is + generic( + width : positive := 8 + ); + port( + clk : in std_logic; + reset : in std_logic; + d : in std_logic_vector(width - 1 downto 0); + q : out std_logic_vector(width - 1 downto 0) + ); +end; + +architecture rtl of dff is +begin + process(clk) is + begin + if rising_edge(clk) then + if reset = '1' then + q <= (others => '0'); + else + q <= d; + end if; + end if; + end process; +end; + +configuration dff_rtl of tb_selecting_dut_with_vhdl_configuration is + for tb + for test_fixture + for dut : dff + use entity work.dff(rtl); + end for; + end for; + end for; +end; + +architecture behavioral of dff is +begin + process + begin + wait until rising_edge(clk); + q <= (others => '0') when reset else d; + end process; +end; + +configuration dff_behavioral of tb_selecting_dut_with_vhdl_configuration is + for tb + for test_fixture + for dut : dff + use entity work.dff(behavioral); + end for; + end for; + end for; +end; diff --git a/examples/vhdl/vhdl_configuration/run.py b/examples/vhdl/vhdl_configuration/run.py new file mode 100644 index 000000000..50546bdcb --- /dev/null +++ b/examples/vhdl/vhdl_configuration/run.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# 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 + +from pathlib import Path +from vunit import VUnit + +vu = VUnit.from_argv() +vu.add_vhdl_builtins() +lib = vu.add_library("lib") +root = Path(__file__).parent +lib.add_source_files(root / "*.vhd") + +# VHDL configurations are treated as a special case of the broader VUnit configuration +# concept. As such the configuration can be extended beyond the capabilities of a +# pure VHDL configuration. For example, by running with different generic values. +tb = lib.test_bench("tb_selecting_dut_with_vhdl_configuration") + +for vhdl_configuration_name in ["dff_rtl", "dff_behavioral"]: + for width in [8, 16]: + tb.add_config( + name=f"{vhdl_configuration_name}_{width}", + generics=dict(width=width), + vhdl_configuration_name=vhdl_configuration_name, + ) + +# A top-level VHDL configuration is bound to an entity, i.e. the testbench. However, +# when handled as part of VUnit configurations it can also be applied to a +# single test case +tb.test("Test reset").add_config(name="dff_rtl_32", generics=dict(width=32), vhdl_configuration_name="dff_rtl") + + +# If the test runner is placed in a component instantiated into the testbench, different architectures of that +# component can implement different tests and VHDL configurations can be used to select what test to run. +# This is the approach taken by a traditional OSVVM testbench. In VUnit, such a test becomes a VUnit configuration +# selecting the associated VHDL configuration rather than a VUnit test case, but that is of less importance. Note that +# this approach is limited in that the test runner architecture can't contain a test suite with explicit test cases +# (run function calls) but only the test_runner_setup and test_runner_cleanup calls. Should you need multiple test +# suites sharing the same test fixture (the DUT and the surrounding verification components), the proper approach +# is to put each test suite in its own testbench and make the test fixture a component reused between the testbenches. +# That approach do not require any VHDL configurations. +tb = lib.test_bench("tb_selecting_test_runner_with_vhdl_configuration") +for vhdl_configuration_name in ["test_reset", "test_state_change"]: + for width in [8, 16]: + tb.add_config( + name=f"{vhdl_configuration_name}_{width}", + generics=dict(width=width), + vhdl_configuration_name=vhdl_configuration_name, + ) + +vu.main() diff --git a/examples/vhdl/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd b/examples/vhdl/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd new file mode 100644 index 000000000..149c898f0 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd @@ -0,0 +1,89 @@ +-- 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 +-- +-- Description: This is an example of a testbench using VHDL configurations +-- to select DUT architecture + +library vunit_lib; +context vunit_lib.vunit_context; + +library ieee; +use ieee.std_logic_1164.all; + +entity tb_selecting_dut_with_vhdl_configuration is + generic( + runner_cfg : string; + width : positive + ); +end entity; + +architecture tb of tb_selecting_dut_with_vhdl_configuration is + constant clk_period : time := 10 ns; + + signal reset : std_logic; + signal clk : std_logic := '0'; + signal d : std_logic_vector(width - 1 downto 0); + signal q : std_logic_vector(width - 1 downto 0); + + component dff is + generic( + width : positive := width + ); + port( + clk : in std_logic; + reset : in std_logic; + d : in std_logic_vector(width - 1 downto 0); + q : out std_logic_vector(width - 1 downto 0) + ); + end component; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test reset") then + d <= (others => '1'); + reset <= '1'; + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + + elsif run("Test state change") then + reset <= '0'; + + d <= (others => '1'); + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, std_logic_vector'(q'range => '1')); + + d <= (others => '0'); + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + end if; + end loop; + + test_runner_cleanup(runner); + end process; + + test_fixture : block is + begin + clk <= not clk after clk_period / 2; + + dut : dff + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + end block; +end architecture; diff --git a/examples/vhdl/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd b/examples/vhdl/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd new file mode 100644 index 000000000..be076ddd9 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd @@ -0,0 +1,75 @@ +-- 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 +-- +-- Description: This is an example of a testbench using separate architectures +-- of a test runner entity to define different tests. This is a structure +-- found in OSVVM-native testbenches + +library vunit_lib; +context vunit_lib.vunit_context; + +library ieee; +use ieee.std_logic_1164.all; + +entity tb_selecting_test_runner_with_vhdl_configuration is + generic( + runner_cfg : string; + width : positive + ); +end entity; + +architecture tb of tb_selecting_test_runner_with_vhdl_configuration is + constant clk_period : time := 10 ns; + + signal reset : std_logic; + signal clk : std_logic := '0'; + signal d : std_logic_vector(width - 1 downto 0); + signal q : std_logic_vector(width - 1 downto 0); + + component test_runner is + generic( + clk_period : time; + width : positive; + nested_runner_cfg : string + ); + port( + reset : out std_logic; + clk : in std_logic; + d : out std_logic_vector(width - 1 downto 0); + q : in std_logic_vector(width - 1 downto 0) + ); + end component; + +begin + test_runner_inst : test_runner + generic map( + clk_period => clk_period, + width => width, + nested_runner_cfg => runner_cfg + ) + port map( + reset => reset, + clk => clk, + d => d, + q => q + ); + + test_fixture : block is + begin + clk <= not clk after clk_period / 2; + + dut : entity work.dff(rtl) + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + end block; +end architecture; diff --git a/examples/vhdl/vhdl_configuration/test_reset.vhd b/examples/vhdl/vhdl_configuration/test_reset.vhd new file mode 100644 index 000000000..76f5f0857 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/test_reset.vhd @@ -0,0 +1,37 @@ +-- 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; + +library ieee; +use ieee.std_logic_1164.all; + +architecture test_reset_a of test_runner is +begin + main : process + begin + test_runner_setup(runner, nested_runner_cfg); + + d <= (others => '1'); + reset <= '1'; + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 * clk_period); +end; + +configuration test_reset of tb_selecting_test_runner_with_vhdl_configuration is + for tb + for test_runner_inst : test_runner + use entity work.test_runner(test_reset_a); + end for; + end for; +end; diff --git a/examples/vhdl/vhdl_configuration/test_runner.vhd b/examples/vhdl/vhdl_configuration/test_runner.vhd new file mode 100644 index 000000000..8cf2e3ab3 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/test_runner.vhd @@ -0,0 +1,22 @@ +-- 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 ieee; +use ieee.std_logic_1164.all; + +entity test_runner is + generic( + clk_period : time; + width : positive; + nested_runner_cfg : string + ); + port( + reset : out std_logic; + clk : in std_logic; + d : out std_logic_vector(width - 1 downto 0); + q : in std_logic_vector(width - 1 downto 0) + ); +end entity; diff --git a/examples/vhdl/vhdl_configuration/test_state_change.vhd b/examples/vhdl/vhdl_configuration/test_state_change.vhd new file mode 100644 index 000000000..aadf97bcd --- /dev/null +++ b/examples/vhdl/vhdl_configuration/test_state_change.vhd @@ -0,0 +1,43 @@ +-- 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; + +library ieee; +use ieee.std_logic_1164.all; + +architecture test_state_change_a of test_runner is +begin + main : process + begin + test_runner_setup(runner, nested_runner_cfg); + + reset <= '0'; + + d <= (others => '1'); + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, std_logic_vector'(q'range => '1')); + + d <= (others => '0'); + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 * clk_period); +end; + +configuration test_state_change of tb_selecting_test_runner_with_vhdl_configuration is + for tb + for test_runner_inst : test_runner + use entity work.test_runner(test_state_change_a); + end for; + end for; +end; diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index 3773120b6..014ffb913 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -176,6 +176,27 @@ def test_vhdl_composite_generics_example_project(self): ], ) + def test_vhdl_configuration_example_project(self): + self.check(ROOT / "examples/vhdl/vhdl_configuration/run.py") + check_report( + self.report_file, + [ + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_8"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_rtl_16.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_rtl_8.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_rtl_32.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_behavioral_16.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_behavioral_16.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_rtl_16.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_behavioral_8.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_behavioral_8.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.dff_rtl_8.Test reset"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_8"), + ], + ) + @mark.xfail( not (simulator_is("ghdl") or simulator_is("nvc")), reason="Support complex JSON strings as generic", diff --git a/vunit/ui/test.py b/vunit/ui/test.py index 1e912df86..8b6a2d8f0 100644 --- a/vunit/ui/test.py +++ b/vunit/ui/test.py @@ -51,8 +51,7 @@ def add_config( # pylint: disable=too-many-arguments :param post_check: A :ref:`callback function ` to be called after test execution. :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration. :param attributes: A `dict` containing the attributes to be set in addition to the default configuration. - :param vhdl_configuration_name : Name of VHDL configuration to use for the testbench entity or None if no - VHDL configuration shall be used. + :param vhdl_configuration_name : Name of VHDL configuration to use for the testbench entity, if any. :example: diff --git a/vunit/ui/testbench.py b/vunit/ui/testbench.py index 727f9a1c3..789e2ddc6 100644 --- a/vunit/ui/testbench.py +++ b/vunit/ui/testbench.py @@ -145,8 +145,7 @@ def add_config( # pylint: disable=too-many-arguments :param post_check: A :ref:`callback function ` to be called after test execution. :param sim_options: A `dict` containing the sim_options to be set in addition to the default configuration :param attributes: A `dict` containing the attributes to be set in addition to the default configuration - :param vhdl_configuration_name : Name of VHDL configuration to use for the testbench entity or None if no - VHDL configuration shall be used. + :param vhdl_configuration_name : Name of VHDL configuration to use for the testbench entity, if any. :example: