Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configuration support #785

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
429 changes: 429 additions & 0 deletions docs/blog/vhdl_configurations.rst

Large diffs are not rendered by default.

29 changes: 20 additions & 9 deletions docs/py/ui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,31 @@ pre_config and post_check :ref:`callback functions <pre_and_post_hooks>`.
User :ref:`attributes <attributes>` can also be added as a part of a
configuration.

Configurations can either be unique for each test case or must be
common for the entire test bench depending on the situation. For test
benches without test such as `tb_example` in the User Guide the
configuration is common for the entire test bench. For test benches
containing tests such as `tb_example_many` the configuration is done
for each test case. If the ``run_all_in_same_sim`` attribute has been used,
VHDL configurations for test bench entities are automatically discovered
and added to the set of configurations created in Python.

Python-defined configurations can either be unique for each test case or
must be common for the entire test bench depending on the situation.
For test benches without tests such as `tb_example` in the User Guide
the configuration is common for the entire test bench. For test benches
containing tests such as `tb_example_many` the configuration is done for
each test case. If the ``run_all_in_same_sim`` attribute has been used,
configuration is performed at the test bench level even if there are
individual tests within since they must run in the same simulation.

In a VUnit all test benches and test cases are created with an unnamed default
In VUnit all test benches and test cases are created with an unnamed default
configuration which is modified by different methods such as ``set_generic`` etc.
In addition to the unnamed default configuration multiple named configurations
can be derived from it by using the ``add_config`` method. The default
configuration is only run if there are no named configurations.
can be derived from it by using the ``add_config`` method or by defining a
VHDL configuration. The default configuration is only run if there are no
named configurations.

Named configurations can be retrieved from test benches and tests using the
``get_configs`` methods and then modified using methods such as ``set_pre_config``.
This is especially useful for VHDL configurations which are automatically
detected and added without any of the configuration capabilities provided
by the Python API. Note that generics can't be added to a VHDL-defined
configuration as that is not allowed in VHDL.

.. _attributes:

Expand Down
14 changes: 14 additions & 0 deletions docs/py/vunit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ Test

.. autoclass:: vunit.ui.test.Test()

ConfigurationList
-----------------

.. autoclass:: vunit.ui.configuration.ConfigurationList()
:special-members: __iter__
:inherited-members:


Configuration
-------------

.. autoclass:: vunit.ui.configuration.Configuration()
:inherited-members:

Results
-------

Expand Down
40 changes: 38 additions & 2 deletions docs/run/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ The code has the following important properties:

* The VUnit functionality is located in the ``vunit_lib`` library and is included with the library and context
statements in the first two lines.
* The ``runner_cfg`` generic is used to control the testbench from PR. If the VR is used standalone, you will
need a default value, ``runner_cfg_default``, for the generic. Note that the generic **must** be named
* The ``runner_cfg`` generic is used to control the testbench from PR. Note that the generic **must** be named
``runner_cfg`` for the testbench to be recognized by PR (there is an exception which we'll get to later).
* Every VUnit testbench has a main controlling process. It's labelled ``test_runner`` in this example but the name is
not important. The process starts by setting up VUnit using the ``test_runner_setup`` procedure with ``runner_cfg`` as
Expand Down Expand Up @@ -262,6 +261,43 @@ it is possible to read out assert error counters and use ``check_equal`` to veri
``test_runner_cleanup``. Failures like division by zero or out of range operations are other examples that won't be
handle gracefully in this mode, and not something that VHDL provides any solutions for.

Testbenches with VHDL Configurations
------------------------------------

If there are VHDL configurations defined for the testbench entity, each configuration, and not the testbench
entity, defines a top-level for the simulation. VUnit automatically detects these VHDL configurations and treat
them as a special case of the larger testbench configuration concept provided by VUnit, see
:ref:`configurations <configurations>`.

VHDL doesn't allow setting generics when the top-level is a VHDL configuration which prevents the `runner_cfg`
generic to be set by PR. To work around this limitation PR provides a `runner.cfg` file containing the same
information. This file is read by `test_runner_setup` whenever its `runner_cfg` parameter is set to
`null_runner_cfg`. An example is shown below.

.. code-block:: vhdl

library vunit_lib;
context vunit_lib.vunit_context;

entity tb_minimal is
generic (runner_cfg : string := null_runner_cfg);
end entity;

architecture tb of tb_minimal is
begin
test_runner : process
begin
test_runner_setup(runner, runner_cfg);

...

test_runner_cleanup(runner);
end process;
end architecture;

Note that the `runner_cfg` generic must remain present since this is the token used by VUnit test scanning
to distinguish testbench entities from other entities.

Special Paths
-------------

Expand Down
42 changes: 42 additions & 0 deletions examples/vhdl/vhdl_configuration/dff.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-- 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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-- 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: Instead of having a testbench containing a shared test fixture
-- and then use VHDL configurations to select different test runners implementing
-- different tests one can flip things upside down. Each test become a separate
-- top-level testbench and the shared test fixture is placed in a separate entity
-- imported by each tetbench.

library vunit_lib;
context vunit_lib.vunit_context;

library ieee;
use ieee.std_logic_1164.all;

entity tb_reset is
generic(
runner_cfg : string;
width : positive
);
end entity;

architecture tb of tb_reset is
constant clk_period : time := 10 ns;

signal reset : std_logic;
signal clk : std_logic;
signal d : std_logic_vector(width - 1 downto 0);
signal q : std_logic_vector(width - 1 downto 0);
begin
text_fixture_inst : entity work.test_fixture
generic map(
width => width,
clk_period => clk_period
)
port map(
clk => clk,
reset => reset,
d => d,
q => q
);

test_runner : process
begin
test_runner_setup(runner, 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;

end architecture;
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
-- 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;

entity tb_selecting_dut_with_generate_statement is
generic(
runner_cfg : string;
width : positive;
dut_arch : string
);
end entity;

architecture tb of tb_selecting_dut_with_generate_statement 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);
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_selection : if dut_arch = "rtl" generate
dut : entity work.dff(rtl)
generic map(
width => width
)
port map(
clk => clk,
reset => reset,
d => d,
q => q
);

elsif dut_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 DUT architecture");
end generate;
end block;
end architecture;
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
-- 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: Instead of having a testbench containing a shared test fixture
-- and then use VHDL configurations to select different test runners implementing
-- different tests one can flip things upside down. Each test become a separate
-- top-level testbench and the shared test fixture is placed in a separate entity
-- imported by each tetbench.

library vunit_lib;
context vunit_lib.vunit_context;

library ieee;
use ieee.std_logic_1164.all;

entity tb_state_change is
generic(
runner_cfg : string;
width : positive
);
end entity;

architecture tb of tb_state_change is
constant clk_period : time := 10 ns;

signal reset : std_logic;
signal clk : std_logic;
signal d : std_logic_vector(width - 1 downto 0);
signal q : std_logic_vector(width - 1 downto 0);
begin
text_fixture_inst : entity work.test_fixture
generic map(
width => width,
clk_period => clk_period
)
port map(
clk => clk,
reset => reset,
d => d,
q => q
);

test_runner : process
begin
test_runner_setup(runner, 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;

end architecture;
Loading