Skip to content

Commit 54198df

Browse files
committed
Added blog on VHDL configurations.
1 parent 11f8f24 commit 54198df

9 files changed

Lines changed: 252 additions & 35 deletions
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
:tags: VUnit, OSVVM, configurations
2+
:author: lasplund
3+
:excerpt: 1
4+
5+
Improved Support for VHDL Configurations and OSVVM
6+
==================================================
7+
8+
For quite some time, several initiatives have been underway to improve the integration between VUnit and OSVVM. Examples
9+
of these efforts are the `external logging framework integration feature
10+
<https://vunit.github.io/logging/user_guide.html#external-logging-framework-integration>`__ and the OSVVM pull request
11+
`#81 <https://github.com/OSVVM/OSVVM/pull/81>`__.
12+
13+
Another example is the introduction of support for top-level VHDL configurations which serves several purposes, for
14+
example:
15+
16+
1. Enabling the selection of the Device Under Test (DUT) to be used in a VUnit testbench.
17+
2. Direct support for running conventional OSVVM testbenches within the VUnit framework.
18+
19+
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.
20+
21+
Selecting DUT Using Generics
22+
----------------------------
23+
24+
Sometimes the VHDL DUT comes in different variants (architectures) and there is a need to verify all of these with the
25+
same testbench. It could be an FPGA and an ASIC implementation or an RTL and a behavioral architecture. Before
26+
supporting VHDL configurations, VUnit addressed this issue with a combination of generics and an if-generate statement
27+
as showed in the example below. For the purpose of this blog, we have removed the complexities typically found in
28+
real-world designs and chosen to focus on the fundamental principles. Thus, we will use a simple variable-width
29+
flip-flop as the DUT for our demonstrations.
30+
31+
.. raw:: html
32+
:file: img/vhdl_configuration/selecting_dut_with_generics.html
33+
34+
This approach is straightforward: simply copy and paste the flip-flop instantiation, but modify the architecture to use
35+
based on the ``dut_arch`` generic. While the approach is simple it also introduces code duplication which can be a bit
36+
dangerous. In this case, since the copies are placed adjacent to each other, the risk of inadvertently changing one
37+
without updating the other is somewhat mitigated.
38+
39+
If your DUT has numerous ports, you can consider leveraging the VHDL-2019 interface construct as a means to raise the
40+
level of abstraction and reduce code duplication. This approach allows for a more concise representation of the design,
41+
provided your simulator supports the latest VHDL standard.
42+
43+
.. NOTE::
44+
There is a proposed update to the VHDL standard related to this topic as it would fully remove the code duplication.
45+
`Issue #235 <https://gitlab.com/IEEE-P1076/VHDL-Issues/-/issues/235>`__ proposes that a string should be possible
46+
to use when specifying the architecture in an entity instantiation, i.e. ``"rtl"`` or ``"behavioral"`` rather than
47+
``rtl`` or ``behavioral``. In our example we would simply have a single entity instantiation which architecture is
48+
specified with the ``dut_arch`` generic.
49+
50+
The various settings of the ``dut_arch`` generic are handled with a VUnit configuration in the Python run script.
51+
Initially, the use of both VUnit and VHDL configuration concepts may appear confusing, but we will soon see that a VHDL
52+
configuration is a special case of the broader VUnit configuration concept. In this example, we are also testing the DUT
53+
with multiple ``width`` settings. Note how we can use the ``product`` function from ``itertools`` to iterate over all
54+
combinations of ``dut_arch`` and ``width``. This is equivalent to two nested loops over these generics but scales better
55+
as the number of generics to combine increases.
56+
57+
.. raw:: html
58+
:file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut.html
59+
60+
If we list all the tests, we will see that there are four for each test
61+
case in the testbench, one for each combination of ``dut_arch`` and ``width``:
62+
63+
.. raw:: html
64+
:file: img/vhdl_configuration/tb_selecting_dut_with_generics_stdout.html
65+
66+
Selecting DUT Using VHDL Configurations
67+
---------------------------------------
68+
69+
When using VHDL configurations we need three ingredients in our testbench
70+
71+
1. A component declaration for the DUT. In the example below it has been
72+
placed in the declarative part of the testbench architecture but it
73+
can also be placed in a separate package.
74+
2. A component instantiation of the declared component. Note that the
75+
``component`` keyword is optional and can be excluded.
76+
3. A configuration declaration for each DUT architecture
77+
78+
.. raw:: html
79+
:file: img/vhdl_configuration/selecting_dut_with_vhdl_configuration.html
80+
81+
Instead of assigning a generic to select our architecture, we now specify which VHDL configuration our VUnit configuration should use:
82+
83+
.. raw:: html
84+
:file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_with_a_vhdl_configuration.html
85+
86+
Incorporating VHDL configurations within VUnit configurations brings forth another advantage. From a VHDL point of view,
87+
VHDL configurations are linked to entities, such as the testbench entity in our scenario. However, a VUnit configuration
88+
can also be applied to specific test cases, opening up the possibility of using VHDL configurations at that finer level
89+
of granularity. For instance, consider a situation where we have an FPGA and an ASIC implementation/architecure that
90+
differ only in the memory IPs they use. In such a case, it might be sufficient to simulate only one of the architectures
91+
for the test cases not involving memory operations.
92+
93+
To illustrate this using the flip-flop example, let's create a test where we set ``width`` to 32 and exclusively
94+
simulate it using the RTL architecture:
95+
96+
.. raw:: html
97+
:file: img/vhdl_configuration/vhdl_configuration_on_a_test_case.html
98+
99+
Now, we have an additional entry in our list of tests:
100+
101+
.. raw:: html
102+
:file: img/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration_stdout.html
103+
104+
Choosing between VHDL configurations and generics is primarily a matter of personal preference. The generic approach led
105+
us to multiple direct entity instantiations and code duplication. However, the configuration approach demands a
106+
component declaration, which essentially duplicates the DUT entity declaration. Additionally, VHDL configuration
107+
declarations are also necessary.
108+
109+
Selecting Test Runner Using VHDL Configurations
110+
-----------------------------------------------
111+
112+
In the previous examples, the VUnit test cases were located in a process called ``test_runner`` residing alongside the
113+
DUT. This is the most straightforward arrangement, as it provides the test cases with direct access to the DUT's
114+
interface. An alternative approach involves encapsulating ``test_runner`` within an entity, which is subsequently
115+
instantiated within the testbench. Such a ``test_runner`` entity needs access to the ``runner_cfg`` and ``width``
116+
generics, in addition to the ``clk_period`` constant and the interface ports of the DUT.
117+
118+
.. raw:: html
119+
:file: img/vhdl_configuration/test_runner_entity.html
120+
121+
Note that the runner configuration generic is called ``nested_runner_cfg`` and not ``runner_cfg``. The reason is that
122+
``runner_cfg`` is the signature used to identify a testbench, the top-level of a simulation. The ``test_runner`` entity
123+
is not a simulation top-level and must not be mistaken as such.
124+
125+
We can now replace the testbench ``test_runner`` process and watchdog with an instantiation of this component:
126+
127+
.. raw:: html
128+
:file: img/vhdl_configuration/test_runner_component_instantiation.html
129+
130+
Having relocated ``test_runner`` into an entity, we can have VHDL configurations selecting which test runner to use, and
131+
let each such test runner represent a single test. This setup is the conventional methodology seen in OSVVM
132+
testbenches. With VUnit's extended support for VHDL configurations, it becomes possible to keep that structure when
133+
adding VUnit capabilities. For example, this is the architecture for the reset test:
134+
135+
.. raw:: html
136+
:file: img/vhdl_configuration/test_reset_architecture_of_test_runner.html
137+
138+
.. NOTE::
139+
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.
140+
141+
Below are the two configurations that select this particular test along with one of the ``rtl`` and ``behavioral``
142+
architectures for the DUT:
143+
144+
.. raw:: html
145+
:file: img/vhdl_configuration/test_reset_configurations.html
146+
147+
This example highlights a drawback of VHDL configurations: every combination of architectures to use in a test has to be
148+
manually created. When we use generics and if generate statements to select architectures, we create all combinations
149+
**programatically** in the Python script using the ``itertools.product`` function. Despite this, Python can continue to
150+
play a role in alleviating certain aspects of the combinatorial workload:
151+
152+
.. raw:: html
153+
:file: img/vhdl_configuration/create_vunit_configuration_for_selecting_dut_and_runner_with_a_vhdl_configuration.html
154+
155+
.. raw:: html
156+
:file: img/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration_stdout.html
157+
158+
That concludes our discussion for now. As always, we highly value your feedback and appreciate any insights you might have to offer.
159+

