diff --git a/docs/blog/2023_08_26_vhdl_configurations.rst b/docs/blog/2023_08_26_vhdl_configurations.rst new file mode 100644 index 000000000..f9b9863a8 --- /dev/null +++ b/docs/blog/2023_08_26_vhdl_configurations.rst @@ -0,0 +1,159 @@ +:tags: VUnit, OSVVM, configurations +:author: lasplund +:excerpt: 1 + +Improved Support for VHDL Configurations and OSVVM +================================================== + +For quite some time, several initiatives have been underway to improve the integration between VUnit and OSVVM. Examples +of these efforts are the `external logging framework integration feature +`__ and the OSVVM pull request +`#81 `__. + +Another example is the introduction of support for top-level VHDL configurations which serves several purposes, for +example: + +1. Enabling the selection of the Device Under Test (DUT) to be used in a VUnit testbench. +2. Direct support for running conventional OSVVM testbenches within the VUnit framework. + +In this blog, we will primarily focus on top-level configurations but before delving into the specifics of these use cases, we will describe how VUnit addressed these issues in the past. + +Selecting DUT Using Generics +---------------------------- + +Sometimes the VHDL DUT comes in different variants (architectures) and there is a need to verify all of these with the +same testbench. It could be an FPGA and an ASIC implementation or an RTL and a behavioral architecture. Before +supporting VHDL configurations, VUnit addressed this issue with a combination of generics and an if-generate statement +as showed in the example below. For the purpose of this blog, we have removed the complexities typically found in +real-world designs and chosen to focus on the fundamental principles. Thus, we will use a simple variable-width +flip-flop as the DUT for our demonstrations. + +.. raw:: html + :file: img/vhdl_configuration/selecting_dut_with_generics.html + +This approach is straightforward: simply copy and paste the flip-flop instantiation, but modify the architecture to use +based on the ``dut_arch`` generic. While the approach is simple it also introduces code duplication which can be a bit +dangerous. In this case, since the copies are placed adjacent to each other, the risk of inadvertently changing one +without updating the other is somewhat mitigated. + +If your DUT has numerous ports, you can consider leveraging the VHDL-2019 interface construct as a means to raise the +level of abstraction and reduce code duplication. This approach allows for a more concise representation of the design, +provided your simulator supports the latest VHDL standard. + +.. NOTE:: + There is a proposed update to the VHDL standard related to this topic as it would fully remove the code duplication. + `Issue #235 `__ proposes that a string should be possible + to use when specifying the architecture in an entity instantiation, i.e. ``"rtl"`` or ``"behavioral"`` rather than + ``rtl`` or ``behavioral``. In our example we would simply have a single entity instantiation which architecture is + specified with the ``dut_arch`` generic. + +The various settings of the ``dut_arch`` generic are handled with a VUnit configuration in the Python run script. +Initially, the use of both VUnit and VHDL configuration concepts may appear confusing, but we will soon see that a VHDL +configuration is a special case of the broader VUnit configuration concept. In this example, we are also testing the DUT +with multiple ``width`` settings. Note how we can use the ``product`` function from ``itertools`` to iterate over all +combinations of ``dut_arch`` and ``width``. This is equivalent to two nested loops over these generics but scales better +as the number of generics to combine increases. + +.. raw:: html + :file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut.html + +If we list all the tests, we will see that there are four for each test +case in the testbench, one for each combination of ``dut_arch`` and ``width``: + +.. raw:: html + :file: img/vhdl_configuration/tb_selecting_dut_with_generics_stdout.html + +Selecting DUT Using VHDL Configurations +--------------------------------------- + +When using VHDL configurations we need three ingredients in our testbench + +1. A component declaration for the DUT. In the example below it has been + placed in the declarative part of the testbench architecture but it + can also be placed in a separate package. +2. A component instantiation of the declared component. Note that the + ``component`` keyword is optional and can be excluded. +3. A configuration declaration for each DUT architecture + +.. raw:: html + :file: img/vhdl_configuration/selecting_dut_with_vhdl_configuration.html + +Instead of assigning a generic to select our architecture, we now specify which VHDL configuration our VUnit configuration should use: + +.. raw:: html + :file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration.html + +Incorporating VHDL configurations within VUnit configurations brings forth another advantage. From a VHDL point of view, +VHDL configurations are linked to entities, such as the testbench entity in our scenario. However, a VUnit configuration +can also be applied to specific test cases, opening up the possibility of using VHDL configurations at that finer level +of granularity. For instance, consider a situation where we have an FPGA and an ASIC implementation/architecure that +differ only in the memory IPs they use. In such a case, it might be sufficient to simulate only one of the architectures +for the test cases not involving memory operations. + +To illustrate this using the flip-flop example, let's create a test where we set ``width`` to 32 and exclusively +simulate it using the RTL architecture: + +.. raw:: html + :file: img/vhdl_configuration/vhdl_configuration_on_a_test_case.html + +Now, we have an additional entry in our list of tests: + +.. raw:: html + :file: img/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration_stdout.html + +Choosing between VHDL configurations and generics is primarily a matter of personal preference. The generic approach led +us to multiple direct entity instantiations and code duplication. However, the configuration approach demands a +component declaration, which essentially duplicates the DUT entity declaration. Additionally, VHDL configuration +declarations are also necessary. + +Selecting Test Runner Using VHDL Configurations +----------------------------------------------- + +In the previous examples, the VUnit test cases were located in a process called ``test_runner`` residing alongside the +DUT. This is the most straightforward arrangement, as it provides the test cases with direct access to the DUT's +interface. An alternative approach involves encapsulating ``test_runner`` within an entity, which is subsequently +instantiated within the testbench. Such a ``test_runner`` entity needs access to the ``runner_cfg`` and ``width`` +generics, in addition to the ``clk_period`` constant and the interface ports of the DUT. + +.. raw:: html + :file: img/vhdl_configuration/test_runner_entity.html + +Note that the runner configuration generic is called ``nested_runner_cfg`` and not ``runner_cfg``. The reason is that +``runner_cfg`` is the signature used to identify a testbench, the top-level of a simulation. The ``test_runner`` entity +is not a simulation top-level and must not be mistaken as such. + +We can now replace the testbench ``test_runner`` process and watchdog with an instantiation of this component: + +.. raw:: html + :file: img/vhdl_configuration/test_runner_component_instantiation.html + +Having relocated ``test_runner`` into an entity, we can have VHDL configurations selecting which test runner to use, and +let each such test runner represent a single test. This setup is the conventional methodology seen in OSVVM +testbenches. With VUnit's extended support for VHDL configurations, it becomes possible to keep that structure when +adding VUnit capabilities. For example, this is the architecture for the reset test: + +.. raw:: html + :file: img/vhdl_configuration/test_reset_architecture_of_test_runner.html + +.. NOTE:: + When using several configurations to select what test runner to use, each test runner can only contain a single test, i.e. no test cases specified by the use of the ``run`` function are allowed. + +Below are the two configurations that select this particular test along with one of the ``rtl`` and ``behavioral`` +architectures for the DUT: + +.. raw:: html + :file: img/vhdl_configuration/test_reset_configurations.html + +This example highlights a drawback of VHDL configurations: every combination of architectures to use in a test has to be +manually created. When we use generics and if generate statements to select architectures, we create all combinations +**programatically** in the Python script using the ``itertools.product`` function. Despite this, Python can continue to +play a role in alleviating certain aspects of the combinatorial workload: + +.. raw:: html + :file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration.html + +.. raw:: html + :file: img/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration_stdout.html + +That concludes our discussion for now. As always, we highly value your feedback and appreciate any insights you might have to offer. + diff --git a/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut.html b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut.html new file mode 100644 index 000000000..e427b3b16 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut.html @@ -0,0 +1,8 @@ +
tb = lib.test_bench("tb_selecting_dut_with_generics")
+
+for dut_arch, width in itertools.product(["rtl", "behavioral"], [8, 16]):
+    tb.add_config(
+        name=f"{dut_arch}_{width}",
+        generics=dict(width=width, dff_arch=dut_arch),
+    )
+
diff --git a/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration.html b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration.html new file mode 100644 index 000000000..13fda5cbc --- /dev/null +++ b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration.html @@ -0,0 +1,12 @@ +
tb = lib.test_bench("tb_selecting_test_runner_with_vhdl_configuration")
+
+for dut_arch, width, test_case_name in itertools.product(
+    ["rtl", "behavioral"], [8, 16], ["test_reset", "test_state_change"]
+):
+    vhdl_configuration_name = f"{test_case_name}_{dut_arch}"
+    tb.add_config(
+        name=f"{vhdl_configuration_name}_{width}",
+        generics=dict(width=width),
+        vhdl_configuration_name=vhdl_configuration_name,
+    )
+
diff --git a/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration.html b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration.html new file mode 100644 index 000000000..697ebaf5c --- /dev/null +++ b/docs/blog/img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration.html @@ -0,0 +1,9 @@ +
tb = lib.test_bench("tb_selecting_dut_with_vhdl_configuration")
+
+for dut_arch, width in itertools.product(["rtl", "behavioral"], [8, 16]):
+    tb.add_config(
+        name=f"{dut_arch}_{width}",
+        generics=dict(width=width),
+        vhdl_configuration_name=dut_arch,
+    )
+
diff --git a/docs/blog/img/vhdl_configuration/selecting_dut_with_generics.html b/docs/blog/img/vhdl_configuration/selecting_dut_with_generics.html new file mode 100644 index 000000000..7a651d94b --- /dev/null +++ b/docs/blog/img/vhdl_configuration/selecting_dut_with_generics.html @@ -0,0 +1,62 @@ +
entity tb_selecting_dut_with_generics is
+  generic(
+    runner_cfg : string;
+    width : positive;
+    dff_arch : string
+  );
+end entity;
+
+architecture tb of tb_selecting_dut_with_generics is
+  ...
+begin
+  test_runner : process
+  begin
+    test_runner_setup(runner, runner_cfg);
+
+    while test_suite loop
+      if run("Test reset") then
+        ...
+      elsif run("Test state change") then
+        ...
+      end if;
+    end loop;
+
+    test_runner_cleanup(runner);
+  end process;
+
+  test_runner_watchdog(runner, 10 * clk_period);
+
+  test_fixture : block is
+  begin
+    clk <= not clk after clk_period / 2;
+
+    dut_selection : if dff_arch = "rtl" generate
+      dut : entity work.dff(rtl)
+        generic map(
+          width => width
+        )
+        port map(
+          clk => clk,
+          reset => reset,
+          d => d,
+          q => q
+        );
+
+    elsif dff_arch = "behavioral" generate
+      dut : entity work.dff(behavioral)
+        generic map(
+          width => width
+        )
+        port map(
+          clk => clk,
+          reset => reset,
+          d => d,
+          q => q
+        );
+
+    else generate
+      error("Unknown DFF architecture");
+    end generate;
+  end block;
+end architecture;
+
diff --git a/docs/blog/img/vhdl_configuration/selecting_dut_with_vhdl_configuration.html b/docs/blog/img/vhdl_configuration/selecting_dut_with_vhdl_configuration.html new file mode 100644 index 000000000..d4a347bab --- /dev/null +++ b/docs/blog/img/vhdl_configuration/selecting_dut_with_vhdl_configuration.html @@ -0,0 +1,79 @@ +
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
+  ...
+
+  -- Component declaration
+  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
+        ...
+      elsif run("Test state change") then
+        ...
+      end if;
+    end loop;
+
+    test_runner_cleanup(runner);
+  end process;
+
+  test_runner_watchdog(runner, 10 * clk_period);
+
+  test_fixture : block is
+  begin
+    clk <= not clk after clk_period / 2;
+
+    -- Component instantiation
+    dut : component dff
+      generic map(
+        width => width
+      )
+      port map(
+        clk => clk,
+        reset => reset,
+        d => d,
+        q => q
+      );
+  end block;
+end architecture;
+
+-- Configuration declarations
+configuration 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;
+
+configuration 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/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_generics_stdout.html b/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_generics_stdout.html new file mode 100644 index 000000000..68103bbe3 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_generics_stdout.html @@ -0,0 +1,11 @@ +
> python run.py --list
+lib.tb_selecting_dut_with_generics.rtl_8.Test reset
+lib.tb_selecting_dut_with_generics.rtl_16.Test reset
+lib.tb_selecting_dut_with_generics.behavioral_8.Test reset
+lib.tb_selecting_dut_with_generics.behavioral_16.Test reset
+lib.tb_selecting_dut_with_generics.rtl_8.Test state change
+lib.tb_selecting_dut_with_generics.rtl_16.Test state change
+lib.tb_selecting_dut_with_generics.behavioral_8.Test state change
+lib.tb_selecting_dut_with_generics.behavioral_16.Test state change
+Listed 8 tests
+
diff --git a/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration_stdout.html b/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration_stdout.html new file mode 100644 index 000000000..39f9132ed --- /dev/null +++ b/docs/blog/img/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration_stdout.html @@ -0,0 +1,12 @@ +
> python run.py --list
+lib.tb_selecting_dut_with_vhdl_configuration.rtl_8.Test reset
+lib.tb_selecting_dut_with_vhdl_configuration.rtl_16.Test reset
+lib.tb_selecting_dut_with_vhdl_configuration.behavioral_8.Test reset
+lib.tb_selecting_dut_with_vhdl_configuration.behavioral_16.Test reset
+lib.tb_selecting_dut_with_vhdl_configuration.rtl_32.Test reset
+lib.tb_selecting_dut_with_vhdl_configuration.rtl_8.Test state change
+lib.tb_selecting_dut_with_vhdl_configuration.rtl_16.Test state change
+lib.tb_selecting_dut_with_vhdl_configuration.behavioral_8.Test state change
+lib.tb_selecting_dut_with_vhdl_configuration.behavioral_16.Test state change
+Listed 9 tests
+
diff --git a/docs/blog/img/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration_stdout.html b/docs/blog/img/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration_stdout.html new file mode 100644 index 000000000..3d532dac0 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration_stdout.html @@ -0,0 +1,11 @@ +
> python run.py --list
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_rtl_8
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_rtl_8
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_rtl_16
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_rtl_16
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_behavioral_8
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_behavioral_8
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_behavioral_16
+lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_behavioral_16
+Listed 8 tests
+
diff --git a/docs/blog/img/vhdl_configuration/test_reset_architecture_of_test_runner.html b/docs/blog/img/vhdl_configuration/test_reset_architecture_of_test_runner.html new file mode 100644 index 000000000..a93b5917b --- /dev/null +++ b/docs/blog/img/vhdl_configuration/test_reset_architecture_of_test_runner.html @@ -0,0 +1,14 @@ +
architecture test_reset_architecture of test_runner is
+begin
+  main : process
+  begin
+    test_runner_setup(runner, nested_runner_cfg);
+
+    -- Test code here
+
+    test_runner_cleanup(runner);
+  end process;
+
+  test_runner_watchdog(runner, 10 * clk_period);
+end;
+
diff --git a/docs/blog/img/vhdl_configuration/test_reset_configurations.html b/docs/blog/img/vhdl_configuration/test_reset_configurations.html new file mode 100644 index 000000000..0e09a35f5 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/test_reset_configurations.html @@ -0,0 +1,28 @@ +
configuration test_reset_behavioral of tb_selecting_test_runner_with_vhdl_configuration is
+  for tb
+    for test_runner_inst : test_runner
+      use entity work.test_runner(test_reset_architecture);
+    end for;
+
+    for test_fixture
+      for dut : dff
+        use entity work.dff(behavioral);
+      end for;
+    end for;
+  end for;
+end;
+
+configuration test_reset_rtl of tb_selecting_test_runner_with_vhdl_configuration is
+  for tb
+    for test_runner_inst : test_runner
+      use entity work.test_runner(test_reset_architecture);
+    end for;
+
+    for test_fixture
+      for dut : dff
+        use entity work.dff(rtl);
+      end for;
+    end for;
+  end for;
+end;
+
diff --git a/docs/blog/img/vhdl_configuration/test_runner_component_instantiation.html b/docs/blog/img/vhdl_configuration/test_runner_component_instantiation.html new file mode 100644 index 000000000..39719d567 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/test_runner_component_instantiation.html @@ -0,0 +1,13 @@ +
test_runner_inst : component 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
+  );
+
diff --git a/docs/blog/img/vhdl_configuration/test_runner_entity.html b/docs/blog/img/vhdl_configuration/test_runner_entity.html new file mode 100644 index 000000000..9614bea32 --- /dev/null +++ b/docs/blog/img/vhdl_configuration/test_runner_entity.html @@ -0,0 +1,14 @@ +
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/docs/blog/img/vhdl_configuration/vhdl_configuration_on_a_test_case.html b/docs/blog/img/vhdl_configuration/vhdl_configuration_on_a_test_case.html new file mode 100644 index 000000000..1649eacdb --- /dev/null +++ b/docs/blog/img/vhdl_configuration/vhdl_configuration_on_a_test_case.html @@ -0,0 +1,2 @@ +
tb.test("Test reset").add_config(name="rtl_32", generics=dict(width=32), vhdl_configuration_name="rtl")
+
diff --git a/docs/blog/src/vhdl_configuration/dff.vhd b/docs/blog/src/vhdl_configuration/dff.vhd new file mode 100644 index 000000000..acac60107 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/dff.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 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; + +architecture behavioral of dff is +begin + process + begin + wait until rising_edge(clk); + q <= (others => '0') when reset else d; + end process; +end; + diff --git a/docs/blog/src/vhdl_configuration/run.py b/docs/blog/src/vhdl_configuration/run.py new file mode 100644 index 000000000..d2a4fc9f9 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/run.py @@ -0,0 +1,170 @@ +#!/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 + +import sys +import itertools +from os import walk +from pathlib import Path +from io import StringIO +from vunit import VUnit, VUnitCLI +from tools.doc_support import highlight_code, highlight_log, LogRegistry + +cli = VUnitCLI() +args = cli.parse_args() +root = Path(__file__).parent + + +def extract_snippets(): + for snippet in [ + "selecting_dut_with_generics", + ]: + highlight_code( + root / "tb_selecting_dut_with_generics.vhd", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + ) + + for snippet in [ + "create_vunit_configuration_for_selecting_dut", + "create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration", + "vhdl_configuration_on_a_test_case", + "create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration", + ]: + highlight_code( + root / "run.py", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + language="python", + ) + + for snippet in [ + "selecting_dut_with_vhdl_configuration", + ]: + highlight_code( + root / "tb_selecting_dut_with_vhdl_configuration.vhd", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + ) + + for snippet in [ + "test_runner_component_instantiation", + ]: + highlight_code( + root / "tb_selecting_test_runner_with_vhdl_configuration.vhd", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + ) + + for snippet in ["test_reset_architecture_of_test_runner", "test_reset_configurations"]: + highlight_code( + root / "test_reset.vhd", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + ) + + for snippet in [ + "test_runner_entity", + ]: + highlight_code( + root / "test_runner.vhd", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{snippet}.html", + snippet, + ) + + +extract_snippets() + +testbenches = [] +for _root, _dirs, files in walk(root): + for f in files: + if f.startswith("tb_") and f.endswith(".vhd"): + testbenches.append(Path(f).stem) + +test_patterns = [f"lib.{testbench}.*" for testbench in testbenches] +for testbench, test_pattern in zip(testbenches, test_patterns): + args.test_patterns = [test_pattern] + + vu = VUnit.from_args(args=args) + vu.add_vhdl_builtins() + lib = vu.add_library("lib") + lib.add_source_files(root / "*.vhd") + + # start_snippet create_vunit_configuration_for_selecting_dut + tb = lib.test_bench("tb_selecting_dut_with_generics") + + for dut_arch, width in itertools.product(["rtl", "behavioral"], [8, 16]): + tb.add_config( + name=f"{dut_arch}_{width}", + generics=dict(width=width, dff_arch=dut_arch), + ) + # end_snippet create_vunit_configuration_for_selecting_dut + + # 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. + + # start_snippet create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration + tb = lib.test_bench("tb_selecting_dut_with_vhdl_configuration") + + for dut_arch, width in itertools.product(["rtl", "behavioral"], [8, 16]): + tb.add_config( + name=f"{dut_arch}_{width}", + generics=dict(width=width), + vhdl_configuration_name=dut_arch, + ) + # end_snippet create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration + + # 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 + + # start_snippet vhdl_configuration_on_a_test_case + tb.test("Test reset").add_config(name="rtl_32", generics=dict(width=32), vhdl_configuration_name="rtl") + # end_snippet vhdl_configuration_on_a_test_case + + # 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. + + # start_snippet create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration + tb = lib.test_bench("tb_selecting_test_runner_with_vhdl_configuration") + + for dut_arch, width, test_case_name in itertools.product( + ["rtl", "behavioral"], [8, 16], ["test_reset", "test_state_change"] + ): + vhdl_configuration_name = f"{test_case_name}_{dut_arch}" + tb.add_config( + name=f"{vhdl_configuration_name}_{width}", + generics=dict(width=width), + vhdl_configuration_name=vhdl_configuration_name, + ) + # end_snippet create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration + + stringio = StringIO() + _stdout = sys.stdout + sys.stdout = stringio + + try: + vu.main() + except SystemExit: + std_out = stringio.getvalue() + sys.stdout = _stdout + print(std_out) + + if args.list: + (root / f"{testbench}_stdout.txt").write_text(f"> python run.py --list\n" + std_out) + highlight_log( + root / f"{testbench}_stdout.txt", + root / ".." / ".." / "img" / "vhdl_configuration" / f"{testbench}_stdout.html", + ) diff --git a/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_generics.vhd b/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_generics.vhd new file mode 100644 index 000000000..bf968ce0d --- /dev/null +++ b/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_generics.vhd @@ -0,0 +1,104 @@ +-- 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 a generic instead +-- of VHDL configurations to select the DUT to run. Without VHDL configurations +-- the width generic to the dff entity can be exposed and modified at the top-level + +library vunit_lib; +context vunit_lib.vunit_context; + +library ieee; +use ieee.std_logic_1164.all; + +-- start_snippet selecting_dut_with_generics +entity tb_selecting_dut_with_generics is + generic( + runner_cfg : string; + width : positive; + dff_arch : string + ); +end entity; + +architecture tb of tb_selecting_dut_with_generics is + -- start_folding ... + 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); + -- end_folding ... +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test reset") then + -- start_folding ... + d <= (others => '1'); + reset <= '1'; + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + -- end_folding ... + elsif run("Test state change") then + -- start_folding ... + 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_folding ... + end if; + end loop; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 * clk_period); + + test_fixture : block is + begin + clk <= not clk after clk_period / 2; + + dut_selection : if dff_arch = "rtl" generate + dut : entity work.dff(rtl) + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + + elsif dff_arch = "behavioral" generate + dut : entity work.dff(behavioral) + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + + else generate + error("Unknown DFF architecture"); + end generate; + end block; +end architecture; +-- end_snippet selecting_dut_with_generics diff --git a/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd b/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd new file mode 100644 index 000000000..4351dc2af --- /dev/null +++ b/docs/blog/src/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd @@ -0,0 +1,121 @@ +-- 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; + +-- start_snippet selecting_dut_with_vhdl_configuration +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 + -- start_folding ... + 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); + -- end_folding ... + + -- Component declaration + 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 + -- start_folding ... + d <= (others => '1'); + reset <= '1'; + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + -- end_folding ... + elsif run("Test state change") then + -- start_folding ... + 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_folding ... + end if; + end loop; + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 * clk_period); + + test_fixture : block is + begin + clk <= not clk after clk_period / 2; + + -- Component instantiation + dut : component dff + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + end block; +end architecture; + +-- Configuration declarations +configuration 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; + +configuration 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; +-- end_snippet selecting_dut_with_vhdl_configuration + diff --git a/docs/blog/src/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd b/docs/blog/src/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd new file mode 100644 index 000000000..a85bdfcc3 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/tb_selecting_test_runner_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 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; + + 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 + -- start_snippet test_runner_component_instantiation + test_runner_inst : component 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 + ); + -- end_snippet test_runner_component_instantiation + + test_fixture : block is + begin + clk <= not clk after clk_period / 2; + + dut : component dff + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + end block; +end architecture; diff --git a/docs/blog/src/vhdl_configuration/test_reset.vhd b/docs/blog/src/vhdl_configuration/test_reset.vhd new file mode 100644 index 000000000..bc9023571 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/test_reset.vhd @@ -0,0 +1,63 @@ +-- 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; + +-- start_snippet test_reset_architecture_of_test_runner +architecture test_reset_architecture of test_runner is +begin + main : process + begin + test_runner_setup(runner, nested_runner_cfg); + + -- start_folding -- Test code here + d <= (others => '1'); + reset <= '1'; + wait until rising_edge(clk); + wait for 0 ns; + check_equal(q, 0); + -- end_folding -- Test code here + + test_runner_cleanup(runner); + end process; + + test_runner_watchdog(runner, 10 * clk_period); +end; +-- end_snippet test_reset_architecture_of_test_runner + +-- start_snippet test_reset_configurations +configuration test_reset_behavioral of tb_selecting_test_runner_with_vhdl_configuration is + for tb + for test_runner_inst : test_runner + use entity work.test_runner(test_reset_architecture); + end for; + + for test_fixture + for dut : dff + use entity work.dff(behavioral); + end for; + end for; + end for; +end; + +configuration test_reset_rtl of tb_selecting_test_runner_with_vhdl_configuration is + for tb + for test_runner_inst : test_runner + use entity work.test_runner(test_reset_architecture); + end for; + + for test_fixture + for dut : dff + use entity work.dff(rtl); + end for; + end for; + end for; +end; +-- end_snippet test_reset_configurations diff --git a/docs/blog/src/vhdl_configuration/test_runner.vhd b/docs/blog/src/vhdl_configuration/test_runner.vhd new file mode 100644 index 000000000..32366bfc8 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/test_runner.vhd @@ -0,0 +1,24 @@ +-- 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; + +-- start_snippet test_runner_entity +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; +-- end_snippet test_runner_entity diff --git a/docs/blog/src/vhdl_configuration/test_state_change.vhd b/docs/blog/src/vhdl_configuration/test_state_change.vhd new file mode 100644 index 000000000..0aba91c84 --- /dev/null +++ b/docs/blog/src/vhdl_configuration/test_state_change.vhd @@ -0,0 +1,63 @@ +-- 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_architecture 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_behavioral 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_architecture); + end for; + + for test_fixture + for dut : dff + use entity work.dff(behavioral); + end for; + end for; + end for; +end; + +configuration test_state_change_rtl 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_architecture); + end for; + + for test_fixture + for dut : dff + use entity work.dff(rtl); + end for; + end for; + end for; +end; diff --git a/docs/news.d/179.feature.rst b/docs/news.d/179.feature.rst new file mode 100644 index 000000000..2199b0fae --- /dev/null +++ b/docs/news.d/179.feature.rst @@ -0,0 +1 @@ +Added support for simulating top-level VHDL configurations. diff --git a/docs/news.d/951.feature.rst b/docs/news.d/951.feature.rst new file mode 100644 index 000000000..2199b0fae --- /dev/null +++ b/docs/news.d/951.feature.rst @@ -0,0 +1 @@ +Added support for simulating top-level VHDL configurations. diff --git a/examples/vhdl/vhdl_configuration/dff.vhd b/examples/vhdl/vhdl_configuration/dff.vhd new file mode 100644 index 000000000..acac60107 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/dff.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 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; + +architecture behavioral of dff is +begin + process + begin + wait until rising_edge(clk); + q <= (others => '0') when reset else d; + end process; +end; + diff --git a/examples/vhdl/vhdl_configuration/run.py b/examples/vhdl/vhdl_configuration/run.py new file mode 100644 index 000000000..30895ed49 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/run.py @@ -0,0 +1,57 @@ +#!/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 dut_architecture in ["rtl", "behavioral"]: + for width in [8, 16]: + tb.add_config( + name=f"{dut_architecture}_{width}", + generics=dict(width=width), + vhdl_configuration_name=dut_architecture, + ) + +# 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="rtl_32", generics=dict(width=32), vhdl_configuration_name="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 test_case_name in ["test_reset", "test_state_change"]: + for dut_architecture in ["rtl", "behavioral"]: + vhdl_configuration_name = f"{test_case_name}_{dut_architecture}" + 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..2d45baf68 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd @@ -0,0 +1,110 @@ +-- 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 : component dff + generic map( + width => width + ) + port map( + clk => clk, + reset => reset, + d => d, + q => q + ); + end block; +end architecture; + +configuration 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; + +configuration 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/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..4ff6c4302 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd @@ -0,0 +1,87 @@ +-- 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; + + 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_inst : component 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 : component 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/test_reset.vhd b/examples/vhdl/vhdl_configuration/test_reset.vhd new file mode 100644 index 000000000..e1c48fb37 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/test_reset.vhd @@ -0,0 +1,57 @@ +-- 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_behavioral 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; + + for test_fixture + for dut : dff + use entity work.dff(behavioral); + end for; + end for; + end for; +end; + +configuration test_reset_rtl 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; + + for test_fixture + for dut : dff + use entity work.dff(rtl); + end for; + 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..5037ca080 --- /dev/null +++ b/examples/vhdl/vhdl_configuration/test_state_change.vhd @@ -0,0 +1,63 @@ +-- 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_behavioral 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; + + for test_fixture + for dut : dff + use entity work.dff(behavioral); + end for; + end for; + end for; +end; + +configuration test_state_change_rtl 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; + + for test_fixture + for dut : dff + use entity work.dff(rtl); + end for; + end for; + end for; +end; 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/cfg3.vhd b/tests/acceptance/artificial/vhdl/cfg3.vhd new file mode 100644 index 000000000..01537edf2 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/cfg3.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 arch3 of ent is +begin + arch <= "arch3"; +end; + +configuration cfg3 of tb_with_vhdl_configuration is + for tb + for ent_inst : ent + use entity work.ent(arch3); + 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..438639859 100644 --- a/tests/acceptance/artificial/vhdl/run.py +++ b/tests/acceptance/artificial/vhdl/run.py @@ -94,10 +94,33 @@ 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").test_bench("tb_with_vhdl_configuration") + test_2 = tb.test("test 2") + test_3 = tb.test("test 3") + + tb.set_vhdl_configuration_name("cfg1") + tb.set_post_check(make_post_check("arch1")) + test_2.set_vhdl_configuration_name("cfg2") + test_2.add_config(name="cfg2", post_check=make_post_check("arch2")) + test_3.add_config(name="cfg3", post_check=make_post_check("arch3"), vhdl_configuration_name="cfg3") + + 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..72d57005c --- /dev/null +++ b/tests/acceptance/artificial/vhdl/tb_with_vhdl_configuration.vhd @@ -0,0 +1,53 @@ +-- 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); + + while test_suite loop + 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); + + if run("test 1") then + elsif run("test 2") then + elsif run("test 3") then + end if; + end loop; + + 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..e53e60818 100644 --- a/tests/acceptance/test_artificial.py +++ b/tests/acceptance/test_artificial.py @@ -245,4 +245,16 @@ 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.test 1", + ), + ( + "passed", + "lib.tb_with_vhdl_configuration.cfg2.test 2", + ), + ( + "passed", + "lib.tb_with_vhdl_configuration.cfg3.test 3", + ), ) diff --git a/tests/acceptance/test_external_run_scripts.py b/tests/acceptance/test_external_run_scripts.py index 3773120b6..e7eab8c17 100644 --- a/tests/acceptance/test_external_run_scripts.py +++ b/tests/acceptance/test_external_run_scripts.py @@ -176,6 +176,31 @@ 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_dut_with_vhdl_configuration.behavioral_16.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.rtl_16.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.rtl_32.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.rtl_8.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.behavioral_8.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.rtl_8.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.behavioral_16.Test state change"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.behavioral_8.Test reset"), + ("passed", "lib.tb_selecting_dut_with_vhdl_configuration.rtl_16.Test reset"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_rtl_8"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_rtl_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_rtl_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_behavioral_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_rtl_8"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_behavioral_8"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_state_change_behavioral_16"), + ("passed", "lib.tb_selecting_test_runner_with_vhdl_configuration.test_reset_behavioral_8"), + ], + ) + @mark.xfail( not (simulator_is("ghdl") or simulator_is("nvc")), reason="Support complex JSON strings as generic", 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/tests/unit/test_test_bench.py b/tests/unit/test_test_bench.py index 915fb617b..009054c20 100644 --- a/tests/unit/test_test_bench.py +++ b/tests/unit/test_test_bench.py @@ -282,7 +282,9 @@ def test_add_config(self, tempdir): test_bench.set_generic("global_value", "global value") test_bench.add_config(name="value=1", generics=dict(value=1, global_value="local value")) - test_bench.add_config(name="value=2", generics=dict(value=2), attributes={".foo": "bar"}) + test_bench.add_config( + name="value=2", generics=dict(value=2), attributes={".foo": "bar"}, vhdl_configuration_name="cfg" + ) self.assertRaises( AttributeException, @@ -299,11 +301,13 @@ def test_add_config(self, tempdir): {"value": 1, "global_value": "local value"}, ) self.assertEqual(get_config_of(tests, "lib.tb_entity.value=1").attributes, {}) + self.assertIsNone(get_config_of(tests, "lib.tb_entity.value=1").vhdl_configuration_name) self.assertEqual( get_config_of(tests, "lib.tb_entity.value=2").generics, {"value": 2, "global_value": "global value"}, ) self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").attributes, {".foo": "bar"}) + self.assertEqual(get_config_of(tests, "lib.tb_entity.value=2").vhdl_configuration_name, "cfg") @with_tempdir def test_test_case_add_config(self, tempdir): diff --git a/vunit/configuration.py b/vunit/configuration.py index bfccbfe2d..b0bc87052 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 @@ -208,6 +217,15 @@ def set_generic(self, name, value): for config in configs.values(): config.set_generic(name, value) + def set_vhdl_configuration_name(self, value: str): + """ + Set VHDL configuration name + """ + self._check_enabled() + for configs in self.get_configuration_dicts(): + for config in configs.values(): + config.set_vhdl_configuration_name(value) + def set_sim_option(self, name, value, overwrite=True): """ Set sim option @@ -248,6 +266,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 +302,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/test.py b/vunit/ui/test.py index 16294a99c..161ed9992 100644 --- a/vunit/ui/test.py +++ b/vunit/ui/test.py @@ -36,6 +36,7 @@ def add_config( # pylint: disable=too-many-arguments post_check=None, sim_options=None, attributes=None, + vhdl_configuration_name=None, ): """ Add a configuration to this test copying the default configuration. @@ -50,6 +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, if any. :example: @@ -86,6 +88,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 set_attribute(self, name, value): @@ -136,6 +139,15 @@ def set_parameter(self, name, value): """ self._test_case.set_generic(name, value) + def set_vhdl_configuration_name(self, value: str): + """ + Set VHDL configuration name of all + |configurations| of this test + + :param value: The VHDL configuration name + """ + self._test_case.set_vhdl_configuration_name(value) + def set_sim_option(self, name, value, overwrite=True): """ Set simulation option within all |configurations| of this test diff --git a/vunit/ui/testbench.py b/vunit/ui/testbench.py index 3ca643063..b744c9f8b 100644 --- a/vunit/ui/testbench.py +++ b/vunit/ui/testbench.py @@ -86,6 +86,15 @@ def set_parameter(self, name, value): """ self._test_bench.set_generic(name, value) + def set_vhdl_configuration_name(self, value: str): + """ + Set VHDL configuration name of all + |configurations| of this test bench or test cases within it + + :param value: The VHDL configuration name + """ + self._test_bench.set_vhdl_configuration_name(value) + def set_sim_option(self, name, value, overwrite=True): """ Set simulation option within all |configurations| of this test bench or test cases within it @@ -130,6 +139,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. @@ -144,6 +154,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, if any. :example: @@ -181,6 +192,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):