docs/news.d/179.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for simulating top-level VHDL configurations.

docs/news.d/951.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for simulating top-level VHDL configurations.

examples/vhdl/vhdl_configuration/dff.vhd

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,6 @@ begin
3232
end process;
3333
end;
3434

35-
configuration dff_rtl of tb_selecting_dut_with_vhdl_configuration is
36-
for tb
37-
for test_fixture
38-
for dut : dff
39-
use entity work.dff(rtl);
40-
end for;
41-
end for;
42-
end for;
43-
end;
44-
4535
architecture behavioral of dff is
4636
begin
4737
process
@@ -51,12 +41,3 @@ begin
5141
end process;
5242
end;
5343

54-
configuration dff_behavioral of tb_selecting_dut_with_vhdl_configuration is
55-
for tb
56-
for test_fixture
57-
for dut : dff
58-
use entity work.dff(behavioral);
59-
end for;
60-
end for;
61-
end for;
62-
end;

examples/vhdl/vhdl_configuration/run.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@
2020
# pure VHDL configuration. For example, by running with different generic values.
2121
tb = lib.test_bench("tb_selecting_dut_with_vhdl_configuration")
2222

23-
for vhdl_configuration_name in ["dff_rtl", "dff_behavioral"]:
23+
for dut_architecture in ["rtl", "behavioral"]:
2424
for width in [8, 16]:
2525
tb.add_config(
26-
name=f"{vhdl_configuration_name}_{width}",
26+
name=f"{dut_architecture}_{width}",
2727
generics=dict(width=width),
28-
vhdl_configuration_name=vhdl_configuration_name,
28+
vhdl_configuration_name=dut_architecture,
2929
)
3030

3131
# A top-level VHDL configuration is bound to an entity, i.e. the testbench. However,
3232
# when handled as part of VUnit configurations it can also be applied to a
3333
# single test case
34-
tb.test("Test reset").add_config(name="dff_rtl_32", generics=dict(width=32), vhdl_configuration_name="dff_rtl")
34+
tb.test("Test reset").add_config(name="rtl_32", generics=dict(width=32), vhdl_configuration_name="rtl")
3535

3636

3737
# If the test runner is placed in a component instantiated into the testbench, different architectures of that
@@ -44,12 +44,14 @@
4444
# is to put each test suite in its own testbench and make the test fixture a component reused between the testbenches.
4545
# That approach do not require any VHDL configurations.
4646
tb = lib.test_bench("tb_selecting_test_runner_with_vhdl_configuration")
47-
for vhdl_configuration_name in ["test_reset", "test_state_change"]:
48-
for width in [8, 16]:
49-
tb.add_config(
50-
name=f"{vhdl_configuration_name}_{width}",
51-
generics=dict(width=width),
52-
vhdl_configuration_name=vhdl_configuration_name,
53-
)
47+
for test_case_name in ["test_reset", "test_state_change"]:
48+
for dut_architecture in ["rtl", "behavioral"]:
49+
vhdl_configuration_name = f"{test_case_name}_{dut_architecture}"
50+
for width in [8, 16]:
51+
tb.add_config(
52+
name=f"{vhdl_configuration_name}_{width}",
53+
generics=dict(width=width),
54+
vhdl_configuration_name=vhdl_configuration_name,
55+
)
5456

5557
vu.main()

examples/vhdl/vhdl_configuration/tb_selecting_dut_with_vhdl_configuration.vhd

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ begin
7575
begin
7676
clk <= not clk after clk_period / 2;
7777

78-
dut : dff
78+
dut : component dff
7979
generic map(
8080
width => width
8181
)
@@ -87,3 +87,24 @@ begin
8787
);
8888
end block;
8989
end architecture;
90+
91+
configuration rtl of tb_selecting_dut_with_vhdl_configuration is
92+
for tb
93+
for test_fixture
94+
for dut : dff
95+
use entity work.dff(rtl);
96+
end for;
97+
end for;
98+
end for;
99+
end;
100+
101+
configuration behavioral of tb_selecting_dut_with_vhdl_configuration is
102+
for tb
103+
for test_fixture
104+
for dut : dff
105+
use entity work.dff(behavioral);
106+
end for;
107+
end for;
108+
end for;
109+
end;
110+

examples/vhdl/vhdl_configuration/tb_selecting_test_runner_with_vhdl_configuration.vhd

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,20 @@ architecture tb of tb_selecting_test_runner_with_vhdl_configuration is
4343
);
4444
end component;
4545

46+
component dff is
47+
generic(
48+
width : positive := width
49+
);
50+
port(
51+
clk : in std_logic;
52+
reset : in std_logic;
53+
d : in std_logic_vector(width - 1 downto 0);
54+
q : out std_logic_vector(width - 1 downto 0)
55+
);
56+
end component;
57+
4658
begin
47-
test_runner_inst : test_runner
59+
test_runner_inst : component test_runner
4860
generic map(
4961
clk_period => clk_period,
5062
width => width,
@@ -61,7 +73,7 @@ begin
6173
begin
6274
clk <= not clk after clk_period / 2;
6375

64-
dut : entity work.dff(rtl)
76+
dut : component dff
6577
generic map(
6678
width => width
6779
)

examples/vhdl/vhdl_configuration/test_reset.vhd

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,30 @@ begin
2828
test_runner_watchdog(runner, 10 * clk_period);
2929
end;
3030

31-
configuration test_reset of tb_selecting_test_runner_with_vhdl_configuration is
31+
configuration test_reset_behavioral of tb_selecting_test_runner_with_vhdl_configuration is
3232
for tb
3333
for test_runner_inst : test_runner
3434
use entity work.test_runner(test_reset_a);
3535
end for;
36+
37+
for test_fixture
38+
for dut : dff
39+
use entity work.dff(behavioral);
40+
end for;
41+
end for;
42+
end for;
43+
end;
44+
45+
configuration test_reset_rtl of tb_selecting_test_runner_with_vhdl_configuration is
46+
for tb
47+
for test_runner_inst : test_runner
48+
use entity work.test_runner(test_reset_a);
49+
end for;
50+
51+
for test_fixture
52+
for dut : dff
53+
use entity work.dff(rtl);
54+
end for;
55+
end for;
3656
end for;
3757
end;

examples/vhdl/vhdl_configuration/test_state_change.vhd

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,30 @@ begin
3434
test_runner_watchdog(runner, 10 * clk_period);
3535
end;
3636

37-
configuration test_state_change of tb_selecting_test_runner_with_vhdl_configuration is
37+
configuration test_state_change_behavioral of tb_selecting_test_runner_with_vhdl_configuration is
3838
for tb
3939
for test_runner_inst : test_runner
4040
use entity work.test_runner(test_state_change_a);
4141
end for;
42+
43+
for test_fixture
44+
for dut : dff
45+
use entity work.dff(behavioral);
46+
end for;
47+
end for;
48+
end for;
49+
end;
50+
51+
configuration test_state_change_rtl of tb_selecting_test_runner_with_vhdl_configuration is
52+
for tb
53+
for test_runner_inst : test_runner
54+
use entity work.test_runner(test_state_change_a);
55+
end for;
56+
57+
for test_fixture
58+
for dut : dff
59+
use entity work.dff(rtl);
60+
end for;
61+
end for;
4262
end for;
4363
end;

0 commit comments

Comments
 (0)