diff --git a/docs/verification_components/user_guide.rst b/docs/verification_components/user_guide.rst index 705e47b53..56b6fc51c 100644 --- a/docs/verification_components/user_guide.rst +++ b/docs/verification_components/user_guide.rst @@ -1,7 +1,7 @@ .. _vc_library: Verification Component Library -=============================== +############################## .. note:: This library is released as a *BETA* version. This means non-backwards compatible changes are still likely based on @@ -13,7 +13,7 @@ as well as a set of utilities for writing your own verification component. Verification components allow a better overview in the test bench by raising the abstraction level of bus transactions. Even if you do not need the advanced features that VCs offer you may still -benefit from using per-verified models of an AXI-bus instead of +benefit from using pre-verified models of an AXI-bus instead of re-implementing it yourself. Included verification components (VCs): @@ -48,10 +48,10 @@ synchronization VCI while also supporting their own bus specific VCIs. interface on the top level. The same testbench code for talking to the submodule can be used in both the submodule test bench as well as the top level test bench regardless of the fact that two different VCs - have been used. Without generic VCIs copy pasting the code and + have been used. Without generic VCIs, copy pasting the code and changing the type of read/write procedure call would be required. -Neither a VC or a VCI there is the :ref:`memory model ` +Neither a VC or a VCI, there is the :ref:`memory model ` which is a model of a memory space such as the DRAM address space in a computer system. The memory mapped slave VCs such as AXI and Wishbone make transactions against the memory model which provides access @@ -64,10 +64,29 @@ reading and writing data. memory_model +.. _verification_components: + +Verification Components +======================= + +A verification component (VC) is an entity that is normally connected +to the DUT via a bus signal interface such as AXI-Lite. The main test +sequence in the test bench sends messages to the VCs that will then +perform the actual bus signal transactions. The benefit of this is +both to raise the abstraction level of the test bench as well as +making it easy to have parallel activity on several bus interfaces. + +A VC typically has an associated package defining procedures for +sending to and receiving messages from the VC. Each VC instance is +associated with a handle that is created by a *constructor* function +in the test bench and set as a generic on the VC instantiation. +The handle is given as an argument to the procedure calls for directing +messages to the specfic VC instance. + .. _verification_component_interfaces: Verification Component Interfaces ---------------------------------- +================================= A verification component interface (VCI) is a procedural interface to a VC. A VCI is defined as procedures in a package file. Several VCs can @@ -76,9 +95,8 @@ and the VC-developers. List of VCIs included in the main repository: -Included verification component interfaces (VCIs): - -* :ref:`Bus master `: generic read and write of bus with address and byte enable. +* :ref:`Bus master `: generic read and write of bus with + address and byte enable. * :ref:`Stream `: push and pop of data stream without address. * :ref:`Synchronization `: wait for time and events. @@ -88,20 +106,210 @@ Included verification component interfaces (VCIs): vci -.. _verification_components: +VC and VCI Compliance Testing +============================= -Verification Components ------------------------ +VUnit also provides a set of compliance tests to help VC and VCI developers +adhere to good design principles. These rules aim at creating flexible, +reusable, interoperable and future proof VCs and VCIs. -A verification component (VC) is an entity that is normally connected -to the DUT via a bus signal interface such as AXI-Lite. The main test -sequence in the test bench sends messages to the VCs that will then -perform the actual bus signal transactions. The benefit of this is -both to raise the abstraction level of the test bench as well as -making it easy to have parallel activity on several bus interfaces. +Compliance testing is done separately for the VC and the VCI. Each test +consists of two parts: one part tests the code by parsing it and the other part +tests the code by running a VHDL testbench. -A VC typically has an associated package defining procedures for -sending to and receiving messages from the VC. Each VC instance is -associated with a handle that is created in the test bench and set as -a generic on the VC instantiation. The handle is given as and argument -to the procedure calls to direct messages to the specfic VC instance. +The VHDL testbenches cannot be automatically created because of: + +* A VC constructor can have VC specific parameters without default values. +* A VC port list can constain unconstrained ports. + +These issues are solved by creating templates for the VHDL testbenches. The +template is created by calling the :vunit_file:`compliance_test.py ` +script (see `--help` for instructions). Some rules are checked in this process +and any violation is reported. When all violations have been fixed, the script +will generate a template, which needs to be modified manually according to the +instructions in the file. + +The ``run.py`` file verifying the VC and the VCI can then add compliance tests +by using the ``VerificationComponentInterface`` and ``VerificationComponent`` +classes. These classes provide methods for generating the full VHDL testbenches +from the templates and some extra parameters. For instance: + +.. code-block:: python + + # Find the VCI for the Avalon sink VC located in the avalon_stream_pkg package + # which has been added to library lib (lib.add_source_files). The return type + # for the VC constructor, avalon_sink_t, which allow the constructor to be + # found analyzed. + avalon_sink_vci = VerificationComponentInterface.find( + lib, "avalon_stream_pkg", "avalon_sink_t" + ) + + # Add a VCI compliance testbench to library test_lib. The generated testbench + # will be saved in the compliance_test directory and the final path identifies + # the previously generated testbench template + avalon_sink_vci.add_vhdl_testbench( + test_lib, + ROOT / "compliance_test", + ROOT / ".vc" / "avalon_stream_pkg" / "tb_avalon_sink_t_compliance_template.vhd" + ) + + # Find the Avalon_sink VC located in the lib library + avalon_sink_vc = VerificationComponent.find(lib, "avalon_sink", avalon_sink_vci) + + # Add a VC compliance testbench to library test_lib. The generated testbench + # will be saved in the compliance_test directory and the final path identifies + # the previously generated testbench template + avalon_sink_vc.add_vhdl_testbench( + test_lib, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_avalon_sink_compliance_template.vhd" + ) + +The Avalon sink test suite will now have a number of extra test cases verifying +that it is compliant: + +.. code-block:: console + + ==== Summary ============================================================================================================================ + pass test_lib.tb_avalon_sink_t_compliance.Test standard configuration (1.5 seconds) + pass test_lib.tb_avalon_sink_t_compliance.Test handle independence (0.7 seconds) + pass test_lib.tb_avalon_sink_t_compliance.Test default logger (0.8 seconds) + pass test_lib.tb_avalon_sink_t_compliance.Test default checker (0.7 seconds) + pass test_lib.tb_avalon_sink_compliance.Test that sync interface is supported (0.8 seconds) + pass test_lib.tb_avalon_sink_compliance.Test that the actor can be customised (0.8 seconds) + pass test_lib.tb_avalon_sink_compliance.accept_unexpected_msg_type.Test unexpected message handling (0.8 seconds) + pass test_lib.tb_avalon_sink_compliance.fail_unexpected_msg_type_with_null_checker.Test unexpected message handling (0.8 seconds) + pass test_lib.tb_avalon_sink_compliance.fail_unexpected_msg_type_with_custom_checker.Test unexpected message handling (0.8 seconds) + ========================================================================================================================================= + pass 9 of 9 + ========================================================================================================================================= + Total time was 7.6 seconds + Elapsed time was 7.7 seconds + ========================================================================================================================================= + All passed! + +Rule 1 +------ + +The file containing the VC entity shall only contain one entity and the file +containing the VCI package shall only contain one package. + +**Rationale**: The VC/VCI can be referenced by file name which makes compliance +testing simpler. + +Rule 2 +------ + +The name of the function used to create a new instance handle for a VC, aka the +constructor, shall start with ``new_``. + +**Rationale**: Makes it easier for the compliance test to automatically find a +constructor and evaluate that with respect to the other applicable rules. + +Rule 3 +------ + +A VC constructor shall have a ``logger`` parameter giving the user the option +to specify the logger to use for VC reporting. + +**Rationale**: It enables user control of the logging, for example enabling +debug messages. + +Rule 4 +------ + +A VC constructor shall have a ``checker`` parameter giving the user the option +to specify the checker to use for VC checking. + +**Rationale**: It enables user control of the checking, for example setting +properties like the stop-level (without affecting the logger used above). + +Rule 5 +------ + +A VC constructor shall have an ``actor`` parameter giving the user the option +to specify the actor to use for VC communication. + +**Rationale**: It enables user control of the communication, for example setting +name for better trace logs. + +Rule 6 +------ + +A VC constructor shall have an ``unexpected_msg_type_policy`` parameter giving the +user the option to specify the action taken when the VC receives an unexpected message type. + +**Rationale**: A VC actor setup to subscribe to another actor may receive messages not +relevant for its operation. OTOH, VCs just addressed directly should only recieve messages it can handle. + +Rule 7 +------ + +A VC constructor shall have a default value for all required parameters above. + +**Rationale**: Makes it easier for the user if there is no preference on what to use. + +Rule 8 +------ + +The default value for the logger parameter shall be ``null_logger`` to indicate that the +VC should assign a logger internally. The internally assigned logger shall not be ``default_logger``. + +**Rationale**: Using a logger more associated with the VC makes the logger output easier to understand. +Using ``null_logger`` as the default parameter value makes it possible to distinguish between a parameter +not specified and the parameter specified with the intended default value. This can be used to detect +when the user unintendently create two VCs of the same type using the same logger. This is allowed but +deserves a warning if made by mistake. + +Rule 9 +------ + +The default value for the checker parameter shall be ``null_checker`` to indicate that the VC should +assign a checker internally. The internally assigned checker shall not be ``default_checker``. + +**Rationale**: Using a checker more associated with the VC makes the checker output easier to understand. +Using ``null_checker`` as the default parameter value makes it possible to distinguish between a parameter +not specified and the parameter specified with the intended default value. If no value is specified but +a logger has been provided the VC can create a checker from the provided logger rather than using the default +checker for that VC. + +Rule 10 +------- + +The default value for the actor parameter shall be ``null_actor`` to indicate that the VC should assign an +actor internally. The internally assigned actor shall be a new actor for each VC. + +**Rationale**: VCs can never share actors in the same way they can share loggers and checkers. There can be +only one VC serving messages sent to a specific actor. + +Rule 11 +------- + +All fields in the handle returned by the constructor shall start with ``p_``. + +**Rationale**: All field shall be considered private and this is a way to emphasize this. Keeping them private +makes updates easier without breaking backward compatibility. + +Rule 12 +------- + +The standard configuration, ``std_cfg_t``, of a VC consisting of the required parameters to the constructor +shall be possible to get from the handle using a call to ``get_std_cfg``. + +**Rationale**: Makes it possible to reuse operations such as ``get_logger`` between VCs. + +Rule 13 +------- + +A VC shall only have one generic. + +**Rationale**: Representing a VC with a single object makes it easier to handle in code. Since all fields of +the handle are private future updates have less risk of breaking backward compatibility. + +Rule 14 +------- + +All VCs shall support the sync interface. + +**Rationale**: Being able to check that a VC is idle and to add a delay between transactions are commonly useful +operations for VC users. diff --git a/setup.py b/setup.py index 2af9d102c..b6dd34462 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,7 @@ def find_all_files(directory, endings=None): "vunit.sim_if", "vunit.test", "vunit.ui", + "vunit.vc", "vunit.vivado", ], package_data={"vunit": DATA_FILES}, diff --git a/tests/acceptance/artificial/vhdl/.vc/tb_vc_with_template_compliance_template.vhd b/tests/acceptance/artificial/vhdl/.vc/tb_vc_with_template_compliance_template.vhd new file mode 100644 index 000000000..7b7936ba1 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/.vc/tb_vc_with_template_compliance_template.vhd @@ -0,0 +1,61 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library lib; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vc_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use lib.vc_pkg_with_template.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_vc_with_template_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_vc_with_template_compliance is + + constant vc_h : vc_handle_t := new_vc( + unspecified => true + ); + + signal a : std_logic; + signal c, d : std_logic_vector(7 downto 0); + signal f : std_logic; + signal j : std_logic; + signal k, l : std_logic; + signal m : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity lib.vc_with_template + generic map(vc_h) + port map( + a => a, + b => open, + c => c, + d => d, + e => open, + f => f, + g => open, + h => open, + i => open, + j => j, + k => k, + l => l, + m => m + ); + +end architecture; diff --git a/tests/acceptance/artificial/vhdl/run.py b/tests/acceptance/artificial/vhdl/run.py index a3df9b08c..1cdd5a920 100644 --- a/tests/acceptance/artificial/vhdl/run.py +++ b/tests/acceptance/artificial/vhdl/run.py @@ -5,11 +5,13 @@ # Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com from pathlib import Path -from vunit import VUnit +from vunit import VUnit, VerificationComponentInterface, VerificationComponent ROOT = Path(__file__).parent VU = VUnit.from_argv() +VU.add_com() +VU.add_verification_components() LIB = VU.add_library("lib") LIB.add_source_files(ROOT / "*.vhd") @@ -88,4 +90,52 @@ def configure_tb_assert_stop_level(ui): 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")) + +TEST_LIB = VU.add_library("test_lib") + +VCI = VerificationComponentInterface.find(LIB, "vc_pkg", "vc_handle_t") +VerificationComponent.find(LIB, "vc", VCI).add_vhdl_testbench(TEST_LIB, ROOT / "compliance_test") + +VCI = VerificationComponentInterface.find(LIB, "vc_pkg_with_template", "vc_handle_t") +VerificationComponent.find(LIB, "vc_with_template", VCI).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_vc_with_template_compliance_template.vhd", +) + + +vci = VerificationComponentInterface.find(LIB, "vc_not_supporting_sync_pkg", "vc_not_supporting_sync_handle_t") +VerificationComponent.find(LIB, "vc_not_supporting_sync", vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +vci = VerificationComponentInterface.find( + LIB, "vc_not_supporting_custom_actor_pkg", "vc_not_supporting_custom_actor_handle_t" +) +VerificationComponent.find(LIB, "vc_not_supporting_custom_actor", vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +vci = VerificationComponentInterface.find( + LIB, + "vc_not_supporting_custom_logger_pkg", + "vc_not_supporting_custom_logger_handle_t", +) +VerificationComponent.find(LIB, "vc_not_supporting_custom_logger", vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +vci = VerificationComponentInterface.find( + LIB, + "vc_not_supporting_unexpected_msg_handling_pkg", + "vc_not_supporting_unexpected_msg_handling_handle_t", +) +VerificationComponent.find(LIB, "vc_not_supporting_unexpected_msg_handling", vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + VU.main() diff --git a/tests/acceptance/artificial/vhdl/vc.vhd b/tests/acceptance/artificial/vhdl/vc.vhd new file mode 100644 index 000000000..8c21ac618 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc.vhd @@ -0,0 +1,50 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; + +use work.vc_pkg.all; + +entity vc is + generic( + vc_h : vc_handle_t + ); + port( + a : in std_logic; + b : in std_logic := '1'; + c, d : in std_logic_vector(0 to 1); + e : in std_logic_vector := X"00"; + f : inout std_logic; + g : inout std_logic := '0'; + h, i : inout std_logic := '0'; + j : out std_logic; + k, l : out std_logic; + m : out std_logic := '1' + ); +end entity; + +architecture a of vc is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(vc_h.p_std_cfg), msg); + + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + unexpected_msg_type(msg_type, vc_h.p_std_cfg); + end process; +end architecture; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor.vhd new file mode 100644 index 000000000..7640e2703 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +use work.vc_not_supporting_custom_actor_pkg.all; + +entity vc_not_supporting_custom_actor is + generic( + vc_h : vc_not_supporting_custom_actor_handle_t + ); +end entity; + +architecture a of vc_not_supporting_custom_actor is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, actor_vec_t'(vc_not_supporting_custom_actor_actor, get_actor(vc_h.p_std_cfg)), msg); + + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + unexpected_msg_type(msg_type, vc_h.p_std_cfg); + end process; +end architecture; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor_pkg.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor_pkg.vhd new file mode 100644 index 000000000..7611c6138 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_actor_pkg.vhd @@ -0,0 +1,75 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_not_supporting_custom_actor_pkg is + type vc_not_supporting_custom_actor_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_not_supporting_custom_actor_logger : logger_t := get_logger("vc_not_supporting_custom_actor"); + constant vc_not_supporting_custom_actor_checker : checker_t := new_checker(vc_not_supporting_custom_actor_logger); + constant vc_not_supporting_custom_actor_actor : actor_t := new_actor("vc_not_supporting_custom_actor_actor"); + + constant transaction_msg : msg_type_t := new_msg_type("transaction"); + + impure function new_vc_not_supporting_custom_actor( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_custom_actor_handle_t; + + procedure transaction( + signal net : inout network_t; + vc_h : vc_not_supporting_custom_actor_handle_t + ); + + impure function as_sync( + vc_h : vc_not_supporting_custom_actor_handle_t + ) return sync_handle_t; + +end package; + +package body vc_not_supporting_custom_actor_pkg is + impure function new_vc_not_supporting_custom_actor( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_custom_actor_handle_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + vc_not_supporting_custom_actor_logger, vc_not_supporting_custom_actor_checker, actor, logger, checker, unexpected_msg_type_policy + ); + + begin + return (p_std_cfg => p_std_cfg); + end; + + procedure transaction( + signal net : inout network_t; + vc_h : vc_not_supporting_custom_actor_handle_t + ) is + variable msg : msg_t; + begin + msg := new_msg(transaction_msg); + send(net, vc_not_supporting_custom_actor_actor, msg); + end procedure; + + impure function as_sync( + vc_h : vc_not_supporting_custom_actor_handle_t + ) return sync_handle_t is + begin + return vc_not_supporting_custom_actor_actor; + end; + +end package body; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger.vhd new file mode 100644 index 000000000..83c9a8321 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +use work.vc_not_supporting_custom_logger_pkg.all; + +entity vc_not_supporting_custom_logger is + generic( + vc_h : vc_not_supporting_custom_logger_handle_t + ); +end entity; + +architecture a of vc_not_supporting_custom_logger is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(vc_h.p_std_cfg), msg); + + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + unexpected_msg_type(msg_type, vc_h.p_std_cfg); + end process; +end architecture; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger_pkg.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger_pkg.vhd new file mode 100644 index 000000000..90f4b3b43 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_custom_logger_pkg.vhd @@ -0,0 +1,69 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_not_supporting_custom_logger_pkg is + type vc_not_supporting_custom_logger_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_not_supporting_custom_logger_logger : logger_t := get_logger("vc_not_supporting_custom_logger"); + constant vc_not_supporting_custom_logger_checker : checker_t := new_checker(vc_not_supporting_custom_logger_logger); + + impure function new_vc_not_supporting_custom_logger( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_custom_logger_handle_t; + + impure function as_sync( + vc_h : vc_not_supporting_custom_logger_handle_t + ) return sync_handle_t; + +end package; + +package body vc_not_supporting_custom_logger_pkg is + impure function new_vc_not_supporting_custom_logger( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_custom_logger_handle_t is + variable p_std_cfg : std_cfg_t; + + begin + if actor = null_actor then + p_std_cfg.p_actor := new_actor; + else + p_std_cfg.p_actor := actor; + end if; + + if checker = null_checker then + p_std_cfg.p_checker := vc_not_supporting_custom_logger_checker; + else + p_std_cfg.p_checker := checker; + end if; + p_std_cfg.p_logger := vc_not_supporting_custom_logger_logger; + p_std_cfg.p_unexpected_msg_type_policy := unexpected_msg_type_policy; + + return (p_std_cfg => p_std_cfg); + end; + + impure function as_sync( + vc_h : vc_not_supporting_custom_logger_handle_t + ) return sync_handle_t is + begin + return get_actor(vc_h.p_std_cfg); + end; + +end package body; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_sync.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_sync.vhd new file mode 100644 index 000000000..ff370318d --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_sync.vhd @@ -0,0 +1,34 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +use work.vc_not_supporting_sync_pkg.all; + +entity vc_not_supporting_sync is + generic( + vc_h : vc_not_supporting_sync_handle_t + ); +end entity; + +architecture a of vc_not_supporting_sync is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(vc_h.p_std_cfg), msg); + + msg_type := message_type(msg); + + unexpected_msg_type(msg_type, vc_h.p_std_cfg); + end process; +end architecture; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_sync_pkg.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_sync_pkg.vhd new file mode 100644 index 000000000..19389e862 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_sync_pkg.vhd @@ -0,0 +1,56 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_not_supporting_sync_pkg is + type vc_not_supporting_sync_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_not_supporting_sync_logger : logger_t := get_logger("vc_not_supporting_sync"); + constant vc_not_supporting_sync_checker : checker_t := new_checker(vc_not_supporting_sync_logger); + + impure function new_vc_not_supporting_sync( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_sync_handle_t; + + impure function as_sync( + vc_h : vc_not_supporting_sync_handle_t + ) return sync_handle_t; + +end package; + +package body vc_not_supporting_sync_pkg is + impure function new_vc_not_supporting_sync( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_sync_handle_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + vc_not_supporting_sync_logger, vc_not_supporting_sync_checker, actor, logger, checker, unexpected_msg_type_policy + ); + begin + return (p_std_cfg => p_std_cfg); + end; + + impure function as_sync( + vc_h : vc_not_supporting_sync_handle_t + ) return sync_handle_t is + begin + return get_actor(vc_h.p_std_cfg); + end; + +end package body; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling.vhd new file mode 100644 index 000000000..085c2bfce --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling.vhd @@ -0,0 +1,36 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +use work.vc_not_supporting_unexpected_msg_handling_pkg.all; + +entity vc_not_supporting_unexpected_msg_handling is + generic( + vc_h : vc_not_supporting_unexpected_msg_handling_handle_t + ); +end entity; + +architecture a of vc_not_supporting_unexpected_msg_handling is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(vc_h.p_std_cfg), msg); + + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + + unexpected_msg_type(msg_type, get_checker(vc_h.p_std_cfg)); + end process; +end architecture; + diff --git a/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling_pkg.vhd b/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling_pkg.vhd new file mode 100644 index 000000000..045987cee --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_not_supporting_unexpected_msg_handling_pkg.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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_not_supporting_unexpected_msg_handling_pkg is + type vc_not_supporting_unexpected_msg_handling_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_not_supporting_unexpected_msg_handling_logger : logger_t := get_logger("vc_not_supporting_unexpected_msg_handling"); + constant vc_not_supporting_unexpected_msg_handling_checker : checker_t := new_checker(vc_not_supporting_unexpected_msg_handling_logger); + + impure function new_vc_not_supporting_unexpected_msg_handling( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_unexpected_msg_handling_handle_t; + + impure function as_sync( + vc_h : vc_not_supporting_unexpected_msg_handling_handle_t + ) return sync_handle_t; + +end package; + +package body vc_not_supporting_unexpected_msg_handling_pkg is + impure function new_vc_not_supporting_unexpected_msg_handling( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_not_supporting_unexpected_msg_handling_handle_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + vc_not_supporting_unexpected_msg_handling_logger, vc_not_supporting_unexpected_msg_handling_checker, + actor, logger, checker, unexpected_msg_type_policy + ); + begin + return (p_std_cfg => p_std_cfg); + end; + + impure function as_sync( + vc_h : vc_not_supporting_unexpected_msg_handling_handle_t + ) return sync_handle_t is + begin + return get_actor(vc_h.p_std_cfg); + end; + +end package body; + diff --git a/tests/acceptance/artificial/vhdl/vc_pkg.vhd b/tests/acceptance/artificial/vhdl/vc_pkg.vhd new file mode 100644 index 000000000..d2d05f972 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_pkg.vhd @@ -0,0 +1,54 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_logger : logger_t := get_logger("vc"); + constant vc_checker : checker_t := new_checker(vc_logger); + + impure function new_vc( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t; + + impure function as_sync( + vc_h : vc_handle_t + ) return sync_handle_t; + +end package; + +package body vc_pkg is + impure function new_vc( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + vc_logger, vc_checker, actor, logger, checker, unexpected_msg_type_policy + ); + begin + return (p_std_cfg => p_std_cfg); + end; + + impure function as_sync(vc_h : vc_handle_t) return sync_handle_t is + begin + return get_actor(vc_h.p_std_cfg); + end; + +end package body; + diff --git a/tests/acceptance/artificial/vhdl/vc_pkg_with_template.vhd b/tests/acceptance/artificial/vhdl/vc_pkg_with_template.vhd new file mode 100644 index 000000000..a76f484cd --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_pkg_with_template.vhd @@ -0,0 +1,56 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +package vc_pkg_with_template is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + constant vc_logger : logger_t := get_logger("vc"); + constant vc_checker : checker_t := new_checker(vc_logger); + + impure function new_vc( + unspecified : boolean; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t; + + impure function as_sync( + vc_h : vc_handle_t + ) return sync_handle_t; + +end package; + +package body vc_pkg_with_template is + impure function new_vc( + unspecified : boolean; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + vc_logger, vc_checker, actor, logger, checker, unexpected_msg_type_policy + ); + + begin + return (p_std_cfg => p_std_cfg); + end; + + impure function as_sync(vc_h : vc_handle_t) return sync_handle_t is + begin + return get_actor(vc_h.p_std_cfg); + end; + +end package body; diff --git a/tests/acceptance/artificial/vhdl/vc_with_template.vhd b/tests/acceptance/artificial/vhdl/vc_with_template.vhd new file mode 100644 index 000000000..ba50d9094 --- /dev/null +++ b/tests/acceptance/artificial/vhdl/vc_with_template.vhd @@ -0,0 +1,49 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context vunit_lib.com_context; +context vunit_lib.vc_context; +use vunit_lib.vc_pkg.all; + +library ieee; +use ieee.std_logic_1164.all; + +use work.vc_pkg_with_template.all; + +entity vc_with_template is + generic( + vc_h : vc_handle_t + ); + port( + a : in std_logic; + b : in std_logic := '1'; + c, d : in std_logic_vector; + e : in std_logic_vector := X"00"; + f : inout std_logic; + g : inout std_logic := '0'; + h, i : inout std_logic := '0'; + j : out std_logic; + k, l : out std_logic; + m : out std_logic := '1' + ); +end entity; + +architecture a of vc_with_template is +begin + controller : process + variable msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(vc_h.p_std_cfg), msg); + + msg_type := message_type(msg); + + handle_sync_message(net, msg_type, msg); + unexpected_msg_type(msg_type, vc_h.p_std_cfg); + end process; +end architecture; diff --git a/tests/acceptance/test_artificial.py b/tests/acceptance/test_artificial.py index 7f400a245..7414503b6 100644 --- a/tests/acceptance/test_artificial.py +++ b/tests/acceptance/test_artificial.py @@ -245,4 +245,131 @@ def test_exit_0_flag(self): "failed", "lib.tb_assert_stop_level.Report failure when VHDL assert stop level = failure", ), + ("passed", "test_lib.tb_vc_compliance.Test that sync interface is supported"), + ("passed", "test_lib.tb_vc_compliance.Test that the actor can be customised"), + ( + "passed", + "test_lib.tb_vc_compliance.accept_unexpected_msg_type.Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_compliance.fail_unexpected_msg_type_with_null_checker.Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_compliance.fail_unexpected_msg_type_with_custom_checker.Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_sync_compliance.accept_unexpected_msg_type.Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_sync_compliance.fail_unexpected_msg_type_with_null_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_sync_compliance.fail_unexpected_msg_type_with_custom_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_actor_compliance.Test that sync interface is supported", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_actor_compliance.accept_unexpected_msg_type." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_actor_compliance.fail_unexpected_msg_type_with_null_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_actor_compliance.fail_unexpected_msg_type_with_custom_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_logger_compliance.Test that sync interface is supported", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_logger_compliance.Test that the actor can be customised", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_logger_compliance.accept_unexpected_msg_type." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_custom_logger_compliance.fail_unexpected_msg_type_with_custom_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_unexpected_msg_handling_compliance.Test that sync interface is supported", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_unexpected_msg_handling_compliance.Test that the actor can be customised", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_unexpected_msg_handling_compliance.fail_unexpected_msg_type_with_null_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_not_supporting_unexpected_msg_handling_compliance." + "fail_unexpected_msg_type_with_custom_checker.Test unexpected message handling", + ), + ( + "failed", + "test_lib.tb_vc_not_supporting_sync_compliance.Test that sync interface is supported", + ), + ( + "failed", + "test_lib.tb_vc_not_supporting_sync_compliance.Test that the actor can be customised", + ), + ( + "failed", + "test_lib.tb_vc_not_supporting_custom_actor_compliance.Test that the actor can be customised", + ), + ( + "failed", + "test_lib.tb_vc_not_supporting_custom_logger_compliance.fail_unexpected_msg_type_with_null_checker." + "Test unexpected message handling", + ), + ( + "failed", + "test_lib.tb_vc_not_supporting_unexpected_msg_handling_compliance.accept_unexpected_msg_type." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_with_template_compliance.Test that sync interface is supported", + ), + ( + "passed", + "test_lib.tb_vc_with_template_compliance.Test that the actor can be customised", + ), + ( + "passed", + "test_lib.tb_vc_with_template_compliance.accept_unexpected_msg_type.Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_with_template_compliance.fail_unexpected_msg_type_with_null_checker." + "Test unexpected message handling", + ), + ( + "passed", + "test_lib.tb_vc_with_template_compliance.fail_unexpected_msg_type_with_custom_checker." + "Test unexpected message handling", + ), ) diff --git a/tests/lint/test_license.py b/tests/lint/test_license.py index 6f28b58b6..c4b02fb85 100644 --- a/tests/lint/test_license.py +++ b/tests/lint/test_license.py @@ -131,6 +131,8 @@ def find_licensed_files(): continue if "codecs" in root: continue + if "compliance_test" in root: + continue if root == str(ROOT / "docs"): continue if str(ROOT / "venv") in root: diff --git a/tests/unit/test_compliance_test.py b/tests/unit/test_compliance_test.py new file mode 100644 index 000000000..041a8d553 --- /dev/null +++ b/tests/unit/test_compliance_test.py @@ -0,0 +1,677 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +""" +Test the compliance test. +""" +from unittest import TestCase, mock +from shutil import rmtree +from pathlib import Path +from itertools import product +import re +from vunit.ostools import renew_path +from vunit.vc.verification_component_interface import ( + VerificationComponentInterface, + LOGGER, +) +from vunit.vc.verification_component import VerificationComponent +from vunit.vc.compliance_test import main +from vunit import VUnit +from vunit.vhdl_parser import VHDLDesignFile, VHDLReference + + +class TestComplianceTest(TestCase): # pylint: disable=too-many-public-methods + """Tests the ComplianceTest class.""" + + def setUp(self): + self.tmp_dir = Path(__file__).parent / "vc_tmp" + renew_path(str(self.tmp_dir)) + self.vc_dir = self.tmp_dir / "vc" + renew_path(str(self.vc_dir)) + self.vci_dir = self.tmp_dir / "vci" + renew_path(str(self.vci_dir)) + self.vc_contents = """ +library ieee +use ieee.std_logic_1164.all; + +entity vc is + generic(vc_h : vc_handle_t); + port( + a, b : in std_logic; + c : in std_logic := '0'; + d, e : inout std_logic; + f, g : inout std_logic := '1'; + h, i : out std_logic := '0'; + j : out std_logic); + +end entity; +""" + self.vc_path = self.make_file("vc.vhd", self.vc_contents, self.vc_dir) + + self.vci_contents = """ +package vc_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function new_vc( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t; + + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + self.vci_path = self.make_file("vci.vhd", self.vci_contents, self.vci_dir) + + self.ui = VUnit.from_argv([]) + + self.vc_lib = self.ui.add_library("vc_lib") + self.vc_lib.add_source_files(str(self.vc_dir / "*.vhd")) + + self.vci_lib = self.ui.add_library("vci_lib") + self.vci_lib.add_source_files(str(self.vci_dir / "*.vhd")) + + def tearDown(self): + if self.tmp_dir.exists(): + rmtree(self.tmp_dir) + + def make_file(self, file_name, contents, directory=None): + """ + Create a file in the temporary directory with contents + Returns the absolute path to the file. + """ + directory = directory if directory is not None else self.tmp_dir + full_file_name = (directory / file_name).resolve() + with full_file_name.open("w") as outfile: + outfile.write(contents) + return str(full_file_name) + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_not_finding_vc(self, error_mock): + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "other_vc", vci) + error_mock.assert_called_once_with("Failed to find VC %s", "other_vc") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_not_finding_vci(self, error_mock): + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vci_lib, + "other_vc_pkg", + "vc_handle_t", + ) + error_mock.assert_called_once_with("Failed to find VCI %s", "other_vc_pkg") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_on_multiple_entities(self, error_mock): + vc_contents = """ +entity vc1 is + generic(a : bit); +end entity; + +entity vc2 is + generic(b : bit); +end entity; +""" + self.vc_lib.add_source_file(self.make_file("vc1_2.vhd", vc_contents)) + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "vc1", vci) + error_mock.assert_called_once_with("%s must contain a single VC entity", self.tmp_dir / "vc1_2.vhd") + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "vc2", vci) + error_mock.assert_called_with("%s must contain a single VC entity", self.tmp_dir / "vc1_2.vhd") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_on_multiple_package(self, error_mock): + vci_contents = """ +package vc_pkg1 is +end package; + +package vc_pkg2 is +end package; +""" + self.vc_lib.add_source_file(self.make_file("vci1_2.vhd", vci_contents)) + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "vc_pkg1", + "vc_handle_t", + ) + error_mock.assert_called_once_with("%s must contain a single VCI package", self.tmp_dir / "vci1_2.vhd") + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "vc_pkg2", + "vc_handle_t", + ) + error_mock.assert_called_with("%s must contain a single VCI package", self.tmp_dir / "vci1_2.vhd") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_evaluating_vc_generics(self, error_mock): + vc1_contents = """ +entity vc1 is +end entity; +""" + self.vc_lib.add_source_file(self.make_file("vc1.vhd", vc1_contents)) + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "vc1", vci) + error_mock.assert_called_once_with("%s must have a single generic", "vc1") + + vc2_contents = """ +entity vc2 is + generic(a : bit; b : bit); +end entity; +""" + self.vc_lib.add_source_file(self.make_file("vc2.vhd", vc2_contents)) + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "vc2", vci) + error_mock.assert_called_with("%s must have a single generic", "vc2") + + vc3_contents = """ +entity vc3 is + generic(a, b : bit); +end entity; +""" + self.vc_lib.add_source_file(self.make_file("vc3.vhd", vc3_contents)) + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + self.assertRaises(SystemExit, VerificationComponent.find, self.vc_lib, "vc3", vci) + error_mock.assert_called_with("%s must have a single generic", "vc3") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_with_no_constructor(self, error_mock): + vci_contents = """\ +package other_vc_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function create_vc return vc_handle_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + self.vc_lib.add_source_file(self.make_file("other_vci.vhd", vci_contents)) + + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_pkg", + "vc_handle_t", + ) + error_mock.assert_called_once_with("Failed to find a constructor function for vc_handle_t starting with new_") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_with_wrong_constructor_return_type(self, error_mock): + vci_contents = """\ +package other_vc_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function new_vc return vc_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; + +end package; +""" + self.vc_lib.add_source_file(self.make_file("other_vci.vhd", vci_contents)) + + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_pkg", + "vc_handle_t", + ) + error_mock.assert_called_once_with( + "Found constructor function new_vc but not with the correct return type vc_handle_t" + ) + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_on_incorrect_constructor_parameters(self, error_mock): + parameters = dict( + logger=("logger_t", "null_logger"), + actor=("actor_t", "null_actor"), + checker=("checker_t", "null_checker"), + unexpected_msg_type_policy=("unexpected_msg_type_policy_t", "fail"), + ) + reasons_for_failure = [ + "missing_parameter", + "invalid_type", + "invalid_default_value", + ] + + for iteration, (invalid_parameter, invalid_reason) in enumerate(product(parameters, reasons_for_failure)): + if (invalid_parameter in ["unexpected_msg_type_policy", "logger"]) and ( + invalid_reason == "invalid_default_value" + ): + continue + + vci_contents = ( + """\ +package other_vc_%d_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function new_vc( +""" + % iteration + ) + for parameter_name, parameter_data in parameters.items(): + if parameter_name != invalid_parameter: + vci_contents += " %s : %s := %s;\n" % ( + parameter_name, + parameter_data[0], + parameter_data[1], + ) + elif invalid_reason == "invalid_type": + vci_contents += " %s : invalid_type := %s;\n" % ( + parameter_name, + parameter_data[1], + ) + elif invalid_reason == "invalid_default_value": + vci_contents += " %s : %s := invalid_default_value;\n" % ( + parameter_name, + parameter_data[0], + ) + + vci_contents = ( + vci_contents[:-2] + + """ + ) return vc_handle_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + ) + self.vc_lib.add_source_file(self.make_file("other_vci_%d.vhd" % iteration, vci_contents)) + + if invalid_reason == "missing_parameter": + error_msg = ( + "Found constructor function new_vc for vc_handle_t but the %s parameter is missing" + % invalid_parameter + ) + elif invalid_reason == "invalid_type": + error_msg = ( + "Found constructor function new_vc for vc_handle_t but the %s parameter is not of type %s" + % (invalid_parameter, parameters[invalid_parameter][0]) + ) + elif invalid_reason == "invalid_default_value": + error_msg = ( + "Found constructor function new_vc for vc_handle_t but null_%s is the only allowed " + "default value for the %s parameter" % (invalid_parameter, invalid_parameter) + ) + + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_%d_pkg" % iteration, + "vc_handle_t", + ) + error_mock.assert_called_with(error_msg) + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_on_non_private_handle_elements(self, error_mock): + vci_contents = """\ +package other_vc_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + foo : bar_t; + end record; + + impure function new_vc( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + + self.vc_lib.add_source_file(self.make_file("other_vci.vhd", vci_contents)) + + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_pkg", + "vc_handle_t", + ) + error_mock.assert_called_once_with("%s in %s doesn't start with p_", "foo", "vc_handle_t") + + @mock.patch("vunit.vc.verification_component_interface.LOGGER.error") + def test_failing_on_missing_handle_record(self, error_mock): + vci_contents = """\ +package other_vc_pkg is + type handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function new_vc( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return vc_handle_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + + self.vc_lib.add_source_file(self.make_file("other_vci.vhd", vci_contents)) + + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_pkg", + "vc_handle_t", + ) + error_mock.assert_called_once_with( + "Failed to find %s record", + "vc_handle_t", + ) + + def test_error_on_missing_default_value(self): + parameters = dict( + logger=("logger_t", "null_logger"), + actor=("actor_t", "null_actor"), + checker=("checker_t", "null_checker"), + unexpected_msg_type_policy=("unexpected_msg_type_policy_t", "fail"), + ) + + for iteration, parameter_wo_init_value in enumerate(parameters): + vci_contents = ( + """\ +package other_vc_%d_pkg is + type vc_handle_t is record + p_std_cfg : std_cfg_t; + end record; + + impure function new_vc( +""" + % iteration + ) + for parameter_name, parameter_data in parameters.items(): + if parameter_name != parameter_wo_init_value: + vci_contents += " %s : %s := %s;\n" % ( + parameter_name, + parameter_data[0], + parameter_data[1], + ) + else: + vci_contents += " %s : %s;\n" % ( + parameter_name, + parameter_data[0], + ) + + vci_contents = ( + vci_contents[:-2] + + """ + ) return vc_handle_t; + function as_sync(handle : vc_handle_t) return sync_handle_t; +end package; +""" + ) + self.vc_lib.add_source_file(self.make_file("other_vci_%d.vhd" % iteration, vci_contents)) + + with mock.patch.object(LOGGER, "error") as error_mock: + self.assertRaises( + SystemExit, + VerificationComponentInterface.find, + self.vc_lib, + "other_vc_%d_pkg" % iteration, + "vc_handle_t", + ) + error_mock.assert_called_once_with( + "Found constructor function new_vc for vc_handle_t but %s is lacking a default value" + % parameter_wo_init_value + ) + + def test_create_vhdl_testbench_template_references(self): + vc_contents = """\ +library std; +library work; +library a_lib; + +use std.a.all; +use work.b.c; +use a_lib.x.y; + +context work.spam; +context a_lib.eggs; + +entity vc2 is + generic(vc_h : vc_handle_t); +end entity; +""" + + vc_path = self.make_file("vc2.vhd", vc_contents) + template, _ = VerificationComponent.create_vhdl_testbench_template("vc_lib", "vc_lib", vc_path, self.vci_path) + template = VHDLDesignFile.parse(template) + refs = template.references + self.assertEqual(len(refs), 14) + self.assertIn(VHDLReference("library", "vunit_lib"), refs) + self.assertIn(VHDLReference("library", "vc_lib"), refs) + self.assertIn(VHDLReference("library", "a_lib"), refs) + self.assertIn(VHDLReference("package", "std", "a", "all"), refs) + self.assertIn(VHDLReference("package", "vc_lib", "b", "c"), refs) + self.assertIn(VHDLReference("package", "vc_lib", "vc_pkg", "all"), refs) + self.assertIn(VHDLReference("package", "a_lib", "x", "y"), refs) + self.assertIn(VHDLReference("package", "vunit_lib", "sync_pkg", "all"), refs) + self.assertIn(VHDLReference("package", "vunit_lib", "vc_pkg", "all"), refs) + self.assertIn(VHDLReference("context", "vc_lib", "spam"), refs) + self.assertIn(VHDLReference("context", "a_lib", "eggs"), refs) + self.assertIn(VHDLReference("context", "vunit_lib", "vunit_context"), refs) + self.assertIn(VHDLReference("context", "vunit_lib", "com_context"), refs) + self.assertIn(VHDLReference("entity", "vc_lib", "vc2"), refs) + + def test_template_with_wrong_name(self): + template_contents = """\ +entity tb_vc2_compliance is + generic(runner_cfg : string); +end entity; + +architecture a of tb_vc2_compliance is + constant vc_h : vc_handle_t := new_vc; +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" + + template_path = self.make_file("template.vhd", template_contents) + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + vc = VerificationComponent.find(self.vc_lib, "vc", vci) + self.assertRaises(RuntimeError, vc.create_vhdl_testbench, template_path) + + def test_template_missing_contructor(self): + template_contents = """\ +entity tb_vc_compliance is + generic(runner_cfg : string); +end entity; + +architecture a of tb_vc_compliance is +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" + + template_path = self.make_file("template.vhd", template_contents) + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + vc = VerificationComponent.find(self.vc_lib, "vc", vci) + self.assertRaises(RuntimeError, vc.create_vhdl_testbench, template_path) + + def test_template_missing_runner_cfg(self): + template_contents = """\ +entity tb_vc_compliance is + generic(foo : bar); +end entity; + +architecture a of tb_vc_compliance is + constant vc_h : vc_handle_t := new_vc; +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" + + template_path = self.make_file("template.vhd", template_contents) + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + vc = VerificationComponent.find(self.vc_lib, "vc", vci) + self.assertRaises(RuntimeError, vc.create_vhdl_testbench, template_path) + + def test_template_missing_test_runner(self): + template_contents = """\ +entity tb_vc_compliance is + generic(runner_cfg : string); +end entity; + +architecture a of tb_vc_compliance is + constant vc_h : vc_handle_t := new_vc; +begin + main : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" + + template_path = self.make_file("template.vhd", template_contents) + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + vc = VerificationComponent.find(self.vc_lib, "vc", vci) + self.assertRaises(RuntimeError, vc.create_vhdl_testbench, template_path) + + def test_creating_template_without_output_path(self): + with mock.patch("sys.argv", ["compliance_test.py", "create-vc", self.vc_path, self.vci_path]): + main() + + self.assertTrue((Path(self.vc_path).parent / ".vc" / "tb_vc_compliance_template.vhd").exists()) + + def test_creating_template_with_output_dir(self): + output_dir = self.tmp_dir / "template" + output_dir.mkdir(parents=True) + with mock.patch( + "sys.argv", + [ + "compliance_test.py", + "create-vc", + "-o", + str(output_dir), + self.vc_path, + self.vci_path, + ], + ): + main() + self.assertTrue((output_dir / "tb_vc_compliance_template.vhd").exists()) + + def test_creating_template_with_output_file(self): + output_dir = self.tmp_dir / "template" + output_dir.mkdir(parents=True) + output_path = output_dir / "template.vhd" + with mock.patch( + "sys.argv", + [ + "compliance_test.py", + "create-vc", + "--output-path", + str(output_path), + self.vc_path, + self.vci_path, + ], + ): + main() + self.assertTrue(output_path.exists()) + + def test_creating_template_with_invalid_output_path(self): + output_dir = self.tmp_dir / "test" + output_path = output_dir / "template.vhd" + with mock.patch( + "sys.argv", + [ + "compliance_test.py", + "create-vc", + "--output-path", + str(output_path), + self.vc_path, + self.vci_path, + ], + ): + self.assertRaises(IOError, main) + + def test_creating_template_with_default_vc_lib(self): + with mock.patch("sys.argv", ["compliance_test.py", "create-vc", self.vc_path, self.vci_path]): + main() + with (Path(self.vc_path).parent / ".vc" / "tb_vc_compliance_template.vhd").open() as fptr: + self.assertIsNotNone( + re.search( + r"library\s+vc_lib\s*;", + fptr.read(), + re.IGNORECASE | re.MULTILINE, + ) + ) + + def test_creating_template_with_specified_vc_lib(self): + with mock.patch( + "sys.argv", + [ + "compliance_test.py", + "create-vc", + "--vc-lib-name", + "my_vc_lib", + "--vci-lib-name", + "my_vc_lib", + self.vc_path, + self.vci_path, + ], + ): + main() + with (Path(self.vc_path).parent / ".vc" / "tb_vc_compliance_template.vhd").open() as fptr: + self.assertIsNotNone( + re.search( + r"library\s+my_vc_lib\s*;", + fptr.read(), + re.IGNORECASE | re.MULTILINE, + ) + ) + + def test_adding_vhdl_testbench(self): + vc_test_lib = self.ui.add_library("vc_test_lib") + + vci = VerificationComponentInterface.find(self.vci_lib, "vc_pkg", "vc_handle_t") + vc = VerificationComponent.find(self.vc_lib, "vc", vci) + + vc.add_vhdl_testbench(vc_test_lib, str(self.tmp_dir / "compliance_test")) + + self.assertTrue((self.tmp_dir / "compliance_test" / "tb_vc_compliance.vhd").exists()) + self.assertRaises( + RuntimeError, + vc.add_vhdl_testbench, + vc_test_lib, + str(self.tmp_dir / "compliance_test"), + ) diff --git a/tests/unit/test_vhdl_parser.py b/tests/unit/test_vhdl_parser.py index a7bbedf00..17be25b75 100644 --- a/tests/unit/test_vhdl_parser.py +++ b/tests/unit/test_vhdl_parser.py @@ -18,6 +18,7 @@ VHDLArrayType, VHDLReference, VHDLRecordType, + VHDLFunctionSpecification, remove_comments, ) @@ -58,7 +59,7 @@ def test_parsing_entity_with_package_generic(self): self.assertEqual(entity.identifier, "ent") self.assertEqual(entity.ports, []) self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "package_g") + self.assertEqual(entity.generics[0].identifier_list, ["package_g"]) self.assertEqual(entity.generics[0].subtype_indication.type_mark, "integer") def test_parsing_entity_with_type_generic(self): @@ -75,7 +76,7 @@ def test_parsing_entity_with_type_generic(self): self.assertEqual(entity.identifier, "ent") self.assertEqual(entity.ports, []) self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "type_g") + self.assertEqual(entity.generics[0].identifier_list, ["type_g"]) self.assertEqual(entity.generics[0].subtype_indication.type_mark, "integer") def test_parsing_entity_with_string_semicolon_colon(self): @@ -93,13 +94,13 @@ def test_parsing_entity_with_string_semicolon_colon(self): self.assertEqual(entity.identifier, "ent") self.assertEqual(entity.ports, []) self.assertEqual(len(entity.generics), 3) - self.assertEqual(entity.generics[0].identifier, "const") + self.assertEqual(entity.generics[0].identifier_list, ["const"]) self.assertEqual(entity.generics[0].subtype_indication.type_mark, "string") self.assertEqual(entity.generics[0].init_value, '"a;a"') - self.assertEqual(entity.generics[1].identifier, "const2") + self.assertEqual(entity.generics[1].identifier_list, ["const2"]) self.assertEqual(entity.generics[1].subtype_indication.type_mark, "string") self.assertEqual(entity.generics[1].init_value, '";a""a;a"') - self.assertEqual(entity.generics[2].identifier, "const3") + self.assertEqual(entity.generics[2].identifier_list, ["const3"]) self.assertEqual(entity.generics[2].subtype_indication.type_mark, "string") self.assertEqual(entity.generics[2].init_value, '": a b c :"') @@ -120,9 +121,9 @@ def test_parsing_entity_with_function_generic(self): self.assertEqual(entity.identifier, "ent") self.assertEqual(entity.ports, []) self.assertEqual(len(entity.generics), 2) - self.assertEqual(entity.generics[0].identifier, "function_g") + self.assertEqual(entity.generics[0].identifier_list, ["function_g"]) self.assertEqual(entity.generics[0].subtype_indication.type_mark, "boolean") - self.assertEqual(entity.generics[1].identifier, "procedure_g") + self.assertEqual(entity.generics[1].identifier_list, ["procedure_g"]) self.assertEqual(entity.generics[1].subtype_indication.type_mark, "boolean") def test_getting_entities_from_design_file(self): @@ -190,7 +191,7 @@ def test_parsing_references(self): package new_pkg is new lib.pkg; """ ) - self.assertEqual(len(design_file.references), 14) + self.assertEqual(len(design_file.references), 19) self.assertEqual( sorted(design_file.references, key=repr), sorted( @@ -209,6 +210,11 @@ def test_parsing_references(self): VHDLReference("package", "name1", "bla", "all"), VHDLReference("package", "name1", "foo", "all"), VHDLReference("package", "lib", "pkg", None), + VHDLReference("library", "ieee", None, None), + VHDLReference("library", "name1", None, None), + VHDLReference("library", "lib1", None, None), + VHDLReference("library", "lib2", None, None), + VHDLReference("library", "lib3", None, None), ], key=repr, ), @@ -230,14 +236,14 @@ def test_parsing_entity_with_generics(self): generics = entity.generics self.assertEqual(len(generics), 2) - self.assertEqual(generics[0].identifier, "max_value") + self.assertEqual(generics[0].identifier_list, ["max_value"]) self.assertEqual(generics[0].init_value, "(2-19)*4") self.assertEqual(generics[0].mode, None) self.assertEqual(generics[0].subtype_indication.code, "integer range 2-2 to 2**10") self.assertEqual(generics[0].subtype_indication.type_mark, "integer") # @TODO does not work # self.assertEqual(generics[0].subtypeIndication.constraint, "range 2-2 to 2**10") - self.assertEqual(generics[1].identifier, "enable_foo") + self.assertEqual(generics[1].identifier_list, ["enable_foo"]) self.assertEqual(generics[1].init_value, None) self.assertEqual(generics[1].mode, None) self.assertEqual(generics[1].subtype_indication.code, "boolean") @@ -256,7 +262,7 @@ def test_parsing_entity_with_generics_corner_cases(self): """ ) self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") + self.assertEqual(entity.generics[0].identifier_list, ["g"]) entity = self.parse_single_entity( """\ @@ -268,7 +274,7 @@ def test_parsing_entity_with_generics_corner_cases(self): """ ) self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") + self.assertEqual(entity.generics[0].identifier_list, ["g"]) entity = self.parse_single_entity( """\ @@ -280,7 +286,7 @@ def test_parsing_entity_with_generics_corner_cases(self): """ ) self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "g") + self.assertEqual(entity.generics[0].identifier_list, ["g"]) entity = self.parse_single_entity( """\ @@ -310,13 +316,15 @@ def test_parsing_entity_with_ports(self): ports = entity.ports self.assertEqual(len(ports), 2) - self.assertEqual(ports[0].identifier, "clk") + self.assertEqual(ports[0].entity_class, "signal") + self.assertEqual(ports[0].identifier_list, ["clk"]) self.assertEqual(ports[0].init_value, None) self.assertEqual(ports[0].mode, "in") self.assertEqual(ports[0].subtype_indication.code, "std_logic") self.assertEqual(ports[0].subtype_indication.type_mark, "std_logic") - self.assertEqual(ports[1].identifier, "data") + self.assertEqual(ports[1].entity_class, "signal") + self.assertEqual(ports[1].identifier_list, ["data"]) self.assertEqual(ports[1].init_value, None) self.assertEqual(ports[1].mode, "out") self.assertEqual(ports[1].subtype_indication.code, "std_logic_vector(11-1 downto 0)") @@ -447,7 +455,8 @@ def test_adding_generics_to_entity(self): entity = VHDLEntity("name") entity.add_generic("max_value", "boolean", "20") self.assertEqual(len(entity.generics), 1) - self.assertEqual(entity.generics[0].identifier, "max_value") + self.assertEqual(entity.generics[0].entity_class, "constant") + self.assertEqual(entity.generics[0].identifier_list, ["max_value"]) self.assertEqual(entity.generics[0].subtype_indication.type_mark, "boolean") self.assertEqual(entity.generics[0].init_value, "20") @@ -455,7 +464,8 @@ def test_adding_ports_to_entity(self): entity = VHDLEntity("name") entity.add_port("foo", "inout", "foo_t") self.assertEqual(len(entity.ports), 1) - self.assertEqual(entity.ports[0].identifier, "foo") + self.assertEqual(entity.ports[0].entity_class, "signal") + self.assertEqual(entity.ports[0].identifier_list, ["foo"]) self.assertEqual(entity.ports[0].mode, "inout") self.assertEqual(entity.ports[0].subtype_indication.type_mark, "foo_t") @@ -520,6 +530,116 @@ def test_that_record_type_declarations_are_found(self): self.assertEqual(records["foo"][0].subtype_indication.constraint, "(7 downto 0)") self.assertTrue(records["foo"][0].subtype_indication.array_type) + def test_parsing_interface_element(self): + element = VHDLInterfaceElement.parse("a : bit") + self.assertEqual(element.identifier_list, ["a"]) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, None) + self.assertEqual(element.mode, None) + self.assertEqual(element.init_value, None) + + element = VHDLInterfaceElement.parse("a : in bit") + self.assertEqual(element.identifier_list, ["a"]) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, None) + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, None) + + element = VHDLInterfaceElement.parse("a : in bit", default_entity_class="constant") + self.assertEqual(element.identifier_list, ["a"]) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, "constant") + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, None) + + element = VHDLInterfaceElement.parse("signal a : in bit", default_entity_class="constant") + self.assertEqual(element.identifier_list, ["a"]) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, "signal") + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, None) + + element = VHDLInterfaceElement.parse("signal a : in bit := '1'", default_entity_class="constant") + self.assertEqual(element.identifier_list, ["a"]) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, "signal") + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, "'1'") + + element = VHDLInterfaceElement.parse("signal a, b : in bit := '1'") + self.assertEqual(len(element.identifier_list), 2) + self.assertIn("a", element.identifier_list) + self.assertIn("b", element.identifier_list) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, "signal") + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, "'1'") + + element = VHDLInterfaceElement.parse( + """\ +signal a ,b, + c:in bit:='1'""" + ) + self.assertEqual(len(element.identifier_list), 3) + self.assertIn("a", element.identifier_list) + self.assertIn("b", element.identifier_list) + self.assertIn("c", element.identifier_list) + self.assertEqual(element.subtype_indication.type_mark, "bit") + self.assertEqual(element.entity_class, "signal") + self.assertEqual(element.mode, "in") + self.assertEqual(element.init_value, "'1'") + + def test_that_function_declarations_are_found(self): + code = """\ +function with_no_parameters return boolean; +impure function with_side_effects return boolean; +function with_single_parameter(a : bit) return integer; +function with_multiple_parameters(a : bit; b : boolean) return integer; +function with_whitespaces ( a:bit ;b : boolean ) +return integer ; +""" + + functions = {f.identifier: f for f in VHDLFunctionSpecification.find(code)} + self.assertEqual(len(functions), 5) + + self.assertIn("with_no_parameters", functions) + self.assertEqual(functions["with_no_parameters"].return_type_mark, "boolean") + self.assertFalse(functions["with_no_parameters"].parameter_list) + + self.assertIn("with_side_effects", functions) + self.assertEqual(functions["with_side_effects"].return_type_mark, "boolean") + self.assertFalse(functions["with_side_effects"].parameter_list) + + self.assertIn("with_single_parameter", functions) + func = functions["with_single_parameter"] + self.assertEqual(func.return_type_mark, "integer") + self.assertEqual(len(func.parameter_list), 1) + self.assertEqual(func.parameter_list[0].entity_class, "constant") + self.assertEqual(func.parameter_list[0].identifier_list, ["a"]) + self.assertEqual(func.parameter_list[0].subtype_indication.type_mark, "bit") + + self.assertIn("with_multiple_parameters", functions) + func = functions["with_multiple_parameters"] + self.assertEqual(func.return_type_mark, "integer") + self.assertEqual(len(func.parameter_list), 2) + self.assertEqual(func.parameter_list[0].entity_class, "constant") + self.assertEqual(func.parameter_list[0].identifier_list, ["a"]) + self.assertEqual(func.parameter_list[0].subtype_indication.type_mark, "bit") + self.assertEqual(func.parameter_list[1].entity_class, "constant") + self.assertEqual(func.parameter_list[1].identifier_list, ["b"]) + self.assertEqual(func.parameter_list[1].subtype_indication.type_mark, "boolean") + + self.assertIn("with_whitespaces", functions) + func = functions["with_whitespaces"] + self.assertEqual(func.return_type_mark, "integer") + self.assertEqual(len(func.parameter_list), 2) + self.assertEqual(func.parameter_list[0].entity_class, "constant") + self.assertEqual(func.parameter_list[0].identifier_list, ["a"]) + self.assertEqual(func.parameter_list[0].subtype_indication.type_mark, "bit") + self.assertEqual(func.parameter_list[1].entity_class, "constant") + self.assertEqual(func.parameter_list[1].identifier_list, ["b"]) + self.assertEqual(func.parameter_list[1].subtype_indication.type_mark, "boolean") + def test_remove_comments(self): self.assertEqual(remove_comments("a\n-- foo \nb"), "a\n \nb") @@ -564,11 +684,11 @@ def _create_entity(): """ Helper function to create a VHDLEntity """ - data_width = VHDLInterfaceElement("data_width", VHDLSubtypeIndication.parse("natural := 16")) + data_width = VHDLInterfaceElement("constant", "data_width", VHDLSubtypeIndication.parse("natural := 16")) - clk = VHDLInterfaceElement("clk", VHDLSubtypeIndication.parse("std_logic"), "in") + clk = VHDLInterfaceElement("signal", "clk", VHDLSubtypeIndication.parse("std_logic"), "in") data = VHDLInterfaceElement( - "data", + "signal" "data", VHDLSubtypeIndication.parse("std_logic_vector(data_width-1 downto 0)"), "out", ) diff --git a/vunit/__init__.py b/vunit/__init__.py index 133bc692b..b376dfc35 100644 --- a/vunit/__init__.py +++ b/vunit/__init__.py @@ -13,6 +13,7 @@ from vunit.ui import VUnit from vunit.vunit_cli import VUnitCLI from vunit.about import version, doc +from vunit.vc import VerificationComponent, VerificationComponentInterface # Repository root ROOT = str(Path(__file__).parent.parent.resolve()) diff --git a/vunit/project.py b/vunit/project.py index d79b9dfb9..872a000e7 100644 --- a/vunit/project.py +++ b/vunit/project.py @@ -213,6 +213,9 @@ def _find_other_vhdl_design_unit_dependencies( # pylint: disable=too-many-branc LOGGER.warning("%s: failed to find library '%s'", source_file.name, ref.library) continue + if ref.is_library_reference(): + continue + if ref.is_entity_reference() and ref.design_unit in library.modules: # Is a verilog module instantiation yield library.modules[ref.design_unit].source_file diff --git a/vunit/source_file.py b/vunit/source_file.py index 608c5ebe3..0c1d92692 100644 --- a/vunit/source_file.py +++ b/vunit/source_file.py @@ -297,7 +297,10 @@ def _find_design_units(self, design_file): """ result = [] for entity in design_file.entities: - generic_names = [generic.identifier for generic in entity.generics] + generic_names = [] + for generic in entity.generics: + for identifier in generic.identifier_list: + generic_names.append(identifier) result.append(Entity(entity.identifier, self, generic_names)) for context in design_file.contexts: diff --git a/vunit/ui/entity.py b/vunit/ui/entity.py new file mode 100644 index 000000000..8f2475084 --- /dev/null +++ b/vunit/ui/entity.py @@ -0,0 +1,41 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +""" +UI class Entity +""" + + +class Entity(object): + """ + User interface of an Entity + """ + + def __init__(self, library_name, entity_name, source_file): + self._library_name = library_name + self._entity_name = entity_name + self._source_file = source_file + + @property + def name(self): + """ + :returns: The entity name + """ + return self._entity_name + + @property + def library(self): + """ + :returns: The library that contains this entity + """ + return self._library_name + + @property + def source_file(self): + """ + :returns: The source file that contains this entity + """ + return self._source_file diff --git a/vunit/ui/library.py b/vunit/ui/library.py index 7ccf625c9..e5c1e3251 100644 --- a/vunit/ui/library.py +++ b/vunit/ui/library.py @@ -18,7 +18,8 @@ from .common import check_not_empty, get_checked_file_names_from_globs from .source import SourceFile, SourceFileList from .testbench import TestBench -from .packagefacade import PackageFacade +from .package_facade import PackageFacade +from .entity import Entity class Library(object): @@ -261,25 +262,45 @@ def add_source_file( # pylint: disable=too-many-arguments return SourceFile(source_file, self._project, self._parent) - def package(self, name): + def _get_design_unit(self, name): """ - Get a package within the library + Get design unit from name """ library = self._project.get_library(self._library_name) design_unit = library.primary_design_units.get(name) if design_unit is None: raise KeyError(name) + + return design_unit + + def package(self, name): + """ + Get a package within the library + """ + design_unit = self._get_design_unit(name) + if design_unit.unit_type != "package": raise KeyError(name) return PackageFacade(self._parent, self._library_name, name, design_unit) - def entity(self, name): + def get_entity(self, name): """ Get an entity within the library + """ + design_unit = self._get_design_unit(name) + + if design_unit.unit_type != "entity": + raise KeyError(name) + + return Entity(self._library_name, name, design_unit.source_file) + + def entity(self, name): + """ + Get a test bench within this library - :param name: The name of the entity + :param name: The name of the test bench entity :returns: A :class:`.TestBench` object :raises: KeyError """ diff --git a/vunit/ui/packagefacade.py b/vunit/ui/package_facade.py similarity index 75% rename from vunit/ui/packagefacade.py rename to vunit/ui/package_facade.py index 8d675d097..1388b8421 100644 --- a/vunit/ui/packagefacade.py +++ b/vunit/ui/package_facade.py @@ -38,3 +38,24 @@ def generate_codecs(self, codec_package_name=None, used_packages=None, output_fi codec_generator.generate_codecs(self._design_unit, codec_package_name, used_packages, output_file_name) return self._parent.add_source_files(output_file_name, self._library_name) + + @property + def name(self): + """ + :returns: The package name + """ + return self._package_name + + @property + def library(self): + """ + :returns: The library that contains this package + """ + return self._library_name + + @property + def source_file(self): + """ + :returns: The source file that contains this package + """ + return self._design_unit.source_file diff --git a/vunit/vc/__init__.py b/vunit/vc/__init__.py new file mode 100644 index 000000000..c00913267 --- /dev/null +++ b/vunit/vc/__init__.py @@ -0,0 +1,12 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +""" +Code related to VUnit verification components. +""" + +from vunit.vc.verification_component import VerificationComponent +from vunit.vc.verification_component_interface import VerificationComponentInterface diff --git a/vunit/vc/compliance_test.py b/vunit/vc/compliance_test.py new file mode 100644 index 000000000..f4052f2db --- /dev/null +++ b/vunit/vc/compliance_test.py @@ -0,0 +1,124 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +""" +Module for generating a compliance test for a VUnit verification component +""" + +import argparse +import sys +import logging +from pathlib import Path +from vunit.vc import VerificationComponent, VerificationComponentInterface + + +def _create_vc_template(args): + """Creates VC testbench template from args.""" + template_code, vc_name = VerificationComponent.create_vhdl_testbench_template( + args.vc_lib_name, args.vci_lib_name, args.vc_path, args.vci_path + ) + if not template_code or not vc_name: + sys.exit(1) + + if not args.output_path: + output_dir = args.vc_path.parent / ".vc" + if not output_dir.exists(): + output_dir.mkdir(parents=True) + + output_path = output_dir / (f"tb_{vc_name!s}_compliance_template.vhd") + elif args.output_path.is_dir(): + output_path = args.output_path / (f"tb_{vc_name!s}_compliance_template.vhd") + else: + output_path = args.output_path + + output_path.write_text(template_code) + print(f"Open {output_path.resolve()!s} and read the TODOs to complete the template.") + + +def _create_vci_template(args): + """Creates VCI testbench template from args.""" + (template_code, vci_name,) = VerificationComponentInterface.create_vhdl_testbench_template( + args.vci_lib_name, args.vci_path, args.vc_handle_t + ) + if not template_code or not vci_name: + sys.exit(1) + + if not args.output_path: + output_dir = args.vc_path.parent / ".vc" / vci_name + if not output_dir.exists(): + output_dir.mkdir(parents=True) + + output_path = output_dir / (f"tb_{args.vc_handle_t!s}_compliance_template.vhd") + elif args.output_path.is_dir(): + output_dir = args.output_path / vci_name + if not output_dir.exists(): + output_dir.exists(parents=True) + output_path = output_dir / (f"tb_{args.vc_handle_t!s}_compliance_template.vhd") + else: + output_path = args.output_path + + output_path.write_text(template_code) + print(f"Open {output_path.resolve()!s} and read the TODOs to complete the template.") + + +def main(): + """Parses the command line arguments and acts accordingly.""" + + def create_vc_parser(subparsers): + parser = subparsers.add_parser("create-vc", help="Creates a VC compliance test template") + parser.add_argument( + "--vc-lib-name", + help="Name of library hosting the VC (default: vc_lib)", + default="vc_lib", + ) + parser.add_argument( + "--vci-lib-name", + help="Name of library hosting the VCI (default: vc_lib)", + default="vc_lib", + ) + parser.add_argument( + "-o", + "--output-path", + type=Path, + help="Path to the template (default: ./compliance_test/tb__compliance_template.vhd)", + ) + parser.add_argument("vc_path", type=Path, help="Path to file containing the VC entity") + parser.add_argument("vci_path", type=Path, help="Path to file containing the VCI package") + + def create_vci_parser(subparsers): + parser = subparsers.add_parser("create-vci", help="Creates a VCI compliance test template") + parser.add_argument( + "--vci-lib-name", + help="Name of library hosting the VCI (default: vc_lib)", + default="vc_lib", + ) + parser.add_argument( + "-o", + "--output-path", + type=Path, + help="Path to the template (default: ./compliance_test/tb__compliance_template.vhd)", + ) + parser.add_argument("vci_path", type=Path, help="Path to file containing the VCI package") + parser.add_argument("vc_handle_t", help="VC handle type") + + parser = argparse.ArgumentParser(description="Compliance test tool") + subparsers = parser.add_subparsers(dest="subparser_name", help="sub-command help") + + create_vc_parser(subparsers) + create_vci_parser(subparsers) + + args = parser.parse_args(sys.argv[1:]) + + logging.basicConfig(format="%(levelname)s: %(message)s") + + if args.subparser_name == "create-vc": + _create_vc_template(args) + elif args.subparser_name == "create-vci": + _create_vci_template(args) + + +if __name__ == "__main__": + main() diff --git a/vunit/vc/vc_template.py b/vunit/vc/vc_template.py new file mode 100644 index 000000000..f243fec93 --- /dev/null +++ b/vunit/vc/vc_template.py @@ -0,0 +1,134 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +"""Templates for the verification component testbench.""" + +from string import Template + + +TB_TEMPLATE_TEMPLATE = Template( + """\ +-- Read the TODOs to complete this template. + +${context_items} +entity tb_${vc_name}_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_${vc_name}_compliance is + +${constructor} +${signal_declarations} +begin + -- DO NOT modify the test runner process. + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + +${vc_instantiation} +end architecture; +""" +) + +ARCHITECTURE_DECLARATIONS_TEMPLATE = Template( + """\ + constant custom_actor : actor_t := new_actor("vc", inbox_size => 1); + constant custom_logger : logger_t := get_logger("vc"); + constant custom_checker : checker_t := new_checker(get_logger("vc_check")); + + impure function create_handle return ${vc_handle_t} is + variable logger : logger_t := ${default_logger}; + variable actor : actor_t := ${default_actor}; + variable checker : checker_t := ${default_checker}; + begin + if use_custom_logger then + logger := custom_logger; + end if; + + if use_custom_actor then + actor := custom_actor; + end if; + + if use_custom_checker then + checker := custom_checker; + end if; + + return ${vc_constructor_name}( + ${specified_parameters} + logger => logger, + actor => actor, + checker => checker, + unexpected_msg_type_policy => unexpected_msg_type_policy); + end; + + constant ${vc_handle_name} : ${vc_handle_t} := create_handle; + constant unexpected_msg : msg_type_t := new_msg_type("unexpected msg"); +""" +) + +TEST_RUNNER_TEMPLATE = Template( + """\ +test_runner : process + variable t_start : time; + variable msg : msg_t; + variable error_logger : logger_t; +begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + + if run("Test that sync interface is supported") then + t_start := now; + wait_for_time(net, as_sync(${vc_handle_name}), 1 ns); + wait_for_time(net, as_sync(${vc_handle_name}), 2 ns); + wait_for_time(net, as_sync(${vc_handle_name}), 3 ns); + check_equal(now - t_start, 0 ns); + t_start := now; + wait_until_idle(net, as_sync(${vc_handle_name})); + check_equal(now - t_start, 6 ns); + + elsif run("Test that the actor can be customised") then + t_start := now; + wait_for_time(net, as_sync(${vc_handle_name}), 1 ns); + wait_for_time(net, as_sync(${vc_handle_name}), 2 ns); + check_equal(now - t_start, 0 ns); + wait_for_time(net, as_sync(${vc_handle_name}), 3 ns); + check_equal(now - t_start, 1 ns); + wait_until_idle(net, as_sync(${vc_handle_name})); + check_equal(now - t_start, 6 ns); + + elsif run("Test unexpected message handling") then + if use_custom_checker then + error_logger := get_logger(custom_checker); + else + error_logger := custom_logger; + end if; + mock(error_logger, failure); + msg := new_msg(unexpected_msg); + send(net, custom_actor, msg); + wait for 1 ns; + if unexpected_msg_type_policy = fail then + check_only_log(error_logger, "Got unexpected message unexpected msg", failure); + else + check_no_log; + end if; + unmock(error_logger); + end if; + + end loop; + + test_runner_cleanup(runner); +end process test_runner;""" +) + +GENERICS_TEMPLATE = """use_custom_logger : boolean := false; + use_custom_checker : boolean := false; + use_custom_actor : boolean := false; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + runner_cfg : string""" diff --git a/vunit/vc/vci_template.py b/vunit/vc/vci_template.py new file mode 100644 index 000000000..0c78e061f --- /dev/null +++ b/vunit/vc/vci_template.py @@ -0,0 +1,153 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +"""Templates for the verification component interface testbench""" + +from string import Template + +TB_TEMPLATE_TEMPLATE = Template( + """\ +-- Read the TODOs to complete this template. + +${context_items} +entity tb_${vc_handle_t}_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_${vc_handle_t}_compliance is +begin + test_runner : process + -- TODO: Specify a value for all listed constants. +${constant_declarations} + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" +) + +TB_EPILOGUE_TEMPLATE = Template( + """ + constant actor1 : actor_t := new_actor("actor1"); + constant logger1 : logger_t := get_logger("logger1"); + constant checker_logger1 : logger_t := get_logger("checker1"); + constant checker1 : checker_t := new_checker(checker_logger1); + + constant actor2 : actor_t := new_actor("actor2"); + constant logger2 : logger_t := get_logger("logger2"); + constant checker_logger2 : logger_t := get_logger("checker2"); + constant checker2 : checker_t := new_checker(checker_logger2); + + constant actor3 : actor_t := new_actor("actor3"); + constant logger3 : logger_t := get_logger("logger3"); + constant checker_logger3 : logger_t := get_logger("checker3"); + constant checker3 : checker_t := new_checker(checker_logger3); + + constant actor4 : actor_t := new_actor("actor4"); + constant logger4 : logger_t := get_logger("logger4"); + constant checker_logger4 : logger_t := get_logger("checker4"); + constant checker4 : checker_t := new_checker(checker_logger4); + + constant actor5 : actor_t := new_actor("actor5"); + constant logger5 : logger_t := get_logger("logger5"); + constant checker_logger5 : logger_t := get_logger("checker5"); + constant checker5 : checker_t := new_checker(checker_logger5); + + constant actor6 : actor_t := new_actor("actor6"); + constant logger6 : logger_t := get_logger("logger6"); + constant checker_logger6 : logger_t := get_logger("checker6"); + constant checker6 : checker_t := new_checker(checker_logger6); + + constant actor7 : actor_t := new_actor("actor7"); + constant logger7 : logger_t := get_logger("logger7"); + constant checker_logger7 : logger_t := get_logger("checker7"); + constant checker7 : checker_t := new_checker(checker_logger7); + +${handle1} +${handle2} +${handle3} +${handle4} +${handle5} +${handle6} +${handle7} + + variable std_cfg1, std_cfg2, std_cfg3, std_cfg4 : std_cfg_t; + variable std_cfg5, std_cfg6, std_cfg7 : std_cfg_t; + begin + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("Test standard configuration") then + std_cfg1 := get_std_cfg(handle1); + + check(get_actor(std_cfg1) = actor1, "Failed to configure actor with ${vc_constructor_name}"); + check(get_logger(std_cfg1) = logger1, "Failed to configure logger with ${vc_constructor_name}"); + check(get_checker(std_cfg1) = checker1, "Failed to configure checker with ${vc_constructor_name}"); + check(unexpected_msg_type_policy(std_cfg1) = fail, + "Failed to configure unexpected_msg_type_policy = fail with ${vc_constructor_name}"); + + std_cfg2 := get_std_cfg(handle2); + + check(unexpected_msg_type_policy(std_cfg2) = ignore, + "Failed to configure unexpected_msg_type_policy = ignore with ${vc_constructor_name}"); + + elsif run("Test handle independence") then + std_cfg1 := get_std_cfg(handle1); + std_cfg2 := get_std_cfg(handle2); + check(get_actor(std_cfg1) /= get_actor(std_cfg2), + "Actor shared between handles created by ${vc_constructor_name}"); + check(get_logger(std_cfg1) /= get_logger(std_cfg2), + "Logger shared between handles created by ${vc_constructor_name}"); + check(get_checker(std_cfg1) /= get_checker(std_cfg2), + "Checker shared between handles created by ${vc_constructor_name}"); + check(unexpected_msg_type_policy(std_cfg1) /= unexpected_msg_type_policy(std_cfg2), + "unexpected_msg_type_policy shared between handles created by ${vc_constructor_name}"); + + elsif run("Test default logger") then + std_cfg3 := get_std_cfg(handle3); + check(get_logger(std_cfg3) /= null_logger, + "No valid default logger (null_logger) created by ${vc_constructor_name}"); + check(get_logger(std_cfg3) /= default_logger, + "No valid default logger (default_logger) created by ${vc_constructor_name}"); + + std_cfg4 := get_std_cfg(handle4); + check(get_logger(std_cfg4) /= null_logger, + "No valid default logger (null_logger) created by ${vc_constructor_name}"); + check(get_logger(std_cfg4) /= default_logger, + "No valid default logger (default_logger) created by ${vc_constructor_name}"); + + elsif run("Test default checker") then + std_cfg5 := get_std_cfg(handle5); + check(get_checker(std_cfg5) /= null_checker, + "No valid default checker (null_checker) created by ${vc_constructor_name}"); + check(get_checker(std_cfg5) /= default_checker, + "No valid default checker (default_checker) created by ${vc_constructor_name}"); + + std_cfg6 := get_std_cfg(handle6); + check(get_checker(std_cfg6) /= null_checker, + "No valid default checker (null_checker) created by ${vc_constructor_name}"); + check(get_checker(std_cfg6) /= default_checker, + "No valid default checker (default_checker) created by ${vc_constructor_name}"); + + std_cfg7 := get_std_cfg(handle7); + check(get_checker(std_cfg7) /= null_checker, + "No valid default checker (null_checker) created by ${vc_constructor_name}"); + check(get_checker(std_cfg7) /= default_checker, + "No valid default checker (default_checker) created by ${vc_constructor_name}"); + check(get_logger(get_checker(std_cfg7)) = logger6, + "Default checker not based on logger provided to ${vc_constructor_name}"); + + end if; + end loop; + + test_runner_cleanup(runner); + end process test_runner; +end architecture; +""" +) diff --git a/vunit/vc/verification_component.py b/vunit/vc/verification_component.py new file mode 100644 index 000000000..4197a2aac --- /dev/null +++ b/vunit/vc/verification_component.py @@ -0,0 +1,399 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +"""VerificationComponent class.""" + +import sys +import re +from pathlib import Path +from re import subn, MULTILINE, IGNORECASE, DOTALL +from vunit.vc.vc_template import ( + TB_TEMPLATE_TEMPLATE, + ARCHITECTURE_DECLARATIONS_TEMPLATE, + TEST_RUNNER_TEMPLATE, + GENERICS_TEMPLATE, +) +from vunit.vhdl_parser import ( + VHDLDesignFile, + find_closing_delimiter, + remove_comments, +) +from vunit.vc.verification_component_interface import ( + VerificationComponentInterface, + create_context_items, + LOGGER, +) + + +class VerificationComponent: + """Represents a Verification Component (VC).""" + + @classmethod + def find(cls, vc_lib, vc_name, vci): + """Finds the specified VC if present. + + :param vc_lib: Library object containing the VC. + :param vc_name: Name of VC entity. + :param vci: A VerificationComponentInterface object representing the VCI used by the VC. + + :returns: A VerificationComponent object. + """ + + if not vci: + LOGGER.error("No VCI provided") + sys.exit(1) + + try: + vc_facade = vc_lib.get_entity(vc_name) + except KeyError: + LOGGER.error("Failed to find VC %s", vc_name) + sys.exit(1) + + vc_code = cls.validate(vc_facade.source_file.name) + if not vc_code: + sys.exit(1) + + vc_entity = vc_code.entities[0] + vc_handle_t = vc_entity.generics[0].subtype_indication.type_mark + + if vc_handle_t != vci.vc_constructor.return_type_mark: + LOGGER.error( + "VC handle (%s) doesn't match that of the VCI (%s)", + vc_handle_t, + vci.vc_constructor.return_type_mark, + ) + sys.exit(1) + + return cls(vc_facade, vc_code, vc_entity, vc_handle_t, vci) + + def __init__(self, vc_facade, vc_code, vc_entity, vc_handle_t, vci): + self.vc_facade = vc_facade + self.vc_code = vc_code + self.vc_entity = vc_entity + self.vc_handle_t = vc_handle_t + self.vci = vci + + @staticmethod + def validate(vc_path): + """Validates the existence and contents of the verification component.""" + vc_path = Path(vc_path) + with vc_path.open("r", encoding="utf-8") as fptr: + vc_code = VHDLDesignFile.parse(fptr.read()) + + if len(vc_code.entities) != 1: + LOGGER.error("%s must contain a single VC entity", vc_path) + return None + + vc_entity = vc_code.entities[0] + + if not ((len(vc_entity.generics) == 1) and (len(vc_entity.generics[0].identifier_list) == 1)): + LOGGER.error("%s must have a single generic", vc_entity.identifier) + return None + + return vc_code + + @staticmethod + def create_vhdl_testbench_template(vc_lib_name, vci_lib_name, vc_path, vci_path): + """ + Creates a template for a VC compliance testbench. + + :param vc_lib_name: Name of the library containing the verification component. + :param vci_lib_name: Name of the library containing the verification component interface. + :param vc_path: Path to the file containing the verification component entity. + :param vci_path: Path to the file containing the verification component interface package. + + :returns: The template string and the name of the verification component entity. + """ + vc_path = Path(vc_path).resolve() + vci_path = Path(vci_path).resolve() + + def create_constructor(vc_entity, vc_handle_t, vc_constructor): + unspecified_parameters = [ + parameter.identifier_list for parameter in vc_constructor.parameter_list if not parameter.init_value + ] + + constant = ( + f"{vc_entity.generics[0].identifier_list[0]!s} : {vc_handle_t!s} := {vc_constructor.identifier!s}" + ) + + if not unspecified_parameters: + return f" constant {constant!s};\n" + return ( + " -- TODO: Specify a value for all listed parameters. Keep all parameters on separate lines\n" + + f" constant {constant!s}(\n" + + "".join( + [ + f" {parameter!s} => ,\n" + for parameter in unspecified_parameters + if parameter + not in [ + "actor", + "logger", + "checker", + "unexpected_msg_type_policy", + ] + ] + ) + )[:-2] + "\n );\n" + + def create_signal_declarations_and_vc_instantiation(vc_entity, vc_lib_name): + signal_declarations = ( + " -- TODO: Constrain any unconstrained signal connecting to the DUT.\n" if vc_entity.ports else "" + ) + port_mappings = "" + for port in vc_entity.ports: + if (port.mode != "out") and port.init_value: + port_mappings += "".join( + [f" {identifier!s} => open,\n" for identifier in port.identifier_list] + ) + else: + signal_declarations += ( + f" signal {', '.join(port.identifier_list)!s} : {port.subtype_indication!s};\n" + ) + port_mappings += "".join( + [f" {identifier!s} => {identifier!s},\n" for identifier in port.identifier_list] + ) + + return ( + signal_declarations, + f""" -- DO NOT modify the VC instantiation. + vc_inst: entity {vc_lib_name!s}.{vc_entity.identifier!s} + generic map({vc_entity.generics[0].identifier_list[0]!s})""" + + (("\n port map(\n" + port_mappings[:-2] + "\n );\n") if len(vc_entity.ports) > 0 else ";\n"), + ) + + vc_code = VerificationComponent.validate(vc_path) + vc_entity = vc_code.entities[0] + vc_handle_t = vc_entity.generics[0].subtype_indication.type_mark + vci_code, vc_constructor = VerificationComponentInterface.validate(vci_path, vc_handle_t) + if (not vci_code) or (not vc_constructor): + return None, None + + ( + signal_declarations, + vc_instantiation, + ) = create_signal_declarations_and_vc_instantiation(vc_entity, vc_lib_name) + + return ( + TB_TEMPLATE_TEMPLATE.substitute( + context_items=create_context_items( + vc_code, + vc_lib_name, + initial_library_names=set(["std", "work", "vunit_lib", vc_lib_name]), + initial_context_refs=set(["vunit_lib.vunit_context", "vunit_lib.com_context"]), + initial_package_refs=set( + [ + "vunit_lib.vc_pkg.all", + "vunit_lib.sync_pkg.all", + f"{vci_lib_name!s}.{vci_code.packages[0].identifier!s}.all", + ] + ), + ), + vc_name=vc_entity.identifier, + constructor=create_constructor(vc_entity, vc_handle_t, vc_constructor), + signal_declarations=signal_declarations, + vc_instantiation=vc_instantiation, + ), + vc_entity.identifier, + ) + + def create_vhdl_testbench(self, template_path=None): + """ + Creates a VHDL VC compliance testbench. + + :param template_path: Path to template file. If None, a default template is assumed. + + :returns: The testbench code as a string. + """ + template_path = Path(template_path).resolve() if template_path is not None else None + + def update_architecture_declarations(code): + releft = rf"\bconstant\s+{self.vc_entity.generics[0].identifier_list[0]}\s*:\s*{self.vc_handle_t}\s*" + constructor_call_start = re.compile( + rf"{releft!s}:=\s*{self.vci.vc_constructor.identifier!s}", + MULTILINE | IGNORECASE | DOTALL, + ).search(code) + + if not constructor_call_start: + raise RuntimeError( + f"Failed to find call to {self.vci.vc_constructor.identifier!s} in template_path {template_path!s}" + ) + + parameter_start = re.compile(r"\s*\(", MULTILINE | IGNORECASE | DOTALL).match( + code[constructor_call_start.end() :] + ) + + if parameter_start: + closing_parenthesis_pos = find_closing_delimiter( + "\\(", + "\\)", + code[constructor_call_start.end() + parameter_start.end() :], + ) + + specified_parameters = ( + code[ + constructor_call_start.end() + + parameter_start.end() : constructor_call_start.end() + + parameter_start.end() + + closing_parenthesis_pos + - 1 + ].strip() + + "," + ) + + else: + specified_parameters = "" + + _constructor_call_end_re = re.compile(r"\s*;", MULTILINE | IGNORECASE | DOTALL) + if parameter_start: + search_start = constructor_call_start.end() + parameter_start.end() + closing_parenthesis_pos + constructor_call_end_match = _constructor_call_end_re.match(code[search_start:]) + else: + search_start = constructor_call_start.end() + constructor_call_end_match = _constructor_call_end_re.match(code[search_start:]) + + if not constructor_call_end_match: + identifier = self.vci.vc_constructor.identifier + raise RuntimeError(f"Missing trailing semicolon for {identifier!s} in template_path {template_path!s}") + + constructor_call_end = search_start + constructor_call_end_match.end() + + default_values = {} + for parameter in self.vci.vc_constructor.parameter_list: + for identifier in parameter.identifier_list: + default_values[identifier] = parameter.init_value + + return ( + code[: constructor_call_start.start()] + + ARCHITECTURE_DECLARATIONS_TEMPLATE.substitute( + vc_handle_t=self.vc_handle_t, + vc_constructor_name=self.vci.vc_constructor.identifier, + specified_parameters=specified_parameters, + vc_handle_name=self.vc_entity.generics[0].identifier_list[0], + default_logger=default_values["logger"] if default_values["logger"] else 'get_logger("vc_logger")', + default_actor=default_values["actor"] if default_values["actor"] else 'new_actor("vc_actor")', + default_checker=default_values["checker"] + if default_values["checker"] + else 'new_checker("vc_checker")', + ) + + code[constructor_call_end:] + ) + + def update_test_runner(code): + code, num_found_test_runners = subn( + re.compile( + r"\btest_runner\s*:\s*process.*?end\s+process\s+test_runner\s*;", + # r"\btest_runner\s*:\s*process", + MULTILINE | IGNORECASE | DOTALL, + ), + TEST_RUNNER_TEMPLATE.substitute(vc_handle_name=self.vc_entity.generics[0].identifier_list[0]), + code, + 1, + ) + if not num_found_test_runners: + raise RuntimeError(f"Failed to find test runner in template_path {template_path!s}") + return code + + def update_generics(code): + code, num_found_runner_cfg = subn( + re.compile(r"\brunner_cfg\s*:\s*string", MULTILINE | IGNORECASE | DOTALL), GENERICS_TEMPLATE, code, 1 + ) + if not num_found_runner_cfg: + raise RuntimeError(f"Failed to find runner_cfg generic in template_path {template_path!s}") + return code + + if template_path: + template_code = template_path.read_text(encoding="utf-8").lower() + else: + template_code, _ = self.create_vhdl_testbench_template( + self.vc_facade.library, + self.vci.vci_facade.library, + self.vc_facade.source_file.name, + self.vci.vci_facade.source_file.name, + ) + if not template_code: + return None + template_code = template_code.lower() + + design_file = VHDLDesignFile.parse(template_code) + if design_file.entities[0].identifier != f"tb_{self.vc_facade.name!s}_compliance": + raise RuntimeError(f"{template_path!s} is not a template_path for {self.vc_facade.name!s}") + + tb_code = update_architecture_declarations(template_code) + tb_code = update_test_runner(tb_code) + tb_code = update_generics(tb_code) + + return remove_comments(tb_code) + + def add_vhdl_testbench(self, vc_test_lib, test_dir, template_path=None): + """ + Adds a VHDL compliance testbench + + :param vc_test_lib: The name of the library to which the testbench is added. + :param test_dir: The name of the directory where the testbench file is stored. + :param template_path: Path to testbench template file. If None, a default template is used. + + :returns: The :class:`.SourceFile` for the added testbench. + + :example: + + .. code-block:: python + + ROOT = Path(__file__).parent() + prj.add_vhdl_testbench("test_lib", ROOT / "test", ROOT / ".vc" /"vc_template.vhd") + + """ + test_dir = Path(test_dir).resolve() + template_path = Path(template_path) if template_path is not None else None + + try: + vc_test_lib.test_bench(f"tb_{self.vc_entity.identifier!s}_compliance") + raise RuntimeError(f"tb_{self.vc_entity.identifier!s}_compliance already exists in {vc_test_lib.name!s}") + except KeyError: + pass + + if not test_dir.exists(): + test_dir.mkdir(parents=True) + + tb_path = test_dir / (f"tb_{self.vc_entity.identifier!s}_compliance.vhd") + testbench_code = self.create_vhdl_testbench(template_path) + if not testbench_code: + return None + tb_path.write_text(testbench_code) + + tb_file = vc_test_lib.add_source_file(str(tb_path)) + testbench = vc_test_lib.test_bench(f"tb_{self.vc_entity.identifier!s}_compliance") + test = testbench.test("Test that the actor can be customised") + test.set_generic("use_custom_actor", True) + + test = testbench.test("Test unexpected message handling") + test.add_config( + name="accept_unexpected_msg_type", + generics=dict( + unexpected_msg_type_policy="ignore", + use_custom_logger=True, + use_custom_actor=True, + ), + ) + test.add_config( + name="fail_unexpected_msg_type_with_null_checker", + generics=dict( + unexpected_msg_type_policy="fail", + use_custom_logger=True, + use_custom_actor=True, + ), + ) + test.add_config( + name="fail_unexpected_msg_type_with_custom_checker", + generics=dict( + unexpected_msg_type_policy="fail", + use_custom_logger=True, + use_custom_checker=True, + use_custom_actor=True, + ), + ) + + return tb_file diff --git a/vunit/vc/verification_component_interface.py b/vunit/vc/verification_component_interface.py new file mode 100644 index 000000000..0c2f09980 --- /dev/null +++ b/vunit/vc/verification_component_interface.py @@ -0,0 +1,453 @@ +# 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +"""VerificationComponentInterface class.""" + +import sys +import re +import logging +from pathlib import Path +from vunit.vc.vci_template import TB_TEMPLATE_TEMPLATE, TB_EPILOGUE_TEMPLATE +from vunit.vhdl_parser import ( + VHDLDesignFile, + VHDLFunctionSpecification, + remove_comments, + VHDLRecordType, +) + +LOGGER = logging.getLogger(__name__) + + +def create_context_items(code, lib_name, initial_library_names, initial_context_refs, initial_package_refs): + """Creates a VHDL snippet with context items found in the provided code and the initial_ arguments.""" + for ref in code.references: + if ref.is_package_reference() or ref.is_context_reference(): + initial_library_names.add(ref.library_name) + + library_name = ref.library_name if ref.library_name != "work" else lib_name + + if ref.is_context_reference(): + initial_context_refs.add(f"{library_name!s}.{ref.design_unit_name!s}") + + if ref.is_package_reference(): + initial_package_refs.add(f"{library_name!s}.{ref.design_unit_name!s}.{ref.name_within!s}") + + context_items = "" + for library in sorted(initial_library_names): + if library not in ["std", "work"]: + context_items += f"library {library!s};\n" + + for context_ref in sorted(initial_context_refs): + context_items += f"context {context_ref!s};\n" + + for package_ref in sorted(initial_package_refs): + context_items += f"use {package_ref!s};\n" + + return context_items + + +class VerificationComponentInterface: + """Represents a Verification Component Interface (VCI).""" + + @classmethod + def find(cls, vci_lib, vci_name, vc_handle_t): + """Finds the specified VCI if present. + + :param vci_lib: Library object containing the VCI. + :param vci_name: Name of VCI package. + :param vc_handle_t: Name of VC handle type. + + :returns: A VerificationComponentInterface object. + """ + + try: + vci_facade = vci_lib.package(vci_name) + except KeyError: + LOGGER.error("Failed to find VCI %s", vci_name) + sys.exit(1) + + _, vc_constructor = cls.validate(vci_facade.source_file.name, vc_handle_t) + if not vc_constructor: + sys.exit(1) + + return cls(vci_facade, vc_constructor) + + @classmethod + def validate(cls, vci_path, vc_handle_t): + """Validates the existence and contents of the verification component interface.""" + vci_path = Path(vci_path) + + with vci_path.open("r", encoding="utf-8") as fptr: + code = remove_comments(fptr.read()) + vci_code = VHDLDesignFile.parse(code) + if len(vci_code.packages) != 1: + LOGGER.error("%s must contain a single VCI package", vci_path) + return None, None + + if not cls._validate_as_sync(code, vc_handle_t): + LOGGER.error("%s must provide an as_sync function", vci_path) + return None, None + + vc_constructor = cls._validate_constructor(code, vc_handle_t) + if not cls._validate_handle(code, vc_handle_t): + vc_constructor = None + + return vci_code, vc_constructor + + def __init__(self, vci_facade, vc_constructor): + self.vci_facade = vci_facade + self.vc_constructor = vc_constructor + + @staticmethod + def _validate_as_sync(code, vc_handle_t): + """Validates the existence and format of the as_sync function.""" + + for func in VHDLFunctionSpecification.find(code): + if not func.identifier.startswith("as_sync"): + continue + + if func.return_type_mark != "sync_handle_t": + continue + + if len(func.parameter_list) != 1: + continue + + if len(func.parameter_list[0].identifier_list) != 1: + continue + + if func.parameter_list[0].subtype_indication.type_mark != vc_handle_t: + continue + + return func + + return None + + @staticmethod + def _validate_constructor(code, vc_handle_t): + """Validates the existence and format of the verification component constructor.""" + + def create_messages(required_parameter_types, expected_default_value): + messages = [ + "Failed to find a constructor function%s for %s starting with new_", + "Found constructor function %s but not with the correct return type %s", + ] + + for parameter_name, parameter_type in required_parameter_types.items(): + messages += [ + f"Found constructor function %s for %s but the {parameter_name!s} parameter is missing", + ( + f"Found constructor function %s for %s but the {parameter_name} " + f"parameter is not of type {parameter_type}" + ), + f"Found constructor function %s for %s but {parameter_name} is lacking a default value", + ( + f"Found constructor function %s for %s but {expected_default_value[parameter_name]} " + f"is the only allowed default value for the {parameter_name} parameter" + ), + ] + + return messages + + def log_error_message(function_score, messages): + high_score = 0 + best_function = "" + for function_name, score in function_score.items(): + if score > high_score: + high_score = score + best_function = function_name + + error_msg = messages[high_score] % (best_function, vc_handle_t) + LOGGER.error(error_msg) + + required_parameter_types = dict( + logger="logger_t", + actor="actor_t", + checker="checker_t", + unexpected_msg_type_policy="unexpected_msg_type_policy_t", + ) + + expected_default_value = dict( + logger="null_logger", + actor="null_actor", + checker="null_checker", + unexpected_msg_type_policy=None, + ) + + messages = create_messages(required_parameter_types, expected_default_value) + + function_score = {} + for func in VHDLFunctionSpecification.find(code): + function_score[func.identifier] = 0 + + if not func.identifier.startswith("new_"): + continue + function_score[func.identifier] += 1 + + if func.return_type_mark != vc_handle_t: + continue + function_score[func.identifier] += 1 + + parameters = {} + for parameter in func.parameter_list: + for identifier in parameter.identifier_list: + parameters[identifier] = parameter + + for parameter_name, parameter_type in required_parameter_types.items(): + if parameter_name not in parameters: + break + function_score[func.identifier] += 1 + + if parameters[parameter_name].subtype_indication.type_mark != parameter_type: + break + function_score[func.identifier] += 1 + + if not parameters[parameter_name].init_value: + break + function_score[func.identifier] += 1 + + if expected_default_value[parameter_name] and ( + parameters[parameter_name].init_value != expected_default_value[parameter_name] + ): + break + function_score[func.identifier] += 1 + + if function_score[func.identifier] == len(messages): + return func + + log_error_message(function_score, messages) + + return None + + @staticmethod + def _validate_handle(code, vc_handle_t): + """Validates the existence and format of the verification component handle type.""" + + handle_is_valid = True + for record in VHDLRecordType.find(code): + if record.identifier == vc_handle_t: + for element in record.elements: + for parameter_name in element.identifier_list: + if not parameter_name.lower().startswith("p_"): + handle_is_valid = False + LOGGER.error( + "%s in %s doesn't start with p_", + parameter_name, + vc_handle_t, + ) + return handle_is_valid + + LOGGER.error( + "Failed to find %s record", + vc_handle_t, + ) + return False + + @classmethod + def create_vhdl_testbench_template(cls, vci_lib_name, vci_path, vc_handle_t): + """ + Creates a template for a VCI compliance testbench. + + :param vc_lib_name: Name of the library containing the verification component interface. + :param vci_path: Path to the file containing the verification component interface package. + :param vc_handle_t: Name of the VC handle type returned by the VC constructor. + + :returns: The template code as a string and the name of the verification component entity. + """ + vci_path = Path(vci_path) + vci_code, vc_constructor = cls.validate(vci_path, vc_handle_t) + + context_items = create_context_items( + vci_code, + vci_lib_name, + initial_library_names=set(["std", "work", "vunit_lib", vci_lib_name]), + initial_context_refs=set(["vunit_lib.vunit_context", "vunit_lib.com_context"]), + initial_package_refs=set( + [ + "vunit_lib.vc_pkg.all", + f"{vci_lib_name!s}.{vci_code.packages[0].identifier!s}.all", + ] + ), + ) + + unspecified_parameters = [parameter for parameter in vc_constructor.parameter_list if not parameter.init_value] + if unspecified_parameters: + constant_declarations = "" + for parameter in unspecified_parameters: + for identifier in parameter.identifier_list: + if identifier in [ + "actor", + "logger", + "checker", + "unexpected_msg_type_policy", + ]: + continue + constant_declarations += ( + f" constant {identifier!s} : {parameter.subtype_indication.type_mark!s} := ;\n" + ) + else: + constant_declarations = "\n" + + template_code = TB_TEMPLATE_TEMPLATE.substitute( + context_items=context_items, + vc_handle_t=vc_handle_t, + constant_declarations=constant_declarations, + ) + + return ( + template_code, + vci_code.packages[0].identifier, + ) + + def create_vhdl_testbench(self, template_path=None): + """ + Creates a VHDL VCI compliance testbench. + + :param template_path: Path to template file. If None, a default template is assumed. + + :returns: The testbench code as a string. + """ + template_path = Path(template_path) if template_path is not None else None + + if not template_path: + template_code, _ = self.create_vhdl_testbench_template( + self.vci_facade.library, + self.vci_facade.source_file.name, + self.vc_constructor.return_type_mark, + ) + else: + with template_path.open("r", encoding="utf-8") as fptr: + template_code = fptr.read() + + test_runner_body_pattern = re.compile(r"\s+-- DO NOT modify this line and the lines below.") + match = test_runner_body_pattern.search(template_code) + if not match: + LOGGER.error("Failed to find body of test_runner in template code.") + return None + + unspecified_parameters = [ + parameter for parameter in self.vc_constructor.parameter_list if not parameter.init_value + ] + + def create_handle_assignment( + handle_name, + actor=None, + logger=None, + checker=None, + unexpected_msg_type_policy="fail", + ): + mark = self.vc_constructor.return_type_mark + handle_assignment = f" constant {handle_name!s} : {mark!s} := {self.vc_constructor.identifier!s}(\n" + for parameter in unspecified_parameters: + for identifier in parameter.identifier_list: + if identifier in [ + "actor", + "logger", + "checker", + "unexpected_msg_type_policy", + ]: + continue + handle_assignment += f" {identifier!s} => {identifier!s},\n" + for formal, actual in dict( + actor=actor, + logger=logger, + checker=checker, + unexpected_msg_type_policy=unexpected_msg_type_policy, + ).items(): + if actual: + handle_assignment += f" {formal!s} => {actual!s},\n" + + handle_assignment = handle_assignment[:-2] + "\n );" + + return handle_assignment + + testbench_code = template_code[: match.start()] + TB_EPILOGUE_TEMPLATE.substitute( + vc_handle_t=self.vc_constructor.return_type_mark, + vc_constructor_name=self.vc_constructor.identifier, + handle1=create_handle_assignment( + "handle1", + actor="actor1", + logger="logger1", + checker="checker1", + unexpected_msg_type_policy="fail", + ), + handle2=create_handle_assignment( + "handle2", + actor="actor2", + logger="logger2", + checker="checker2", + unexpected_msg_type_policy="ignore", + ), + handle3=create_handle_assignment( + "handle3", + actor="actor3", + checker="checker3", + unexpected_msg_type_policy="fail", + ), + handle4=create_handle_assignment( + "handle4", + actor="actor4", + logger="null_logger", + checker="checker4", + unexpected_msg_type_policy="fail", + ), + handle5=create_handle_assignment( + "handle5", + actor="actor5", + unexpected_msg_type_policy="fail", + ), + handle6=create_handle_assignment( + "handle6", + actor="actor6", + checker="null_checker", + unexpected_msg_type_policy="fail", + ), + handle7=create_handle_assignment( + "handle7", + actor="actor7", + logger="logger6", + unexpected_msg_type_policy="fail", + ), + ) + + return testbench_code + + def add_vhdl_testbench(self, vci_test_lib, test_dir, template_path=None): + """ + Adds a VHDL compliance testbench + + :param vci_test_lib: The name of the library to which the testbench is added. + :param test_dir: The name of the directory where the testbench file is stored. + :param template_path: Path to testbench template file. If None, a default template is used. + + :returns: The :class:`.SourceFile` for the added testbench. + + :example: + + .. code-block:: python + + ROOT = Path(__file__).parent + prj.add_vhdl_testbench("test_lib", ROOT / "test", ROOT / ".vc" / "vc_template.vhd") + + """ + template_path = Path(template_path).resolve() if template_path is not None else None + test_dir = Path(test_dir).resolve() + + try: + vci_test_lib.test_bench(f"tb_{self.vci_facade.name!s}_{self.vc_constructor.return_type_mark!s}_compliance") + raise RuntimeError(f"tb_{self.vci_facade.name!s}_compliance already exists in {vci_test_lib.name!s}") + except KeyError: + pass + + if not test_dir.exists(): + test_dir.mkdir(parents=True) + + tb_path = test_dir / (f"tb_{self.vci_facade.name!s}_{self.vc_constructor.return_type_mark!s}_compliance.vhd") + testbench_code = self.create_vhdl_testbench(template_path) + if not testbench_code: + return None + tb_path.write_text(testbench_code) + + return vci_test_lib.add_source_file(tb_path) diff --git a/vunit/vhdl/com/src/com_types.vhd b/vunit/vhdl/com/src/com_types.vhd index a2a4aca42..14087d691 100644 --- a/vunit/vhdl/com/src/com_types.vhd +++ b/vunit/vhdl/com/src/com_types.vhd @@ -20,6 +20,7 @@ use work.integer_vector_ptr_pkg.all; use work.integer_array_pkg.all; use work.string_ptr_pkg.all; use work.logger_pkg.all; +use work.checker_pkg.all; use work.queue_pkg.all; use work.queue_2008p_pkg.all; use work.queue_pool_pkg.all; @@ -188,6 +189,9 @@ package com_types_pkg is procedure unexpected_msg_type(msg_type : msg_type_t; logger : logger_t := com_logger); + procedure unexpected_msg_type(msg_type : msg_type_t; + checker : checker_t); + procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger); alias push is push_msg_type [msg_t, msg_type_t, logger_t]; @@ -445,6 +449,12 @@ package body com_types_pkg is end if; end procedure; + procedure unexpected_msg_type(msg_type : msg_type_t; + checker : checker_t) is + begin + unexpected_msg_type(msg_type, get_logger(checker)); + end; + procedure push_msg_type(msg : msg_t; msg_type : msg_type_t; logger : logger_t := com_logger) is begin push(msg, msg_type.p_code); diff --git a/vunit/vhdl/logging/src/logger_pkg.vhd b/vunit/vhdl/logging/src/logger_pkg.vhd index 423262924..cbdb8291a 100644 --- a/vunit/vhdl/logging/src/logger_pkg.vhd +++ b/vunit/vhdl/logging/src/logger_pkg.vhd @@ -406,4 +406,7 @@ package logger_pkg is -- Return the number of unchecked messages in the mock queue impure function mock_queue_length return natural; + -- Return an integer reference to the logger + impure function to_integer(logger : logger_t) return integer; + end package; diff --git a/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_master_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_master_t_compliance_template.vhd new file mode 100644 index 000000000..c320f7537 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_master_t_compliance_template.vhd @@ -0,0 +1,36 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.avalon_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_master_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_master_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + constant address_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_slave_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_slave_t_compliance_template.vhd new file mode 100644 index 000000000..365ac9319 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/avalon_pkg/tb_avalon_slave_t_compliance_template.vhd @@ -0,0 +1,36 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.avalon_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_slave_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_slave_t_compliance is +begin + test_runner : process + constant memory : memory_t := new_memory; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_sink_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_sink_t_compliance_template.vhd new file mode 100644 index 000000000..bbfa43824 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_sink_t_compliance_template.vhd @@ -0,0 +1,36 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.avalon_stream_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_sink_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_sink_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_source_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_source_t_compliance_template.vhd new file mode 100644 index 000000000..da07aaddd --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/avalon_stream_pkg/tb_avalon_source_t_compliance_template.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.avalon_stream_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_source_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_source_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/axi_slave_pkg/tb_axi_slave_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/axi_slave_pkg/tb_axi_slave_t_compliance_template.vhd new file mode 100644 index 000000000..e54061f29 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/axi_slave_pkg/tb_axi_slave_t_compliance_template.vhd @@ -0,0 +1,36 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_slave_pkg.all; +use vunit_lib.axi_statistics_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_slave_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_slave_t_compliance is +begin + test_runner : process + constant memory : memory_t := new_memory; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_master_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_master_t_compliance_template.vhd new file mode 100644 index 000000000..14d57ce7a --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_master_t_compliance_template.vhd @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_master_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_master_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_monitor_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_monitor_t_compliance_template.vhd new file mode 100644 index 000000000..1608a5b22 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_monitor_t_compliance_template.vhd @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_monitor_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_monitor_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_protocol_checker_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_protocol_checker_t_compliance_template.vhd new file mode 100644 index 000000000..1c929e7e3 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_protocol_checker_t_compliance_template.vhd @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_protocol_checker_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_protocol_checker_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_slave_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_slave_t_compliance_template.vhd new file mode 100644 index 000000000..bb136e697 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/axi_stream_pkg/tb_axi_stream_slave_t_compliance_template.vhd @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_slave_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_slave_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/bus2memory_pkg/tb_bus2memory_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/bus2memory_pkg/tb_bus2memory_t_compliance_template.vhd new file mode 100644 index 000000000..d14049559 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/bus2memory_pkg/tb_bus2memory_t_compliance_template.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.bus2memory_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_bus2memory_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_bus2memory_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + constant address_length : natural := 8; + constant memory : memory_t := new_memory; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/bus_master_pkg/tb_bus_master_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/bus_master_pkg/tb_bus_master_t_compliance_template.vhd new file mode 100644 index 000000000..8ec402914 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/bus_master_pkg/tb_bus_master_t_compliance_template.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.checker_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_bus_master_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_bus_master_t_compliance is +begin + test_runner : process + constant data_length : natural := 32; + constant address_length : natural := 32; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/ram_master_pkg/tb_ram_master_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/ram_master_pkg/tb_ram_master_t_compliance_template.vhd new file mode 100644 index 000000000..cb67c86c4 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/ram_master_pkg/tb_ram_master_t_compliance_template.vhd @@ -0,0 +1,35 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.ram_master_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_ram_master_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_ram_master_t_compliance is +begin + test_runner : process + constant data_length : natural := 8; + constant address_length : natural := 8; + constant latency : positive := 2; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_avalon_master_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_avalon_master_compliance_template.vhd new file mode 100644 index 000000000..0b7350461 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_avalon_master_compliance_template.vhd @@ -0,0 +1,70 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.avalon_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.com_types_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_master_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_master_compliance is + + -- TODO: Specify a value for all listed parameters. Keep all parameters on separate lines + constant avalon_master_handle : avalon_master_t := new_avalon_master( + data_length => 32, + address_length => 32 + ); + + signal clk : std_logic; + signal address : std_logic_vector(31 downto 0); + signal byteenable : std_logic_vector(3 downto 0); + signal burstcount : std_logic_vector(7 downto 0); + signal waitrequest : std_logic; + signal write : std_logic; + signal writedata : std_logic_vector(31 downto 0); + signal read : std_logic; + signal readdata : std_logic_vector(31 downto 0); + signal readdatavalid : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.avalon_master + generic map(avalon_master_handle) + port map( + clk => clk, + address => address, + byteenable => byteenable, + burstcount => burstcount, + waitrequest => waitrequest, + write => write, + writedata => writedata, + read => read, + readdata => readdata, + readdatavalid => readdatavalid + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_avalon_sink_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_avalon_sink_compliance_template.vhd new file mode 100644 index 000000000..e4665ac17 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_avalon_sink_compliance_template.vhd @@ -0,0 +1,55 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.avalon_stream_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_sink_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_sink_compliance is + + constant sink : avalon_sink_t := new_avalon_sink( + data_length => 8 + ); + + signal clk : std_logic; + signal ready : std_logic; + signal valid : std_logic; + signal sop : std_logic; + signal eop : std_logic; + signal data : std_logic_vector(data_length(sink)-1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.avalon_sink + generic map(sink) + port map( + clk => clk, + ready => ready, + valid => valid, + sop => sop, + eop => eop, + data => data + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_avalon_slave_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_avalon_slave_compliance_template.vhd new file mode 100644 index 000000000..44a01690c --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_avalon_slave_compliance_template.vhd @@ -0,0 +1,64 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.avalon_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_slave_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_slave_compliance is + + constant avalon_slave : avalon_slave_t := new_avalon_slave( + memory => new_memory + ); + + signal clk : std_logic; + signal address : std_logic_vector(31 downto 0); + signal byteenable : std_logic_vector(3 downto 0); + signal burstcount : std_logic_vector(7 downto 0); + signal waitrequest : std_logic; + signal write : std_logic; + signal writedata : std_logic_vector(31 downto 0); + signal read : std_logic; + signal readdata : std_logic_vector(31 downto 0); + signal readdatavalid : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.avalon_slave + generic map(avalon_slave) + port map( + clk => clk, + address => address, + byteenable => byteenable, + burstcount => burstcount, + waitrequest => waitrequest, + write => write, + writedata => writedata, + read => read, + readdata => readdata, + readdatavalid => readdatavalid + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_avalon_source_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_avalon_source_compliance_template.vhd new file mode 100644 index 000000000..3af993522 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_avalon_source_compliance_template.vhd @@ -0,0 +1,56 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.avalon_stream_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_avalon_source_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_avalon_source_compliance is + + constant source : avalon_source_t := new_avalon_source( + data_length => 8 + ); + + signal clk : std_logic; + signal ready : std_logic; + signal valid : std_logic; + signal sop : std_logic; + signal eop : std_logic; + signal data : std_logic_vector(data_length(source)-1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.avalon_source + generic map(source) + port map( + clk => clk, + ready => ready, + valid => valid, + sop => sop, + eop => eop, + data => data + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_lite_master_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_lite_master_compliance_template.vhd new file mode 100644 index 000000000..da006f9e5 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_lite_master_compliance_template.vhd @@ -0,0 +1,81 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_lite_master_pkg.all; +use vunit_lib.axi_pkg.all; +use vunit_lib.axi_slave_pkg.all; +use vunit_lib.axi_slave_private_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_lite_master_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_lite_master_compliance is + + constant bus_handle : bus_master_t := new_bus( + data_length => 32, + address_length => 32 + ); + + signal aclk : std_logic; + signal arready : std_logic; + signal arvalid : std_logic; + signal araddr : std_logic_vector(address_length(bus_handle) - 1 downto 0); + signal rready : std_logic; + signal rvalid : std_logic; + signal rdata : std_logic_vector(data_length(bus_handle) - 1 downto 0); + signal rresp : axi_resp_t; + signal awready : std_logic; + signal awvalid : std_logic; + signal awaddr : std_logic_vector(address_length(bus_handle) - 1 downto 0); + signal wready : std_logic; + signal wvalid : std_logic; + signal wdata : std_logic_vector(data_length(bus_handle) - 1 downto 0); + signal wstrb : std_logic_vector(byte_enable_length(bus_handle) - 1 downto 0); + signal bvalid : std_logic; + signal bready : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_lite_master + generic map(bus_handle) + port map( + aclk => aclk, + arready => arready, + arvalid => arvalid, + araddr => araddr, + rready => rready, + rvalid => rvalid, + rdata => rdata, + rresp => rresp, + awready => awready, + awvalid => awvalid, + awaddr => awaddr, + wready => wready, + wvalid => wvalid, + wdata => wdata, + wstrb => wstrb, + bvalid => bvalid, + bready => bready, + bresp => open + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_read_slave_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_read_slave_compliance_template.vhd new file mode 100644 index 000000000..b5fd98139 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_read_slave_compliance_template.vhd @@ -0,0 +1,78 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vc_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use vunit_lib.axi_pkg.all; +use vunit_lib.axi_slave_pkg.all; +use vunit_lib.axi_slave_private_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_read_slave_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_read_slave_compliance is + + constant memory : memory_t := new_memory; + constant axi_slave : axi_slave_t := new_axi_slave( + memory => memory + ); + + constant log_data_size : integer := 4; + constant data_size : integer := 2**log_data_size; + + signal aclk : std_logic; + signal arvalid : std_logic; + signal arready : std_logic; + signal arid : std_logic_vector(3 downto 0); + signal araddr : std_logic_vector(31 downto 0); + signal arlen : axi4_len_t; + signal arsize : axi4_size_t; + signal arburst : axi_burst_type_t; + signal rvalid : std_logic; + signal rready : std_logic; + signal rid : std_logic_vector(arid'range); + signal rdata : std_logic_vector(8*data_size-1 downto 0); + signal rresp : axi_resp_t; + signal rlast : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_read_slave + generic map(axi_slave) + + port map( + aclk => aclk, + arvalid => arvalid, + arready => arready, + arid => arid, + araddr => araddr, + arlen => arlen, + arsize => arsize, + arburst => arburst, + rvalid => rvalid, + rready => rready, + rid => rid, + rdata => rdata, + rresp => rresp, + rlast => rlast + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_stream_master_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_stream_master_compliance_template.vhd new file mode 100644 index 000000000..86f9a5fe3 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_stream_master_compliance_template.vhd @@ -0,0 +1,62 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.stream_master_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_master_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_master_compliance is + + constant master : axi_stream_master_t := new_axi_stream_master( + data_length => 8 + ); + + signal aclk : std_logic; + signal tvalid : std_logic; + signal tdata : std_logic_vector(data_length(master)-1 downto 0); + signal tlast : std_logic; + signal tkeep : std_logic_vector(data_length(master)/8-1 downto 0); + signal tstrb : std_logic_vector(data_length(master)/8-1 downto 0); + signal tid : std_logic_vector(id_length(master)-1 downto 0); + signal tdest : std_logic_vector(dest_length(master)-1 downto 0); + signal tuser : std_logic_vector(user_length(master)-1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_stream_master + generic map(master) + port map( + aclk => aclk, + areset_n => open, + tvalid => tvalid, + tready => open, + tdata => tdata, + tlast => tlast, + tkeep => tkeep, + tstrb => tstrb, + tid => tid, + tdest => tdest, + tuser => tuser + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_stream_monitor_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_stream_monitor_compliance_template.vhd new file mode 100644 index 000000000..1fa2e368f --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_stream_monitor_compliance_template.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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_monitor_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_monitor_compliance is + + constant monitor : axi_stream_monitor_t := new_axi_stream_monitor( + data_length => 8 + ); + + signal aclk : std_logic; + signal tvalid : std_logic; + signal tdata : std_logic_vector(data_length(monitor) - 1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_stream_monitor + generic map(monitor) + port map( + aclk => aclk, + tvalid => tvalid, + tready => open, + tdata => tdata, + tlast => open, + tkeep => open, + tstrb => open, + tid => open, + tdest => open, + tuser => open + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_stream_protocol_checker_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_stream_protocol_checker_compliance_template.vhd new file mode 100644 index 000000000..8fce31136 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_stream_protocol_checker_compliance_template.vhd @@ -0,0 +1,56 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std_unsigned.all; +use ieee.std_logic_1164.all; +use std.textio.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_protocol_checker_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_protocol_checker_compliance is + + constant protocol_checker : axi_stream_protocol_checker_t := new_axi_stream_protocol_checker( + data_length => 8 + ); + + signal aclk : std_logic; + signal tvalid : std_logic; + signal tdata : std_logic_vector(data_length(protocol_checker) - 1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_stream_protocol_checker + generic map(protocol_checker) + port map( + aclk => aclk, + areset_n => open, + tvalid => tvalid, + tready => open, + tdata => tdata, + tlast => open, + tkeep => open, + tstrb => open, + tid => open, + tdest => open, + tuser => open + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_stream_slave_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_stream_slave_compliance_template.vhd new file mode 100644 index 000000000..4d6e91de4 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_stream_slave_compliance_template.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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.axi_stream_pkg.all; +use vunit_lib.stream_slave_pkg.all; +use vunit_lib.string_ptr_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_stream_slave_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_stream_slave_compliance is + + constant slave : axi_stream_slave_t := new_axi_stream_slave( + data_length => 8 + ); + + signal aclk : std_logic; + signal tvalid : std_logic; + signal tready : std_logic; + signal tdata : std_logic_vector(data_length(slave)-1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_stream_slave + generic map(slave) + port map( + aclk => aclk, + areset_n => open, + tvalid => tvalid, + tready => tready, + tdata => tdata, + tlast => open, + tkeep => open, + tstrb => open, + tid => open, + tdest => open, + tuser => open + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_axi_write_slave_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_axi_write_slave_compliance_template.vhd new file mode 100644 index 000000000..77589778d --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_axi_write_slave_compliance_template.vhd @@ -0,0 +1,83 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use vunit_lib.axi_pkg.all; +use vunit_lib.axi_slave_pkg.all; +use vunit_lib.axi_slave_private_pkg.all; +use vunit_lib.integer_vector_ptr_pkg.all; +use vunit_lib.integer_vector_ptr_pool_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_axi_write_slave_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_axi_write_slave_compliance is + constant axi_slave : axi_slave_t := new_axi_slave( + memory => new_memory + ); + + constant log_data_size : integer := 4; + constant data_size : integer := 2**log_data_size; + + signal aclk : std_logic; + signal awvalid : std_logic; + signal awready : std_logic; + signal awid : std_logic_vector(3 downto 0); + signal awaddr : std_logic_vector(31 downto 0); + signal awlen : std_logic_vector(7 downto 0); + signal awsize : std_logic_vector(2 downto 0); + signal awburst : axi_burst_type_t; + signal wvalid : std_logic; + signal wready : std_logic; + signal wdata : std_logic_vector(8*data_size-1 downto 0); + signal wstrb : std_logic_vector(data_size downto 0); + signal wlast : std_logic; + signal bvalid : std_logic; + signal bready : std_logic; + signal bid : std_logic_vector(awid'range); + signal bresp : axi_resp_t; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.axi_write_slave + generic map(axi_slave) + port map( + aclk => aclk, + awvalid => awvalid, + awready => awready, + awid => awid, + awaddr => awaddr, + awlen => awlen, + awsize => awsize, + awburst => awburst, + wvalid => wvalid, + wready => wready, + wdata => wdata, + wstrb => wstrb, + wlast => wlast, + bvalid => bvalid, + bready => bready, + bid => bid, + bresp => bresp + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_bus2memory_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_bus2memory_compliance_template.vhd new file mode 100644 index 000000000..9d8beb5cc --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_bus2memory_compliance_template.vhd @@ -0,0 +1,41 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use vunit_lib.bus2memory_pkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_bus2memory_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_bus2memory_compliance is + constant bus2memory_handle : bus2memory_t := new_bus2memory( + data_length => 8, + address_length => 8, + memory => new_memory + ); +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.bus2memory + generic map(bus2memory_handle); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_ram_master_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_ram_master_compliance_template.vhd new file mode 100644 index 000000000..88c90bbcf --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_ram_master_compliance_template.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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.ram_master_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_ram_master_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_ram_master_compliance is + + constant ram_master : ram_master_t := new_ram_master( + data_length => 32, + address_length => 32, + latency => 2 + ); + + signal clk : std_logic; + signal en : std_logic; + signal we : std_logic_vector(byte_enable_length(as_bus_master(ram_master))-1 downto 0); + signal addr : std_logic_vector(address_length(as_bus_master(ram_master)) - 1 downto 0); + signal wdata : std_logic_vector(data_length(as_bus_master(ram_master)) - 1 downto 0); + signal rdata : std_logic_vector(data_length(as_bus_master(ram_master)) - 1 downto 0); + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.ram_master + generic map(ram_master) + port map( + clk => clk, + en => en, + we => we, + addr => addr, + wdata => wdata, + rdata => rdata + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_std_logic_checker_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_std_logic_checker_compliance_template.vhd new file mode 100644 index 000000000..57bcab7ee --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_std_logic_checker_compliance_template.vhd @@ -0,0 +1,39 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.data_types_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.signal_checker_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; + +entity tb_std_logic_checker_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_std_logic_checker_compliance is + constant signal_checker : signal_checker_t := new_signal_checker; + + signal value : std_logic_vector(7 downto 0); +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.std_logic_checker + generic map(signal_checker) + port map( + value => value + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_wishbone_master_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_wishbone_master_compliance_template.vhd new file mode 100644 index 000000000..2ec0db0bc --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_wishbone_master_compliance_template.vhd @@ -0,0 +1,71 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.check_pkg.all; +use vunit_lib.com_types_pkg.all; +use vunit_lib.log_levels_pkg.all; +use vunit_lib.logger_pkg.all; +use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; +use vunit_lib.wishbone_pkg.all; + +entity tb_wishbone_master_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_wishbone_master_compliance is + + constant wishbone_master : wishbone_master_t := new_wishbone_master( + data_length => 32, + address_length => 32 + ); + + signal clk : std_logic; + signal adr : std_logic_vector(31 downto 0); + signal dat_i : std_logic_vector(31 downto 0); + signal dat_o : std_logic_vector(31 downto 0); + signal sel : std_logic_vector(3 downto 0); + signal cyc : std_logic; + signal stb : std_logic; + signal we : std_logic; + signal stall : std_logic; + signal ack : std_logic; + +begin + -- DO NOT modify the test runner process. + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + -- DO NOT modify the VC instantiation. + vc_inst: entity vunit_lib.wishbone_master + generic map(wishbone_master) + port map( + clk => clk, + adr => adr, + dat_i => dat_i, + dat_o => dat_o, + sel => sel, + cyc => cyc, + stb => stb, + we => we, + stall => stall, + ack => ack + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/tb_wishbone_slave_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/tb_wishbone_slave_compliance_template.vhd new file mode 100644 index 000000000..23e4367f5 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/tb_wishbone_slave_compliance_template.vhd @@ -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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library osvvm; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.numeric_std.all; +use ieee.std_logic_1164.all; +use osvvm.randompkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; +use vunit_lib.wishbone_pkg.all; + +entity tb_wishbone_slave_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_wishbone_slave_compliance is + + constant memory : memory_t := new_memory; + constant wishbone_slave : wishbone_slave_t := new_wishbone_slave( + memory => memory + ); + + signal clk : std_logic; + signal adr : std_logic_vector(31 downto 0) := (others => '0'); + signal dat_i : std_logic_vector(31 downto 0) := (others => '0'); + signal dat_o : std_logic_vector(31 downto 0) := (others => '0'); + signal sel : std_logic_vector(3 downto 0) := (others => '1'); + signal cyc : std_logic; + signal stb : std_logic; + signal we : std_logic; + signal stall : std_logic; + signal ack : std_logic; + +begin + test_runner : process + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; + + vc_inst: entity vunit_lib.wishbone_slave + generic map(wishbone_slave) + port map( + clk => clk, + adr => adr, + dat_i => dat_i, + dat_o => dat_o, + sel => sel, + cyc => cyc, + stb => stb, + we => we, + stall => stall, + ack => ack + ); + +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_master_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_master_t_compliance_template.vhd new file mode 100644 index 000000000..4c5f7c173 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_master_t_compliance_template.vhd @@ -0,0 +1,34 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; +use vunit_lib.wishbone_pkg.all; + +entity tb_wishbone_master_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_wishbone_master_t_compliance is +begin + test_runner : process + constant data_length : natural := 32; + constant address_length : natural := 32; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_slave_t_compliance_template.vhd b/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_slave_t_compliance_template.vhd new file mode 100644 index 000000000..bcfa14161 --- /dev/null +++ b/vunit/vhdl/verification_components/.vc/wishbone_pkg/tb_wishbone_slave_t_compliance_template.vhd @@ -0,0 +1,33 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +library vunit_lib; +context vunit_lib.com_context; +context vunit_lib.vunit_context; +use ieee.std_logic_1164.all; +use vunit_lib.bus_master_pkg.all; +use vunit_lib.memory_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; +use vunit_lib.wishbone_pkg.all; + +entity tb_wishbone_slave_t_compliance is + generic( + runner_cfg : string); +end entity; + +architecture tb of tb_wishbone_slave_t_compliance is +begin + test_runner : process + constant memory : memory_t := new_memory; + + -- DO NOT modify this line and the lines below. + begin + test_runner_setup(runner, runner_cfg); + test_runner_cleanup(runner); + end process test_runner; +end architecture; diff --git a/vunit/vhdl/verification_components/run.py b/vunit/vhdl/verification_components/run.py index 068b209d1..ad8df47a2 100644 --- a/vunit/vhdl/verification_components/run.py +++ b/vunit/vhdl/verification_components/run.py @@ -6,7 +6,11 @@ from pathlib import Path from itertools import product -from vunit import VUnit +from vunit import ( + VUnit, + VerificationComponentInterface, + VerificationComponent, +) ROOT = Path(__file__).parent @@ -158,6 +162,7 @@ def gen_avalon_master_tests(obj, *args): for max_waits in [0, 8]: TEST_FAILING_MAX_WAITS.add_config(name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits)) + TB_AXI_STREAM.test("test random stall on master").add_config( name="stall_master", generics=dict(g_stall_percentage_master=30) ) @@ -170,4 +175,199 @@ def gen_avalon_master_tests(obj, *args): name="stall_slave", generics=dict(g_stall_percentage_slave=40) ) +TEST_LIB = UI.add_library("test_lib") + +bus_master_vci = VerificationComponentInterface.find(LIB, "bus_master_pkg", "bus_master_t") +bus_master_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "bus_master_pkg" / "tb_bus_master_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_lite_master", bus_master_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_lite_master_compliance_template.vhd", +) + +axi_slave_vci = VerificationComponentInterface.find(LIB, "axi_slave_pkg", "axi_slave_t") +axi_slave_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "axi_slave_pkg" / "tb_axi_slave_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_read_slave", axi_slave_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_read_slave_compliance_template.vhd", +) + +VerificationComponent.find(LIB, "axi_write_slave", axi_slave_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_write_slave_compliance_template.vhd", +) + +axi_stream_master_vci = VerificationComponentInterface.find(LIB, "axi_stream_pkg", "axi_stream_master_t") +axi_stream_master_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "axi_stream_pkg" / "tb_axi_stream_master_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_stream_master", axi_stream_master_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_stream_master_compliance_template.vhd", +) + +axi_stream_slave_vci = VerificationComponentInterface.find(LIB, "axi_stream_pkg", "axi_stream_slave_t") +axi_stream_slave_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "axi_stream_pkg" / "tb_axi_stream_slave_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_stream_slave", axi_stream_slave_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_stream_slave_compliance_template.vhd", +) + +axi_stream_monitor_vci = VerificationComponentInterface.find(LIB, "axi_stream_pkg", "axi_stream_monitor_t") +axi_stream_monitor_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "axi_stream_pkg" / "tb_axi_stream_monitor_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_stream_monitor", axi_stream_monitor_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_stream_monitor_compliance_template.vhd", +) + +axi_stream_protocol_checker_vci = VerificationComponentInterface.find( + LIB, "axi_stream_pkg", "axi_stream_protocol_checker_t" +) +axi_stream_protocol_checker_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "axi_stream_pkg" / "tb_axi_stream_protocol_checker_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "axi_stream_protocol_checker", axi_stream_protocol_checker_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_axi_stream_protocol_checker_compliance_template.vhd", +) + +uart_master_vci = VerificationComponentInterface.find(LIB, "uart_pkg", "uart_master_t") +uart_master_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) +VerificationComponent.find(LIB, "uart_master", uart_master_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +uart_slave_vci = VerificationComponentInterface.find(LIB, "uart_pkg", "uart_slave_t") +uart_slave_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) +VerificationComponent.find(LIB, "uart_slave", uart_slave_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +std_logic_checker_vci = VerificationComponentInterface.find(LIB, "signal_checker_pkg", "signal_checker_t") +std_logic_checker_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) +VerificationComponent.find(LIB, "std_logic_checker", std_logic_checker_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_std_logic_checker_compliance_template.vhd", +) + +ram_master_vci = VerificationComponentInterface.find(LIB, "ram_master_pkg", "ram_master_t") +ram_master_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "ram_master_pkg" / "tb_ram_master_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "ram_master", ram_master_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_ram_master_compliance_template.vhd", +) + +VerificationComponentInterface.find(LIB, "stream_master_pkg", "stream_master_t").add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +VerificationComponentInterface.find(LIB, "stream_slave_pkg", "stream_slave_t").add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", +) + +bus2memory_vci = VerificationComponentInterface.find(LIB, "bus2memory_pkg", "bus2memory_t") +bus2memory_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "bus2memory_pkg" / "tb_bus2memory_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "bus2memory", bus2memory_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_bus2memory_compliance_template.vhd", +) + +avalon_master_vci = VerificationComponentInterface.find(LIB, "avalon_pkg", "avalon_master_t") +avalon_master_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "avalon_pkg" / "tb_avalon_master_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "avalon_master", avalon_master_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_avalon_master_compliance_template.vhd", +) + +avalon_slave_vci = VerificationComponentInterface.find(LIB, "avalon_pkg", "avalon_slave_t") +avalon_slave_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "avalon_pkg" / "tb_avalon_slave_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "avalon_slave", avalon_slave_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_avalon_slave_compliance_template.vhd", +) + +avalon_source_vci = VerificationComponentInterface.find(LIB, "avalon_stream_pkg", "avalon_source_t") +avalon_source_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "avalon_stream_pkg" / "tb_avalon_source_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "avalon_source", avalon_source_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_avalon_source_compliance_template.vhd", +) + +avalon_sink_vci = VerificationComponentInterface.find(LIB, "avalon_stream_pkg", "avalon_sink_t") +avalon_sink_vci.add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "avalon_stream_pkg" / "tb_avalon_sink_t_compliance_template.vhd", +) +VerificationComponent.find(LIB, "avalon_sink", avalon_sink_vci).add_vhdl_testbench( + TEST_LIB, + ROOT / "compliance_test", + ROOT / ".vc" / "tb_avalon_sink_compliance_template.vhd", +) + UI.main() diff --git a/vunit/vhdl/verification_components/src/avalon_master.vhd b/vunit/vhdl/verification_components/src/avalon_master.vhd index 1471da0f1..b20ca239e 100644 --- a/vunit/vhdl/verification_components/src/avalon_master.vhd +++ b/vunit/vhdl/verification_components/src/avalon_master.vhd @@ -18,19 +18,17 @@ use work.com_types_pkg.all; use work.logger_pkg.all; use work.check_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; +use work.avalon_pkg.all; library osvvm; use osvvm.RandomPkg.all; entity avalon_master is - generic ( - bus_handle : bus_master_t; - use_readdatavalid : boolean := true; - fixed_read_latency : natural := 1; -- (bus cycles). This parameter is ignored when use_readdatavalid is true - write_high_probability : real range 0.0 to 1.0 := 1.0; - read_high_probability : real range 0.0 to 1.0 := 1.0 + generic( + avalon_master_handle : avalon_master_t ); - port ( + port( clk : in std_logic; address : out std_logic_vector; byteenable : out std_logic_vector; @@ -57,85 +55,80 @@ begin variable request_msg : msg_t; variable msg_type : msg_type_t; variable rnd : RandomPType; - variable msgs : natural; variable burst : positive; begin rnd.InitSeed(rnd'instance_name); write <= '0'; read <= '0'; burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - wait until rising_edge(clk); loop - request_msg := null_msg; - msgs := num_of_messages(bus_handle.p_actor); - if (msgs > 0) then - receive(net, bus_handle.p_actor, request_msg); - msg_type := message_type(request_msg); - if msg_type = bus_read_msg then - while rnd.Uniform(0.0, 1.0) > read_high_probability loop - wait until rising_edge(clk); - end loop; - address <= pop_std_ulogic_vector(request_msg); - byteenable(byteenable'range) <= (others => '1'); - read <= '1'; - push(acknowledge_queue, request_msg); - wait until rising_edge(clk) and waitrequest = '0'; - read <= '0'; + receive(net, get_actor(avalon_master_handle.p_bus_handle), request_msg); + msg_type := message_type(request_msg); - elsif msg_type = bus_burst_read_msg then - while rnd.Uniform(0.0, 1.0) > read_high_probability loop - wait until rising_edge(clk); - end loop; - address <= pop_std_ulogic_vector(request_msg); - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); - burst := pop_integer(request_msg); - burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); - byteenable(byteenable'range) <= (others => '1'); - read <= '1'; - push(burst_acknowledge_queue, request_msg); - wait until rising_edge(clk) and waitrequest = '0'; - read <= '0'; - push(burstlen_queue, burst); + if msg_type = bus_read_msg then + while rnd.Uniform(0.0, 1.0) > avalon_master_handle.p_read_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + byteenable(byteenable'range) <= (others => '1'); + read <= '1'; + push(acknowledge_queue, request_msg); + wait until rising_edge(clk) and waitrequest = '0'; + read <= '0'; - elsif msg_type = bus_write_msg then - while rnd.Uniform(0.0, 1.0) > write_high_probability loop + elsif msg_type = bus_burst_read_msg then + while rnd.Uniform(0.0, 1.0) > avalon_master_handle.p_read_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + burst := pop_integer(request_msg); + burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); + byteenable(byteenable'range) <= (others => '1'); + read <= '1'; + push(burst_acknowledge_queue, request_msg); + wait until rising_edge(clk) and waitrequest = '0'; + read <= '0'; + push(burstlen_queue, burst); + + elsif msg_type = bus_write_msg then + while rnd.Uniform(0.0, 1.0) > avalon_master_handle.p_write_high_probability loop + wait until rising_edge(clk); + end loop; + address <= pop_std_ulogic_vector(request_msg); + burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); + writedata <= pop_std_ulogic_vector(request_msg); + byteenable <= pop_std_ulogic_vector(request_msg); + write <= '1'; + wait until rising_edge(clk) and waitrequest = '0'; + write <= '0'; + + elsif msg_type = bus_burst_write_msg then + address <= pop_std_ulogic_vector(request_msg); + burst := pop_integer(request_msg); + burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); + for i in 0 to burst-1 loop + while rnd.Uniform(0.0, 1.0) > avalon_master_handle.p_write_high_probability loop wait until rising_edge(clk); end loop; - address <= pop_std_ulogic_vector(request_msg); - burstcount <= std_logic_vector(to_unsigned(1, burstcount'length)); writedata <= pop_std_ulogic_vector(request_msg); - byteenable <= pop_std_ulogic_vector(request_msg); + -- TODO handle byteenable + byteenable(byteenable'range) <= (others => '1'); write <= '1'; wait until rising_edge(clk) and waitrequest = '0'; write <= '0'; + address(address'range) <= (others => 'U'); + burstcount(burstcount'range) <= (others => 'U'); + end loop; - elsif msg_type = bus_burst_write_msg then - address <= pop_std_ulogic_vector(request_msg); - burst := pop_integer(request_msg); - burstcount <= std_logic_vector(to_unsigned(burst, burstcount'length)); - for i in 0 to burst-1 loop - while rnd.Uniform(0.0, 1.0) > write_high_probability loop - wait until rising_edge(clk); - end loop; - writedata <= pop_std_ulogic_vector(request_msg); - -- TODO handle byteenable - byteenable(byteenable'range) <= (others => '1'); - write <= '1'; - wait until rising_edge(clk) and waitrequest = '0'; - write <= '0'; - address(address'range) <= (others => 'U'); - burstcount(burstcount'range) <= (others => 'U'); - end loop; - - elsif msg_type = wait_until_idle_msg then - wait until not burst_read_flag and is_empty(burst_acknowledge_queue) and rising_edge(clk); - handle_wait_until_idle(net, msg_type, request_msg); + elsif msg_type = wait_until_idle_msg or msg_type = wait_for_time_msg then + while burst_read_flag or not is_empty(burst_acknowledge_queue) loop + wait until rising_edge(clk); + end loop; + handle_sync_message(net, msg_type, request_msg); - else - unexpected_msg_type(msg_type); - end if; else - wait until rising_edge(clk); + unexpected_msg_type(msg_type, get_std_cfg(avalon_master_handle.p_bus_handle)); end if; end loop; end process; @@ -143,13 +136,13 @@ begin read_capture : process variable request_msg, reply_msg : msg_t; begin - if use_readdatavalid then + if avalon_master_handle.p_use_readdatavalid then wait until readdatavalid = '1' and not is_empty(acknowledge_queue) and rising_edge(clk); else -- Non-pipelined case: waits for slave to de-assert waitrequest and sample data after fixed_read_latency cycles. wait until rising_edge(clk) and waitrequest = '0' and read = '1'; - if fixed_read_latency > 0 then - for i in 0 to fixed_read_latency - 1 loop + if avalon_master_handle.p_fixed_read_latency > 0 then + for i in 0 to avalon_master_handle.p_fixed_read_latency - 1 loop wait until rising_edge(clk); end loop; end if; diff --git a/vunit/vhdl/verification_components/src/avalon_pkg.vhd b/vunit/vhdl/verification_components/src/avalon_pkg.vhd index b6991f0e1..7ec8899d2 100644 --- a/vunit/vhdl/verification_components/src/avalon_pkg.vhd +++ b/vunit/vhdl/verification_components/src/avalon_pkg.vhd @@ -9,48 +9,143 @@ use ieee.std_logic_1164.all; use work.queue_pkg.all; use work.logger_pkg.all; +use work.checker_pkg.all; use work.memory_pkg.all; +use work.bus_master_pkg.all; +use work.vc_pkg.all; +use work.sync_pkg.all; context work.com_context; package avalon_pkg is + type avalon_master_t is record + p_bus_handle : bus_master_t; + p_use_readdatavalid : boolean; + p_fixed_read_latency : natural; + p_write_high_probability : real range 0.0 to 1.0; + p_read_high_probability : real range 0.0 to 1.0; + end record; + type avalon_slave_t is record - readdatavalid_high_probability : real range 0.0 to 1.0; - waitrequest_high_probability : real range 0.0 to 1.0; - -- Private - p_actor : actor_t; + p_readdatavalid_high_probability : real range 0.0 to 1.0; + p_waitrequest_high_probability : real range 0.0 to 1.0; + p_std_cfg : std_cfg_t; p_ack_actor : actor_t; p_memory : memory_t; - p_logger : logger_t; end record; - constant avalon_slave_logger : logger_t := get_logger("vunit_lib:avalon_pkg"); + constant avalon_logger : logger_t := get_logger("vunit_lib:avalon_pkg"); + constant avalon_checker : checker_t := new_checker(avalon_logger); + + impure function new_avalon_master( + data_length : natural; + address_length : natural; + use_readdatavalid : boolean := true; + fixed_read_latency : natural := 1; -- (bus cycles). This parameter is ignored when use_readdatavalid is true + write_high_probability : real range 0.0 to 1.0 := 1.0; + read_high_probability : real range 0.0 to 1.0 := 1.0; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return avalon_master_t; + impure function new_avalon_slave( - memory : memory_t; - readdatavalid_high_probability : real := 1.0; - waitrequest_high_probability : real := 0.0; - name : string := ""; - logger : logger_t := avalon_slave_logger) - return avalon_slave_t; + memory : memory_t; + readdatavalid_high_probability : real := 1.0; + waitrequest_high_probability : real := 0.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) + return avalon_slave_t; + + impure function as_sync(avalon_master : avalon_master_t) return sync_handle_t; + impure function as_sync(avalon_slave : avalon_slave_t) return sync_handle_t; + impure function as_bus_master(avalon_master : avalon_master_t) return bus_master_t; + impure function get_std_cfg(avalon_master : avalon_master_t) return std_cfg_t; + impure function get_std_cfg(avalon_slave : avalon_slave_t) return std_cfg_t; end package; package body avalon_pkg is + impure function new_avalon_master( + data_length : natural; + address_length : natural; + use_readdatavalid : boolean := true; + fixed_read_latency : natural := 1; -- (bus cycles). This parameter is ignored when use_readdatavalid is true + write_high_probability : real range 0.0 to 1.0 := 1.0; + read_high_probability : real range 0.0 to 1.0 := 1.0; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return avalon_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + avalon_logger, avalon_checker, actor, logger, checker, unexpected_msg_type_policy + ); + constant p_bus_handle : bus_master_t := new_bus(data_length, address_length, byte_length, get_logger(p_std_cfg), get_actor(p_std_cfg), get_checker(p_std_cfg), + unexpected_msg_type_policy + ); + begin + return (p_bus_handle => p_bus_handle, + p_use_readdatavalid => use_readdatavalid, + p_fixed_read_latency => fixed_read_latency, + p_write_high_probability => write_high_probability, + p_read_high_probability => read_high_probability + ); + end; + impure function new_avalon_slave( - memory : memory_t; - readdatavalid_high_probability : real := 1.0; - waitrequest_high_probability : real := 0.0; - name : string := ""; - logger : logger_t := avalon_slave_logger) - return avalon_slave_t is + memory : memory_t; + readdatavalid_high_probability : real := 1.0; + waitrequest_high_probability : real := 0.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) + return avalon_slave_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + avalon_logger, avalon_checker, actor, logger, checker, unexpected_msg_type_policy + ); + + begin + return (p_std_cfg => p_std_cfg, + p_ack_actor => new_actor(name(actor) & " read-ack"), + p_memory => to_vc_interface(memory, get_logger(p_std_cfg)), + p_readdatavalid_high_probability => readdatavalid_high_probability, + p_waitrequest_high_probability => waitrequest_high_probability + ); + end; + + impure function as_sync(avalon_master : avalon_master_t) return sync_handle_t is begin - return (p_actor => new_actor(name), - p_ack_actor => new_actor(name&" read-ack"), - p_memory => to_vc_interface(memory, logger), - p_logger => logger, - readdatavalid_high_probability => readdatavalid_high_probability, - waitrequest_high_probability => waitrequest_high_probability - ); + return as_sync(avalon_master.p_bus_handle); end; + impure function as_sync(avalon_slave : avalon_slave_t) return sync_handle_t is + begin + return get_actor(avalon_slave.p_std_cfg); + end; + + impure function as_bus_master(avalon_master : avalon_master_t) return bus_master_t is + begin + return avalon_master.p_bus_handle; + end; + + impure function get_std_cfg(avalon_master : avalon_master_t) return std_cfg_t is + begin + return get_std_cfg(avalon_master.p_bus_handle); + end; + + impure function get_std_cfg(avalon_slave : avalon_slave_t) return std_cfg_t is + begin + return avalon_slave.p_std_cfg; + end; + + end package body; diff --git a/vunit/vhdl/verification_components/src/avalon_sink.vhd b/vunit/vhdl/verification_components/src/avalon_sink.vhd index be069f642..fff35c68e 100644 --- a/vunit/vhdl/verification_components/src/avalon_sink.vhd +++ b/vunit/vhdl/verification_components/src/avalon_sink.vhd @@ -15,6 +15,8 @@ context work.vunit_context; context work.com_context; use work.stream_slave_pkg.all; use work.avalon_stream_pkg.all; +use work.vc_pkg.all; +use work.sync_pkg.all; library osvvm; use osvvm.RandomPkg.all; @@ -40,13 +42,15 @@ begin variable rnd : RandomPType; variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); begin - receive(net, sink.p_actor, msg); + receive(net, get_actor(sink.p_std_cfg), msg); msg_type := message_type(msg); + handle_sync_message(net, msg_type, msg); + if msg_type = stream_pop_msg or msg_type = pop_avalon_stream_msg then -- Loop till got valid data loop - while rnd.Uniform(0.0, 1.0) > sink.ready_high_probability loop + while rnd.Uniform(0.0, 1.0) > sink.p_ready_high_probability loop wait until rising_edge(clk); end loop; ready <= '1'; @@ -77,7 +81,7 @@ begin end loop; else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, sink.p_std_cfg); end if; end process; diff --git a/vunit/vhdl/verification_components/src/avalon_slave.vhd b/vunit/vhdl/verification_components/src/avalon_slave.vhd index c3c8fcbdf..35ed972c7 100644 --- a/vunit/vhdl/verification_components/src/avalon_slave.vhd +++ b/vunit/vhdl/verification_components/src/avalon_slave.vhd @@ -15,6 +15,8 @@ context work.vunit_context; context work.com_context; use work.memory_pkg.all; use work.avalon_pkg.all; +use work.vc_pkg.all; +use work.sync_pkg.all; library osvvm; use osvvm.RandomPkg.all; @@ -43,6 +45,18 @@ architecture a of avalon_slave is begin + -- TODO: Add interface for updating probability numbers + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(avalon_slave.p_std_cfg), request_msg); + msg_type := message_type(request_msg); + + handle_sync_message(net, msg_type, request_msg); + unexpected_msg_type(msg_type, avalon_slave.p_std_cfg); + end process; + write_handler : process variable pending_writes : positive := 1; variable addr : natural; @@ -67,7 +81,7 @@ begin variable rd_request_msg : msg_t; begin wait until read = '1' and waitrequest = '0' and rising_edge(clk); - rd_request_msg := new_msg(slave_read_msg, avalon_slave.p_actor); + rd_request_msg := new_msg(slave_read_msg); -- For read, only address is passed to ack proc push_integer(rd_request_msg, to_integer(unsigned(burstcount))); push_integer(rd_request_msg, to_integer(unsigned(address))); @@ -89,7 +103,7 @@ begin burst := pop_integer(request_msg); baseaddr := pop_integer(request_msg); for i in 0 to burst-1 loop - while rnd.Uniform(0.0, 1.0) > avalon_slave.readdatavalid_high_probability loop + while rnd.Uniform(0.0, 1.0) > avalon_slave.p_readdatavalid_high_probability loop wait until rising_edge(clk); end loop; readdata <= read_word(avalon_slave.p_memory, baseaddr + byteenable'length*i, byteenable'length); @@ -106,7 +120,7 @@ begin waitrequest_stim: process variable rnd : RandomPType; begin - if rnd.Uniform(0.0, 1.0) < avalon_slave.waitrequest_high_probability then + if rnd.Uniform(0.0, 1.0) < avalon_slave.p_waitrequest_high_probability then waitrequest <= '1'; else waitrequest <= '0'; diff --git a/vunit/vhdl/verification_components/src/avalon_source.vhd b/vunit/vhdl/verification_components/src/avalon_source.vhd index 18cda6fc9..8ae40242e 100644 --- a/vunit/vhdl/verification_components/src/avalon_source.vhd +++ b/vunit/vhdl/verification_components/src/avalon_source.vhd @@ -14,6 +14,7 @@ use work.stream_master_pkg.all; use work.avalon_stream_pkg.all; use work.queue_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; library osvvm; use osvvm.RandomPkg.all; @@ -39,13 +40,13 @@ begin variable rnd : RandomPType; variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'range)); begin - receive(net, source.p_actor, msg); + receive(net, get_actor(source.p_std_cfg), msg); msg_type := message_type(msg); handle_sync_message(net, msg_type, msg); if msg_type = stream_push_msg or msg_type = push_avalon_stream_msg then - while rnd.Uniform(0.0, 1.0) > source.valid_high_probability loop + while rnd.Uniform(0.0, 1.0) > source.p_valid_high_probability loop wait until rising_edge(clk); end loop; valid <= '1'; @@ -72,7 +73,7 @@ begin sop <= '0'; eop <= '0'; else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, source.p_std_cfg); end if; end process; diff --git a/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd b/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd index 5871944e3..2b8b20fda 100644 --- a/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/avalon_stream_pkg.vhd @@ -8,41 +8,53 @@ library ieee; use ieee.std_logic_1164.all; use work.logger_pkg.all; +use work.checker_pkg.all; use work.stream_master_pkg.all; use work.stream_slave_pkg.all; +use work.vc_pkg.all; +use work.sync_pkg.all; context work.com_context; context work.data_types_context; package avalon_stream_pkg is type avalon_source_t is record - valid_high_probability : real range 0.0 to 1.0; - p_actor : actor_t; - p_data_length : natural; - p_logger : logger_t; + p_std_cfg : std_cfg_t; + p_valid_high_probability : real range 0.0 to 1.0; + p_data_length : natural; end record; type avalon_sink_t is record - ready_high_probability : real range 0.0 to 1.0; - -- Private - p_actor : actor_t; + p_std_cfg : std_cfg_t; + p_ready_high_probability : real range 0.0 to 1.0; p_data_length : natural; - p_logger : logger_t; end record; constant avalon_stream_logger : logger_t := get_logger("vunit_lib:avalon_stream_pkg"); - impure function new_avalon_source(data_length : natural; - valid_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_source_t; - impure function new_avalon_sink(data_length : natural; - ready_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_sink_t; + constant avalon_stream_checker : checker_t := new_checker(avalon_stream_logger); + + impure function new_avalon_source(data_length : natural; + valid_high_probability : real := 1.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return avalon_source_t; + impure function new_avalon_sink(data_length : natural; + ready_high_probability : real := 1.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return avalon_sink_t; impure function data_length(source : avalon_source_t) return natural; impure function data_length(source : avalon_sink_t) return natural; + impure function as_sync(source : avalon_source_t) return sync_handle_t; + impure function as_sync(sink : avalon_sink_t) return sync_handle_t; impure function as_stream(source : avalon_source_t) return stream_master_t; impure function as_stream(sink : avalon_sink_t) return stream_slave_t; + function get_std_cfg(source : avalon_source_t) return std_cfg_t; + function get_std_cfg(sink : avalon_sink_t) return std_cfg_t; + constant push_avalon_stream_msg : msg_type_t := new_msg_type("push avalon stream"); constant pop_avalon_stream_msg : msg_type_t := new_msg_type("pop avalon stream"); @@ -85,32 +97,35 @@ end package; package body avalon_stream_pkg is - impure function new_avalon_source(data_length : natural; - valid_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_source_t is - variable p_actor : actor_t; + impure function new_avalon_source(data_length : natural; + valid_high_probability : real := 1.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return avalon_source_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + avalon_stream_logger, avalon_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin - p_actor := actor when actor /= null_actor else new_actor; - - return (valid_high_probability => valid_high_probability, - p_actor => p_actor, - p_data_length => data_length, - p_logger => logger); + return (p_std_cfg => p_std_cfg, + p_valid_high_probability => valid_high_probability, + p_data_length => data_length); end; - impure function new_avalon_sink(data_length : natural; - ready_high_probability : real := 1.0; - logger : logger_t := avalon_stream_logger; - actor : actor_t := null_actor) return avalon_sink_t is - variable p_actor : actor_t; + impure function new_avalon_sink(data_length : natural; + ready_high_probability : real := 1.0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return avalon_sink_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + avalon_stream_logger, avalon_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin - p_actor := actor when actor /= null_actor else new_actor; - - return (ready_high_probability => ready_high_probability, - p_actor => p_actor, - p_data_length => data_length, - p_logger => logger); + return (p_std_cfg => p_std_cfg, + p_ready_high_probability => ready_high_probability, + p_data_length => data_length); end; impure function data_length(source : avalon_source_t) return natural is @@ -123,15 +138,35 @@ package body avalon_stream_pkg is return source.p_data_length; end; + impure function as_sync(source : avalon_source_t) return sync_handle_t is + begin + return get_actor(source.p_std_cfg); + end; + + impure function as_sync(sink : avalon_sink_t) return sync_handle_t is + begin + return get_actor(sink.p_std_cfg); + end; + impure function as_stream(source : avalon_source_t) return stream_master_t is begin - return (p_actor => source.p_actor); + return (p_std_cfg => source.p_std_cfg); end; impure function as_stream(sink : avalon_sink_t) return stream_slave_t is begin - return (p_actor => sink.p_actor); -end; + return (p_std_cfg => sink.p_std_cfg); + end; + + function get_std_cfg(source : avalon_source_t) return std_cfg_t is + begin + return source.p_std_cfg; + end; + + function get_std_cfg(sink : avalon_sink_t) return std_cfg_t is + begin + return sink.p_std_cfg; + end; procedure push_avalon_stream(signal net : inout network_t; avalon_source : avalon_source_t; @@ -153,7 +188,7 @@ end; avalon_stream_transaction.eop := false; end if; push_avalon_stream_transaction(msg, avalon_stream_transaction); - send(net, avalon_source.p_actor, msg); + send(net, get_actor(avalon_source.p_std_cfg), msg); end; procedure pop_avalon_stream(signal net : inout network_t; @@ -165,7 +200,7 @@ end; variable reply_msg : msg_t; variable avalon_stream_transaction : avalon_stream_transaction_t(data(data'length - 1 downto 0)); begin - send(net, avalon_sink.p_actor, reference); + send(net, get_actor(avalon_sink.p_std_cfg), reference); receive_reply(net, reference, reply_msg); pop_avalon_stream_transaction(reply_msg, avalon_stream_transaction); data := avalon_stream_transaction.data; diff --git a/vunit/vhdl/verification_components/src/axi_lite_master.vhd b/vunit/vhdl/verification_components/src/axi_lite_master.vhd index 908a5e25b..077983309 100644 --- a/vunit/vhdl/verification_components/src/axi_lite_master.vhd +++ b/vunit/vhdl/verification_components/src/axi_lite_master.vhd @@ -11,6 +11,7 @@ use ieee.std_logic_1164.all; use work.queue_pkg.all; use work.bus_master_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; use work.axi_pkg.all; use work.axi_slave_pkg.all; use work.axi_slave_private_pkg.all; @@ -49,22 +50,27 @@ entity axi_lite_master is end entity; architecture a of axi_lite_master is - constant reply_queue, message_queue : queue_t := new_queue; + constant reply_queue, message_queue, transaction_token_queue : queue_t := new_queue; begin main : process variable request_msg : msg_t; variable msg_type : msg_type_t; begin - receive(net, bus_handle.p_actor, request_msg); + receive(net, get_actor(bus_handle), request_msg); msg_type := message_type(request_msg); + handle_wait_for_time(net, msg_type, request_msg); + if is_read(msg_type) or is_write(msg_type) then push(message_queue, request_msg); + push(transaction_token_queue, true); elsif msg_type = wait_until_idle_msg then - wait until ((bvalid and bready) = '1' or (rvalid and rready) = '1') and is_empty(message_queue) and rising_edge(aclk); + if not is_empty(transaction_token_queue) then + wait until rising_edge(aclk) and is_empty(transaction_token_queue); + end if; handle_wait_until_idle(net, msg_type, request_msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, get_std_cfg(bus_handle)); end if; end process; @@ -74,6 +80,7 @@ begin variable msg_type : msg_type_t; variable w_done, aw_done : boolean; variable expected_resp : axi_resp_t; + variable transaction_token : boolean; begin wait until rising_edge(aclk) and not is_empty(message_queue); @@ -93,9 +100,10 @@ begin wait until (rvalid and rready) = '1' and rising_edge(aclk); rready <= '0'; check_axi_resp(bus_handle, rresp, expected_resp, "rresp"); + transaction_token := pop(transaction_token_queue); - if is_visible(bus_handle.p_logger, debug) then - debug(bus_handle.p_logger, + if is_visible(get_logger(bus_handle), debug) then + debug(get_logger(bus_handle), "Read 0x" & to_hstring(rdata) & " from address 0x" & to_hstring(araddr)); end if; @@ -130,9 +138,10 @@ begin wait until (bvalid and bready) = '1' and rising_edge(aclk); bready <= '0'; check_axi_resp(bus_handle, bresp, expected_resp, "bresp"); + transaction_token := pop(transaction_token_queue); - if is_visible(bus_handle.p_logger, debug) then - debug(bus_handle.p_logger, + if is_visible(get_logger(bus_handle), debug) then + debug(get_logger(bus_handle), "Wrote 0x" & to_hstring(wdata) & " to address 0x" & to_hstring(awaddr)); end if; diff --git a/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd b/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd index f497b0113..39b284ed9 100644 --- a/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_lite_master_pkg.vhd @@ -85,7 +85,7 @@ package body axi_lite_master_pkg is push_std_ulogic_vector(request_msg, expected_bresp); - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure read_axi_lite(signal net : inout network_t; @@ -100,7 +100,7 @@ package body axi_lite_master_pkg is full_address(address'length - 1 downto 0) := address; push_std_ulogic_vector(request_msg, full_address); push_std_ulogic_vector(request_msg, expected_rresp); - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure read_axi_lite(signal net : inout network_t; @@ -142,7 +142,7 @@ package body axi_lite_master_pkg is read_axi_lite(net, bus_handle, address, expected_rresp, data); if not std_match(data, edata) then - failure(bus_handle.p_logger, base_error); + failure(get_logger(bus_handle), base_error); end if; end procedure; diff --git a/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd b/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd index a6d23455e..ed7de25fb 100644 --- a/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_slave_pkg.vhd @@ -9,7 +9,10 @@ use ieee.std_logic_1164.all; use work.queue_pkg.all; use work.logger_pkg.all; +use work.checker_pkg.all; use work.memory_pkg.all; +use work.sync_pkg.all; +use work.vc_pkg.all; context work.com_context; use work.axi_statistics_pkg.all; @@ -18,6 +21,7 @@ package axi_slave_pkg is type axi_slave_t is record -- Private + p_std_cfg : std_cfg_t; p_initial_address_fifo_depth : positive; p_initial_write_response_fifo_depth : positive; p_initial_check_4kbyte_boundary : boolean; @@ -26,12 +30,11 @@ package axi_slave_pkg is p_initial_write_response_stall_probability : probability_t; p_initial_min_response_latency : delay_length; p_initial_max_response_latency : delay_length; - p_actor : actor_t; p_memory : memory_t; - p_logger : logger_t; end record; constant axi_slave_logger : logger_t := get_logger("vunit_lib:axi_slave_pkg"); + constant axi_slave_checker : checker_t := new_checker(axi_slave_logger); impure function new_axi_slave(memory : memory_t; actor : actor_t := null_actor; address_fifo_depth : positive := 1; @@ -42,10 +45,25 @@ package axi_slave_pkg is write_response_stall_probability : probability_t := 0.0; min_response_latency : delay_length := 0 ns; max_response_latency : delay_length := 0 ns; - logger : logger_t := axi_slave_logger) return axi_slave_t; + logger : logger_t := null_logger; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return axi_slave_t; - -- Get the logger used by the axi_slave - function get_logger(axi_slave : axi_slave_t) return logger_t; + function get_std_cfg(axi_slave : axi_slave_t) return std_cfg_t; + + impure function as_sync(axi_slave : axi_slave_t) return sync_handle_t; + + -- Return the actor used by the axi_slave + impure function get_actor(axi_slave : axi_slave_t) return actor_t; + + -- Return the logger used by the axi_slave + impure function get_logger(axi_slave : axi_slave_t) return logger_t; + + -- Return the checker used by the axi_slave + impure function get_checker(axi_slave : axi_slave_t) return checker_t; + + -- Return policy for handling unexpected messages to the actor + impure function unexpected_msg_type_policy(axi_slave : axi_slave_t) return unexpected_msg_type_policy_t; -- Set the maximum number address channel tokens that can be queued procedure set_address_fifo_depth(signal net : inout network_t; @@ -147,15 +165,14 @@ package body axi_slave_pkg is write_response_stall_probability : probability_t := 0.0; min_response_latency : delay_length := 0 ns; max_response_latency : delay_length := 0 ns; - logger : logger_t := axi_slave_logger) return axi_slave_t is - variable actor_tmp : actor_t := null_actor; + logger : logger_t := null_logger; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return axi_slave_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + axi_slave_logger, axi_slave_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin - if actor = null_actor then - actor_tmp := new_actor; - else - actor_tmp := actor; - end if; - return (p_actor => actor_tmp, + return (p_std_cfg => p_std_cfg, p_initial_address_fifo_depth => address_fifo_depth, p_initial_write_response_fifo_depth => write_response_fifo_depth, p_initial_check_4kbyte_boundary => check_4kbyte_boundary, @@ -164,13 +181,37 @@ package body axi_slave_pkg is p_initial_write_response_stall_probability => write_response_stall_probability, p_initial_min_response_latency => min_response_latency, p_initial_max_response_latency => max_response_latency, - p_memory => to_vc_interface(memory, logger), - p_logger => logger); + p_memory => to_vc_interface(memory, get_logger(p_std_cfg))); + end; + + function get_std_cfg(axi_slave : axi_slave_t) return std_cfg_t is + begin + return axi_slave.p_std_cfg; + end; + + impure function as_sync(axi_slave : axi_slave_t) return sync_handle_t is + begin + return get_actor(axi_slave.p_std_cfg); + end; + + impure function get_actor(axi_slave : axi_slave_t) return actor_t is + begin + return get_actor(axi_slave.p_std_cfg); + end; + + impure function get_logger(axi_slave : axi_slave_t) return logger_t is + begin + return get_logger(axi_slave.p_std_cfg); + end; + + impure function get_checker(axi_slave : axi_slave_t) return checker_t is + begin + return get_checker(axi_slave.p_std_cfg); end; - function get_logger(axi_slave : axi_slave_t) return logger_t is + impure function unexpected_msg_type_policy(axi_slave : axi_slave_t) return unexpected_msg_type_policy_t is begin - return axi_slave.p_logger; + return unexpected_msg_type_policy(axi_slave.p_std_cfg); end; procedure set_address_fifo_depth(signal net : inout network_t; @@ -181,7 +222,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_set_address_fifo_depth_msg); push(request_msg, depth); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_address_fifo_depth command"; end; @@ -193,7 +234,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_set_write_response_fifo_depth_msg); push(request_msg, depth); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_write_response_fifo_depth command"; end; @@ -205,7 +246,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_set_address_stall_probability_msg); push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_address_stall_probability command"; end; @@ -217,7 +258,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_set_data_stall_probability_msg); push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_data_stall_probability command"; end; @@ -228,7 +269,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_set_write_response_stall_probability_msg); push_real(request_msg, probability); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_write_response_stall_probability command"; end; @@ -240,7 +281,7 @@ package body axi_slave_pkg is begin request_msg := new_msg(axi_slave_configure_4kbyte_boundary_check_msg); push_boolean(request_msg, value); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on configure_4kbyte_boundary_check command"; end; @@ -253,7 +294,7 @@ package body axi_slave_pkg is request_msg := new_msg(axi_slave_set_response_latency_msg); push_time(request_msg, min_latency); push_time(request_msg, max_latency); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on set_response_latency command"; end; @@ -287,7 +328,7 @@ package body axi_slave_pkg is deallocate(stat); request_msg := new_msg(axi_slave_get_statistics_msg); push_boolean(request_msg, clear); - send(net, axi_slave.p_actor, request_msg); + send(net, get_actor(axi_slave.p_std_cfg), request_msg); receive_reply(net, request_msg, reply_msg); stat := (p_count_by_burst_length => pop_integer_vector_ptr_ref(reply_msg)); delete(request_msg); @@ -300,7 +341,7 @@ package body axi_slave_pkg is variable ack : boolean; begin request_msg := new_msg(axi_slave_enable_well_behaved_check_msg); - request(net, axi_slave.p_actor, request_msg, ack); + request(net, get_actor(axi_slave.p_std_cfg), request_msg, ack); assert ack report "Failed on msg_enable_well_behaved_check command"; end; end package body; diff --git a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd index 689b94d5b..ccce9dafa 100644 --- a/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_slave_private_pkg.vhd @@ -15,6 +15,7 @@ use std.textio.all; use work.axi_pkg.all; use work.queue_pkg.all; use work.integer_vector_ptr_pkg.all; +use work.vc_pkg.all; context work.vunit_context; context work.com_context; context work.vc_context; @@ -47,6 +48,10 @@ package axi_slave_private_pkg is max_id : natural; data : std_logic_vector); impure function get_actor return actor_t; + impure function get_logger return logger_t; + impure function get_checker return checker_t; + impure function unexpected_msg_type_policy return unexpected_msg_type_policy_t; + impure function get_std_cfg return std_cfg_t; procedure set_address_fifo_depth(depth : positive); procedure set_write_response_fifo_depth(depth : positive); @@ -162,7 +167,27 @@ package body axi_slave_private_pkg is impure function get_actor return actor_t is begin - return p_axi_slave.p_actor; + return get_actor(p_axi_slave.p_std_cfg); + end; + + impure function get_logger return logger_t is + begin + return get_logger(p_axi_slave.p_std_cfg); + end; + + impure function get_checker return checker_t is + begin + return get_checker(p_axi_slave.p_std_cfg); + end; + + impure function unexpected_msg_type_policy return unexpected_msg_type_policy_t is + begin + return unexpected_msg_type_policy(p_axi_slave.p_std_cfg); + end; + + impure function get_std_cfg return std_cfg_t is + begin + return p_axi_slave.p_std_cfg; end; procedure set_address_fifo_depth(depth : positive) is @@ -295,8 +320,8 @@ package body axi_slave_private_pkg is burst.index := get(p_id_indexes, burst.id); set(p_id_indexes, burst.id, burst.index + 1); - if is_visible(p_axi_slave.p_logger, debug) then - debug(p_axi_slave.p_logger, + if is_visible(get_logger(p_axi_slave), debug) then + debug(get_logger(p_axi_slave), "Got " & description & " " & describe_burst(burst) & LF & ax & "id = 0x" & to_hstring(axid) & LF & ax & "addr = 0x" & to_hstring(axaddr) & @@ -328,13 +353,13 @@ package body axi_slave_private_pkg is impure function pop_burst return axi_burst_t is constant burst : axi_burst_t := pop_axi_burst(p_burst_queue); begin - if is_visible(p_axi_slave.p_logger, debug) then + if is_visible(get_logger(p_axi_slave), debug) then case p_axi_slave_type is when write_slave => - debug(p_axi_slave.p_logger, + debug(get_logger(p_axi_slave), "Start accepting data for write burst " & describe_burst(burst)); when read_slave => - debug(p_axi_slave.p_logger, + debug(get_logger(p_axi_slave), "Start providing data for read burst " & describe_burst(burst)); end case; end if; @@ -366,8 +391,8 @@ package body axi_slave_private_pkg is impure function pop_resp return axi_burst_t is constant resp_burst : axi_burst_t := pop_axi_burst(p_resp_queue); begin - if is_visible(p_axi_slave.p_logger, debug) then - debug(p_axi_slave.p_logger, + if is_visible(get_logger(p_axi_slave), debug) then + debug(get_logger(p_axi_slave), "Providing write response for burst " & describe_burst(resp_burst)); end if; p_resp_queue_length := p_resp_queue_length - 1; @@ -376,13 +401,13 @@ package body axi_slave_private_pkg is procedure finish_burst(burst : axi_burst_t) is begin - if is_visible(p_axi_slave.p_logger, debug) then + if is_visible(get_logger(p_axi_slave), debug) then case p_axi_slave_type is when write_slave => - debug(p_axi_slave.p_logger, + debug(get_logger(p_axi_slave), "Accepted last data for write burst " & describe_burst(burst)); when read_slave => - debug(p_axi_slave.p_logger, + debug(get_logger(p_axi_slave), "Providing last data for read burst " & describe_burst(burst)); end case; end if; @@ -424,7 +449,7 @@ package body axi_slave_private_pkg is procedure fail(msg : string) is begin - failure(p_axi_slave.p_logger, msg); + check_failed(get_checker(p_axi_slave), msg, failure); end; procedure check_4kbyte_boundary(burst : axi_burst_t) is @@ -503,6 +528,8 @@ package body axi_slave_private_pkg is receive(net, self.get_actor, request_msg); msg_type := message_type(request_msg); + handle_sync_message(net, msg_type, request_msg); + if msg_type = axi_slave_set_address_fifo_depth_msg then self.set_address_fifo_depth(pop(request_msg)); acknowledge(net, request_msg, true); @@ -549,7 +576,7 @@ package body axi_slave_private_pkg is self.enable_well_behaved_check; acknowledge(net, request_msg, true); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, self.get_std_cfg); end if; delete(request_msg); @@ -573,8 +600,6 @@ package body axi_slave_private_pkg is return resp_to_string(resp) & "(" & to_string(resp) & ")"; end; begin - if got /= expected then - failure(bus_handle.p_logger, msg & " - Got AXI response " & describe(got) & " expected " & describe(expected)); - end if; + check(get_checker(bus_handle), got = expected, result("for " & msg & " - Got AXI response " & describe(got) & " expected " & describe(expected)), failure); end; end package body; diff --git a/vunit/vhdl/verification_components/src/axi_stream_master.vhd b/vunit/vhdl/verification_components/src/axi_stream_master.vhd index 21ba42a8d..cd8382c00 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_master.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_master.vhd @@ -14,16 +14,14 @@ use work.axi_stream_pkg.all; use work.axi_stream_private_pkg.all; use work.queue_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; library osvvm; use osvvm.RandomPkg.RandomPType; entity axi_stream_master is generic ( - master : axi_stream_master_t; - drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X'; - drive_invalid_val_user : std_logic := '0' + master : axi_stream_master_t ); port ( aclk : in std_logic; @@ -41,11 +39,8 @@ entity axi_stream_master is end entity; architecture a of axi_stream_master is - - constant notify_request_msg : msg_type_t := new_msg_type("notify request"); - constant message_queue : queue_t := new_queue; - signal notify_bus_process_done : std_logic := '0'; - + constant message_queue, transaction_token_queue : queue_t := new_queue; + signal notification : boolean := false; procedure drive_invalid_output(signal l_tdata : out std_logic_vector(data_length(master)-1 downto 0); signal l_tkeep : out std_logic_vector(data_length(master)/8-1 downto 0); signal l_tstrb : out std_logic_vector(data_length(master)/8-1 downto 0); @@ -54,51 +49,69 @@ architecture a of axi_stream_master is signal l_tuser : out std_logic_vector(user_length(master)-1 downto 0)) is begin - l_tdata <= (others => drive_invalid_val); - l_tkeep <= (others => drive_invalid_val); - l_tstrb <= (others => drive_invalid_val); - l_tid <= (others => drive_invalid_val); - l_tdest <= (others => drive_invalid_val); - l_tuser <= (others => drive_invalid_val_user); + l_tdata <= (others => master.p_drive_invalid_val); + l_tkeep <= (others => master.p_drive_invalid_val); + l_tstrb <= (others => master.p_drive_invalid_val); + l_tid <= (others => master.p_drive_invalid_val); + l_tdest <= (others => master.p_drive_invalid_val); + l_tuser <= (others => master.p_drive_invalid_val_user); end procedure; begin main : process variable request_msg : msg_t; - variable notify_msg : msg_t; variable msg_type : msg_type_t; + variable timestamp : time; + + procedure wait_on_pending_transactions is begin - receive(net, master.p_actor, request_msg); + if not is_empty(transaction_token_queue) then + wait on notification until is_empty(transaction_token_queue); + end if; + end; + begin + receive(net, get_actor(master.p_std_cfg), request_msg); msg_type := message_type(request_msg); if msg_type = stream_push_msg or msg_type = push_axi_stream_msg then push(message_queue, request_msg); + push(transaction_token_queue, true); elsif msg_type = wait_for_time_msg then - push(message_queue, request_msg); + wait_on_pending_transactions; + handle_wait_for_time(net, msg_type, request_msg); elsif msg_type = wait_until_idle_msg then - notify_msg := new_msg(notify_request_msg); - push(message_queue, notify_msg); - wait on notify_bus_process_done until is_empty(message_queue); + wait_on_pending_transactions; + loop + timestamp := now; + if master.p_monitor /= null_axi_stream_monitor then + wait_until_idle(net, as_sync(master.p_monitor)); + end if; + if master.p_protocol_checker /= null_axi_stream_protocol_checker then + wait_until_idle(net, as_sync(master.p_protocol_checker)); + end if; + exit when now = timestamp; + end loop; handle_wait_until_idle(net, msg_type, request_msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, master.p_std_cfg); end if; end process; bus_process : process variable msg : msg_t; variable msg_type : msg_type_t; + variable not_used : boolean; variable rnd : RandomPType; begin rnd.InitSeed(rnd'instance_name); loop - if drive_invalid then + if master.p_drive_invalid then drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser); end if; -- Wait for messages to arrive on the queue, posted by the process above - wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0'); + wait until (rising_edge(aclk) and not is_empty(message_queue)) or areset_n = '0'; if (areset_n = '0') then tvalid <= '0'; @@ -107,13 +120,7 @@ begin msg := pop(message_queue); msg_type := message_type(msg); - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - -- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment. - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then + if msg_type = stream_push_msg or msg_type = push_axi_stream_msg then drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser); -- stall according to probability configuration probability_stall_axi_stream(aclk, master, rnd); @@ -139,19 +146,19 @@ begin tdest <= (others => '0'); tuser <= (others => '0'); end if; - wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk); + wait until (rising_edge(aclk) and (tvalid and tready) = '1') or areset_n = '0'; tvalid <= '0'; tlast <= '0'; - else - unexpected_msg_type(msg_type); + + not_used := pop(transaction_token_queue); + notification <= not notification; + wait on notification; + else + unexpected_msg_type(msg_type, master.p_std_cfg); end if; delete(msg); end loop; - - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; end if; end loop; end process; @@ -173,6 +180,20 @@ begin tdest => tdest, tuser => tuser ); + + repeater : if master.p_use_default_monitor generate + process + constant subscriber : actor_t := new_actor; + variable msg : msg_t; + begin + subscribe(subscriber, get_actor(master.p_monitor.p_std_cfg)); + loop + receive(net, subscriber, msg); + publish(net, get_actor(master.p_std_cfg), msg); + end loop; + end process; + end generate; + end generate axi_stream_monitor_generate; axi_stream_protocol_checker_generate : if master.p_protocol_checker /= null_axi_stream_protocol_checker generate diff --git a/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd b/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd index 5ce59c117..96b57ce99 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_monitor.vhd @@ -10,6 +10,8 @@ use ieee.std_logic_1164.all; context work.vunit_context; context work.com_context; use work.axi_stream_pkg.all; +use work.sync_pkg.all; +use work.vc_pkg.all; entity axi_stream_monitor is generic ( @@ -31,6 +33,25 @@ end entity; architecture a of axi_stream_monitor is begin main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net,get_actor(monitor.p_std_cfg), request_msg); + msg_type := message_type(request_msg); + + handle_wait_for_time(net, msg_type, request_msg); + + if msg_type = wait_until_idle_msg then + if monitor.p_protocol_checker /= null_axi_stream_protocol_checker then + wait_until_idle(net, as_sync(monitor.p_protocol_checker)); + end if; + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type, monitor.p_std_cfg); + end if; + end process; + + publish_transactions : process variable msg : msg_t; variable axi_stream_transaction : axi_stream_transaction_t( tdata(tdata'range), @@ -43,8 +64,8 @@ begin begin wait until (tvalid and tready) = '1' and rising_edge(aclk); - if is_visible(monitor.p_logger, debug) then - debug(monitor.p_logger, "tdata: " & to_nibble_string(tdata) & " (" & to_integer_string(tdata) & ")" & ", tlast: " & to_string(tlast)); + if is_visible(get_logger(monitor.p_std_cfg), debug) then + debug(get_logger(monitor.p_std_cfg), "tdata: " & to_nibble_string(tdata) & " (" & to_integer_string(tdata) & ")" & ", tlast: " & to_string(tlast)); end if; axi_stream_transaction := ( @@ -58,7 +79,7 @@ begin ); msg := new_axi_stream_transaction_msg(axi_stream_transaction); - publish(net, monitor.p_actor, msg); + publish(net, get_actor(monitor.p_std_cfg), msg); end process; axi_stream_protocol_checker_generate : if monitor.p_protocol_checker /= null_axi_stream_protocol_checker generate diff --git a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd index a49ee63c7..f1683f901 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_pkg.vhd @@ -13,12 +13,12 @@ use work.check_pkg.all; use work.stream_master_pkg.all; use work.stream_slave_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; context work.vunit_context; context work.com_context; context work.data_types_context; package axi_stream_pkg is - type stall_config_t is record stall_probability : real range 0.0 to 1.0; min_stall_cycles : natural; @@ -34,270 +34,287 @@ package axi_stream_pkg is type axi_stream_component_type_t is (null_component, default_component, custom_component); type axi_stream_protocol_checker_t is record + p_std_cfg : std_cfg_t; p_type : axi_stream_component_type_t; - p_actor : actor_t; p_data_length : natural; p_id_length : natural; p_dest_length : natural; p_user_length : natural; - p_logger : logger_t; p_max_waits : natural; end record; constant null_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( + p_std_cfg => null_std_cfg, p_type => null_component, - p_actor => null_actor, p_data_length => 0, p_id_length => 0, p_dest_length => 0, p_user_length => 0, - p_logger => null_logger, p_max_waits => 0 - ); + ); -- The default protocol checker is used to specify that the checker -- configuration is defined by the parent component into which the checker is -- instantiated. constant default_axi_stream_protocol_checker : axi_stream_protocol_checker_t := ( + p_std_cfg => null_std_cfg, p_type => default_component, - p_actor => null_actor, p_data_length => 0, p_id_length => 0, p_dest_length => 0, p_user_length => 0, - p_logger => null_logger, p_max_waits => 0 - ); + ); type axi_stream_monitor_t is record + p_std_cfg : std_cfg_t; p_type : axi_stream_component_type_t; - p_actor : actor_t; p_data_length : natural; p_id_length : natural; p_dest_length : natural; p_user_length : natural; - p_logger : logger_t; p_protocol_checker : axi_stream_protocol_checker_t; end record; constant null_axi_stream_monitor : axi_stream_monitor_t := ( + p_std_cfg => null_std_cfg, p_type => null_component, - p_actor => null_actor, p_data_length => 0, p_id_length => 0, p_dest_length => 0, p_user_length => 0, - p_logger => null_logger, p_protocol_checker => null_axi_stream_protocol_checker - ); + ); -- The default monitor is used to specify that the monitor -- configuration is defined by the parent component into which the monitor is -- instantiated. constant default_axi_stream_monitor : axi_stream_monitor_t := ( + p_std_cfg => null_std_cfg, p_type => default_component, - p_actor => null_actor, p_data_length => 0, p_id_length => 0, p_dest_length => 0, p_user_length => 0, - p_logger => null_logger, p_protocol_checker => null_axi_stream_protocol_checker - ); + ); type axi_stream_master_t is record - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_stall_config : stall_config_t; - p_logger : logger_t; - p_monitor : axi_stream_monitor_t; - p_protocol_checker : axi_stream_protocol_checker_t; + p_std_cfg : std_cfg_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_stall_config : stall_config_t; + p_drive_invalid : boolean; + p_drive_invalid_val : std_logic; + p_drive_invalid_val_user : std_logic; + p_monitor : axi_stream_monitor_t; + p_use_default_monitor : boolean; + p_protocol_checker : axi_stream_protocol_checker_t; end record; constant null_axi_stream_master : axi_stream_master_t := ( - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_stall_config => null_stall_config, - p_logger => null_logger, - p_monitor => null_axi_stream_monitor, - p_protocol_checker => null_axi_stream_protocol_checker + p_std_cfg => null_std_cfg, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_stall_config => null_stall_config, + p_drive_invalid => true, + p_drive_invalid_val => 'X', + p_drive_invalid_val_user => '0', + p_monitor => null_axi_stream_monitor, + p_use_default_monitor => true, + p_protocol_checker => null_axi_stream_protocol_checker ); type axi_stream_slave_t is record - p_actor : actor_t; - p_data_length : natural; - p_id_length : natural; - p_dest_length : natural; - p_user_length : natural; - p_stall_config : stall_config_t; - p_logger : logger_t; - p_monitor : axi_stream_monitor_t; - p_protocol_checker : axi_stream_protocol_checker_t; + p_std_cfg : std_cfg_t; + p_data_length : natural; + p_id_length : natural; + p_dest_length : natural; + p_user_length : natural; + p_stall_config : stall_config_t; + p_monitor : axi_stream_monitor_t; + p_use_default_monitor : boolean; + p_protocol_checker : axi_stream_protocol_checker_t; end record; constant null_axi_stream_slave : axi_stream_slave_t := ( - p_actor => null_actor, - p_data_length => 0, - p_id_length => 0, - p_dest_length => 0, - p_user_length => 0, - p_stall_config => null_stall_config, - p_logger => null_logger, - p_monitor => null_axi_stream_monitor, - p_protocol_checker => null_axi_stream_protocol_checker + p_std_cfg => null_std_cfg, + p_data_length => 0, + p_id_length => 0, + p_dest_length => 0, + p_user_length => 0, + p_stall_config => null_stall_config, + p_monitor => null_axi_stream_monitor, + p_use_default_monitor => true, + p_protocol_checker => null_axi_stream_protocol_checker ); constant axi_stream_logger : logger_t := get_logger("vunit_lib:axi_stream_pkg"); constant axi_stream_checker : checker_t := new_checker(axi_stream_logger); impure function new_axi_stream_master( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - stall_config : stall_config_t := null_stall_config; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_master_t; + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + drive_invalid_val_user : std_logic := '0'; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_master_t; impure function new_axi_stream_slave( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - stall_config : stall_config_t := null_stall_config; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_slave_t; + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_slave_t; impure function new_axi_stream_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_monitor_t; + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_monitor_t; impure function new_axi_stream_protocol_checker( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - max_waits : natural := 16 - ) return axi_stream_protocol_checker_t; - - impure function data_length(master : axi_stream_master_t) return natural; - impure function data_length(slave : axi_stream_slave_t) return natural; - impure function data_length(monitor : axi_stream_monitor_t) return natural; + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + max_waits : natural := 16 + ) return axi_stream_protocol_checker_t; + + impure function data_length(master : axi_stream_master_t) return natural; + impure function data_length(slave : axi_stream_slave_t) return natural; + impure function data_length(monitor : axi_stream_monitor_t) return natural; impure function data_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function id_length(master : axi_stream_master_t) return natural; - impure function id_length(slave : axi_stream_slave_t) return natural; - impure function id_length(monitor : axi_stream_monitor_t) return natural; - impure function id_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function dest_length(master : axi_stream_master_t) return natural; - impure function dest_length(slave : axi_stream_slave_t) return natural; - impure function dest_length(monitor : axi_stream_monitor_t) return natural; + impure function id_length(master : axi_stream_master_t) return natural; + impure function id_length(slave : axi_stream_slave_t) return natural; + impure function id_length(monitor : axi_stream_monitor_t) return natural; + impure function id_length(protocol_checker : axi_stream_protocol_checker_t) return natural; + impure function dest_length(master : axi_stream_master_t) return natural; + impure function dest_length(slave : axi_stream_slave_t) return natural; + impure function dest_length(monitor : axi_stream_monitor_t) return natural; impure function dest_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function user_length(master : axi_stream_master_t) return natural; - impure function user_length(slave : axi_stream_slave_t) return natural; - impure function user_length(monitor : axi_stream_monitor_t) return natural; + impure function user_length(master : axi_stream_master_t) return natural; + impure function user_length(slave : axi_stream_slave_t) return natural; + impure function user_length(monitor : axi_stream_monitor_t) return natural; impure function user_length(protocol_checker : axi_stream_protocol_checker_t) return natural; - impure function as_stream(master : axi_stream_master_t) return stream_master_t; - impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t; - impure function as_sync(master : axi_stream_master_t) return sync_handle_t; - impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t; - - constant push_axi_stream_msg : msg_type_t := new_msg_type("push axi stream"); - constant pop_axi_stream_msg : msg_type_t := new_msg_type("pop axi stream"); - constant check_axi_stream_msg : msg_type_t := new_msg_type("check axi stream"); + impure function as_stream(master : axi_stream_master_t) return stream_master_t; + impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t; + impure function as_sync(master : axi_stream_master_t) return sync_handle_t; + impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t; + impure function as_sync(monitor : axi_stream_monitor_t) return sync_handle_t; + impure function as_sync(protocol_checker : axi_stream_protocol_checker_t) return sync_handle_t; + function get_std_cfg(master : axi_stream_master_t) return std_cfg_t; + function get_std_cfg(slave : axi_stream_slave_t) return std_cfg_t; + function get_std_cfg(monitor : axi_stream_monitor_t) return std_cfg_t; + function get_std_cfg(protocol_checker : axi_stream_protocol_checker_t) return std_cfg_t; + + constant push_axi_stream_msg : msg_type_t := new_msg_type("push axi stream"); + constant pop_axi_stream_msg : msg_type_t := new_msg_type("pop axi stream"); + constant check_axi_stream_msg : msg_type_t := new_msg_type("check axi stream"); constant axi_stream_transaction_msg : msg_type_t := new_msg_type("axi stream transaction"); alias axi_stream_reference_t is msg_t; procedure push_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_master_t; - tdata : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := "" + signal net : inout network_t; + axi_stream : axi_stream_master_t; + tdata : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := "" ); -- Blocking: pop a value from the axi stream procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector ); procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic ); -- Non-blocking: pop a value from the axi stream to be read in the future - procedure pop_axi_stream(signal net : inout network_t; - axi_stream : axi_stream_slave_t; + procedure pop_axi_stream(signal net : inout network_t; + axi_stream : axi_stream_slave_t; variable reference : inout axi_stream_reference_t); -- Blocking: Wait for reply to non-blocking pop procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector ); procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic ); -- Blocking: read axi stream and check result against expected value procedure check_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - expected : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := ""; - msg : string := ""; - blocking : boolean := true + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + expected : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := ""; + msg : string := ""; + blocking : boolean := true ); type axi_stream_transaction_t is record @@ -312,45 +329,45 @@ package axi_stream_pkg is procedure push_axi_stream_transaction(msg : msg_t; axi_stream_transaction : axi_stream_transaction_t); procedure pop_axi_stream_transaction( - constant msg : in msg_t; + constant msg : in msg_t; variable axi_stream_transaction : out axi_stream_transaction_t - ); + ); impure function new_axi_stream_transaction_msg( axi_stream_transaction : axi_stream_transaction_t - ) return msg_t; + ) return msg_t; procedure handle_axi_stream_transaction( variable msg_type : inout msg_type_t; variable msg : inout msg_t; - variable axi_transaction : out axi_stream_transaction_t); + variable axi_transaction : out axi_stream_transaction_t); function new_stall_config( stall_probability : real range 0.0 to 1.0; min_stall_cycles : natural; max_stall_cycles : natural - ) return stall_config_t; + ) return stall_config_t; end package; package body axi_stream_pkg is - impure function get_valid_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - monitor : axi_stream_monitor_t; - parent_component : string + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t; + actor : actor_t; + checker : checker_t; + monitor : axi_stream_monitor_t; + parent_component : string ) return axi_stream_monitor_t is begin if monitor = null_axi_stream_monitor then return monitor; elsif monitor = default_axi_stream_monitor then check(actor /= null_actor, "A valid actor is needed to create a default monitor"); - return new_axi_stream_monitor(data_length, id_length, dest_length, user_length, logger, actor); + return new_axi_stream_monitor(data_length, id_length, dest_length, user_length, logger, new_actor(name(actor) & ":monitor"), checker); else check_equal(axi_stream_checker, monitor.p_data_length, data_length, "Data length of monitor doesn't match that of the " & parent_component); check_equal(axi_stream_checker, monitor.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); @@ -362,19 +379,21 @@ package body axi_stream_pkg is impure function get_valid_protocol_checker( data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; logger : logger_t; actor : actor_t; + checker : checker_t; protocol_checker : axi_stream_protocol_checker_t; parent_component : string - ) return axi_stream_protocol_checker_t is + ) return axi_stream_protocol_checker_t is begin if protocol_checker = null_axi_stream_protocol_checker then return protocol_checker; elsif protocol_checker = default_axi_stream_protocol_checker then - return new_axi_stream_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor); + check(actor /= null_actor, "A valid actor is needed to create a default protocol checker"); + return new_axi_stream_protocol_checker(data_length, id_length, dest_length, user_length, logger, new_actor(name(actor) & ":protocol_checker"), checker); else check_equal(axi_stream_checker, protocol_checker.p_data_length, data_length, "Data length of protocol checker doesn't match that of the " & parent_component); check_equal(axi_stream_checker, protocol_checker.p_id_length, id_length, "ID length of monitor doesn't match that of the " & parent_component); @@ -385,107 +404,129 @@ package body axi_stream_pkg is end; impure function new_axi_stream_master( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - stall_config : stall_config_t := null_stall_config; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker - ) return axi_stream_master_t is - variable p_actor : actor_t; + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + drive_invalid_val_user : std_logic := '0'; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + ) return axi_stream_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + axi_stream_logger, axi_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); variable p_monitor : axi_stream_monitor_t; variable p_protocol_checker : axi_stream_protocol_checker_t; begin - p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "master"); - p_actor := actor when actor /= null_actor else new_actor; - p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "master"); + p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, checker, monitor, "master"); + p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, checker, protocol_checker, "master"); - return (p_actor => p_actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_stall_config => stall_config, - p_logger => logger, - p_monitor => p_monitor, - p_protocol_checker => p_protocol_checker); + return ( + p_std_cfg => p_std_cfg, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_stall_config => stall_config, + p_drive_invalid => drive_invalid, + p_drive_invalid_val => drive_invalid_val, + p_drive_invalid_val_user => drive_invalid_val_user, + p_monitor => p_monitor, + p_use_default_monitor => monitor = default_axi_stream_monitor, + p_protocol_checker => p_protocol_checker); end; impure function new_axi_stream_slave( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - stall_config : stall_config_t := null_stall_config; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - monitor : axi_stream_monitor_t := null_axi_stream_monitor; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + stall_config : stall_config_t := null_stall_config; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + monitor : axi_stream_monitor_t := null_axi_stream_monitor; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker ) return axi_stream_slave_t is - variable p_actor : actor_t; + constant p_std_cfg : std_cfg_t := create_std_cfg( + axi_stream_logger, axi_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); variable p_monitor : axi_stream_monitor_t; variable p_protocol_checker : axi_stream_protocol_checker_t; begin - p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, monitor, "slave"); - p_actor := actor when actor /= null_actor else new_actor; - p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "slave"); + p_monitor := get_valid_monitor(data_length, id_length, dest_length, user_length, logger, actor, checker, monitor, "slave"); + p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, checker, protocol_checker, "slave"); - return (p_actor => p_actor, - p_data_length => data_length, - p_id_length => id_length, - p_dest_length => dest_length, - p_user_length => user_length, - p_stall_config => stall_config, - p_logger => logger, - p_monitor => p_monitor, - p_protocol_checker => p_protocol_checker); + return ( + p_std_cfg => p_std_cfg, + p_data_length => data_length, + p_id_length => id_length, + p_dest_length => dest_length, + p_user_length => user_length, + p_stall_config => stall_config, + p_monitor => p_monitor, + p_use_default_monitor => monitor = default_axi_stream_monitor, + p_protocol_checker => p_protocol_checker); end; impure function new_axi_stream_monitor( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t; - protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker ) return axi_stream_monitor_t is constant p_protocol_checker : axi_stream_protocol_checker_t := get_valid_protocol_checker( - data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "monitor" - ); + data_length, id_length, dest_length, user_length, logger, actor, checker, protocol_checker, "monitor" + ); + constant p_std_cfg : std_cfg_t := create_std_cfg( + axi_stream_logger, axi_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin return ( + p_std_cfg => p_std_cfg, p_type => custom_component, - p_actor => actor, p_data_length => data_length, p_id_length => id_length, p_dest_length => dest_length, p_user_length => user_length, - p_logger => logger, p_protocol_checker => p_protocol_checker); end; impure function new_axi_stream_protocol_checker( - data_length : natural; - id_length : natural := 0; - dest_length : natural := 0; - user_length : natural := 0; - logger : logger_t := axi_stream_logger; - actor : actor_t := null_actor; - max_waits : natural := 16 + data_length : natural; + id_length : natural := 0; + dest_length : natural := 0; + user_length : natural := 0; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail; + max_waits : natural := 16 ) return axi_stream_protocol_checker_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + axi_stream_logger, axi_stream_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin return ( + p_std_cfg => p_std_cfg, p_type => custom_component, - p_actor => actor, p_data_length => data_length, p_id_length => id_length, p_dest_length => dest_length, p_user_length => user_length, - p_logger => logger, p_max_waits => max_waits); end; @@ -524,7 +565,7 @@ package body axi_stream_pkg is return monitor.p_id_length; end; - impure function id_length(protocol_checker: axi_stream_protocol_checker_t) return natural is + impure function id_length(protocol_checker : axi_stream_protocol_checker_t) return natural is begin return protocol_checker.p_id_length; end; @@ -534,7 +575,7 @@ package body axi_stream_pkg is return master.p_dest_length; end; - impure function dest_length(slave: axi_stream_slave_t) return natural is + impure function dest_length(slave : axi_stream_slave_t) return natural is begin return slave.p_dest_length; end; @@ -571,42 +612,72 @@ package body axi_stream_pkg is impure function as_stream(master : axi_stream_master_t) return stream_master_t is begin - return (p_actor => master.p_actor); + return (p_std_cfg => master.p_std_cfg); end; impure function as_stream(slave : axi_stream_slave_t) return stream_slave_t is begin - return (p_actor => slave.p_actor); + return (p_std_cfg => slave.p_std_cfg); end; impure function as_sync(master : axi_stream_master_t) return sync_handle_t is begin - return master.p_actor; + return get_actor(master.p_std_cfg); end; impure function as_sync(slave : axi_stream_slave_t) return sync_handle_t is begin - return slave.p_actor; + return get_actor(slave.p_std_cfg); + end; + + impure function as_sync(monitor : axi_stream_monitor_t) return sync_handle_t is + begin + return get_actor(monitor.p_std_cfg); + end; + + impure function as_sync(protocol_checker : axi_stream_protocol_checker_t) return sync_handle_t is + begin + return get_actor(protocol_checker.p_std_cfg); + end; + + function get_std_cfg(master : axi_stream_master_t) return std_cfg_t is + begin + return master.p_std_cfg; + end; + + function get_std_cfg(slave : axi_stream_slave_t) return std_cfg_t is + begin + return slave.p_std_cfg; + end; + + function get_std_cfg(monitor : axi_stream_monitor_t) return std_cfg_t is + begin + return monitor.p_std_cfg; + end; + + function get_std_cfg(protocol_checker : axi_stream_protocol_checker_t) return std_cfg_t is + begin + return protocol_checker.p_std_cfg; end; procedure push_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_master_t; - tdata : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := "" + signal net : inout network_t; + axi_stream : axi_stream_master_t; + tdata : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := "" ) is - variable msg : msg_t := new_msg(push_axi_stream_msg); - variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); + variable msg : msg_t := new_msg(push_axi_stream_msg); + variable normalized_data : std_logic_vector(data_length(axi_stream)-1 downto 0) := (others => '0'); variable normalized_keep : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); variable normalized_strb : std_logic_vector(data_length(axi_stream)/8-1 downto 0) := (others => '0'); - variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); - variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_id : std_logic_vector(id_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_dest : std_logic_vector(dest_length(axi_stream)-1 downto 0) := (others => '0'); + variable normalized_user : std_logic_vector(user_length(axi_stream)-1 downto 0) := (others => '0'); begin normalized_data(tdata'length-1 downto 0) := tdata; push_std_ulogic_vector(msg, normalized_data); @@ -615,33 +686,33 @@ package body axi_stream_pkg is push_std_ulogic_vector(msg, normalized_keep); normalized_strb(tstrb'length-1 downto 0) := tstrb; push_std_ulogic_vector(msg, normalized_strb); - normalized_id(tid'length-1 downto 0) := tid; + normalized_id(tid'length-1 downto 0) := tid; push_std_ulogic_vector(msg, normalized_id); normalized_dest(tdest'length-1 downto 0) := tdest; push_std_ulogic_vector(msg, normalized_dest); normalized_user(tuser'length-1 downto 0) := tuser; push_std_ulogic_vector(msg, normalized_user); - send(net, axi_stream.p_actor, msg); + send(net, get_actor(axi_stream.p_std_cfg), msg); end; - procedure pop_axi_stream(signal net : inout network_t; - axi_stream : axi_stream_slave_t; + procedure pop_axi_stream(signal net : inout network_t; + axi_stream : axi_stream_slave_t; variable reference : inout axi_stream_reference_t) is begin reference := new_msg(pop_axi_stream_msg); - send(net, axi_stream.p_actor, reference); + send(net, get_actor(axi_stream.p_std_cfg), reference); end; procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector ) is variable reply_msg : msg_t; begin @@ -662,10 +733,10 @@ package body axi_stream_pkg is end; procedure await_pop_axi_stream_reply( - signal net : inout network_t; - variable reference : inout axi_stream_reference_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic + signal net : inout network_t; + variable reference : inout axi_stream_reference_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic ) is variable reply_msg : msg_t; begin @@ -681,15 +752,15 @@ package body axi_stream_pkg is end; procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic; - variable tkeep : out std_logic_vector; - variable tstrb : out std_logic_vector; - variable tid : out std_logic_vector; - variable tdest : out std_logic_vector; - variable tuser : out std_logic_vector + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic; + variable tkeep : out std_logic_vector; + variable tstrb : out std_logic_vector; + variable tid : out std_logic_vector; + variable tdest : out std_logic_vector; + variable tuser : out std_logic_vector ) is variable reference : axi_stream_reference_t; begin @@ -698,10 +769,10 @@ package body axi_stream_pkg is end; procedure pop_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - variable tdata : out std_logic_vector; - variable tlast : out std_logic + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + variable tdata : out std_logic_vector; + variable tlast : out std_logic ) is variable reference : axi_stream_reference_t; begin @@ -710,17 +781,17 @@ package body axi_stream_pkg is end; procedure check_axi_stream( - signal net : inout network_t; - axi_stream : axi_stream_slave_t; - expected : std_logic_vector; - tlast : std_logic := '1'; - tkeep : std_logic_vector := ""; - tstrb : std_logic_vector := ""; - tid : std_logic_vector := ""; - tdest : std_logic_vector := ""; - tuser : std_logic_vector := ""; - msg : string := ""; - blocking : boolean := true + signal net : inout network_t; + axi_stream : axi_stream_slave_t; + expected : std_logic_vector; + tlast : std_logic := '1'; + tkeep : std_logic_vector := ""; + tstrb : std_logic_vector := ""; + tid : std_logic_vector := ""; + tdest : std_logic_vector := ""; + tuser : std_logic_vector := ""; + msg : string := ""; + blocking : boolean := true ) is variable got_tdata : std_logic_vector(data_length(axi_stream)-1 downto 0); variable got_tlast : std_logic; @@ -759,7 +830,7 @@ package body axi_stream_pkg is push_std_ulogic_vector(check_msg, tid); push_std_ulogic_vector(check_msg, tdest); push_std_ulogic_vector(check_msg, tuser); - send(net, axi_stream.p_actor, check_msg); + send(net, get_actor(axi_stream.p_std_cfg), check_msg); end if; end procedure; @@ -775,9 +846,9 @@ package body axi_stream_pkg is end; procedure pop_axi_stream_transaction( - constant msg : in msg_t; + constant msg : in msg_t; variable axi_stream_transaction : out axi_stream_transaction_t - ) is + ) is begin axi_stream_transaction.tdata := pop_std_ulogic_vector(msg); axi_stream_transaction.tlast := pop_boolean(msg); @@ -790,7 +861,7 @@ package body axi_stream_pkg is impure function new_axi_stream_transaction_msg( axi_stream_transaction : axi_stream_transaction_t - ) return msg_t is + ) return msg_t is variable msg : msg_t; begin msg := new_msg(axi_stream_transaction_msg); @@ -801,7 +872,7 @@ package body axi_stream_pkg is procedure handle_axi_stream_transaction( variable msg_type : inout msg_type_t; variable msg : inout msg_t; - variable axi_transaction : out axi_stream_transaction_t) is + variable axi_transaction : out axi_stream_transaction_t) is begin if msg_type = axi_stream_transaction_msg then handle_message(msg_type); @@ -814,7 +885,7 @@ package body axi_stream_pkg is stall_probability : real range 0.0 to 1.0; min_stall_cycles : natural; max_stall_cycles : natural) return stall_config_t is - variable stall_config : stall_config_t; + variable stall_config : stall_config_t; begin stall_config := ( stall_probability => stall_probability, diff --git a/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd b/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd index e37d0cd58..100859f89 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_protocol_checker.vhd @@ -12,84 +12,117 @@ use std.textio.all; context work.vunit_context; context work.com_context; +use work.sync_pkg.all; +use work.vc_pkg.all; use work.axi_stream_pkg.all; entity axi_stream_protocol_checker is - generic ( + generic( protocol_checker : axi_stream_protocol_checker_t); - port ( + port( aclk : in std_logic; - areset_n : in std_logic := '1'; + areset_n : in std_logic := '1'; tvalid : in std_logic; - tready : in std_logic := '1'; + tready : in std_logic := '1'; tdata : in std_logic_vector(data_length(protocol_checker) - 1 downto 0); - tlast : in std_logic := '1'; - tkeep : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); - tstrb : in std_logic_vector(data_length(protocol_checker)/8-1 downto 0) := (others => '0'); - tid : in std_logic_vector(id_length(protocol_checker)-1 downto 0) := (others => '0'); - tdest : in std_logic_vector(dest_length(protocol_checker)-1 downto 0) := (others => '0'); - tuser : in std_logic_vector(user_length(protocol_checker)-1 downto 0) := (others => '0') - ); + tlast : in std_logic := '1'; + tkeep : in std_logic_vector(data_length(protocol_checker) / 8 - 1 downto 0) := (others => '0'); + tstrb : in std_logic_vector(data_length(protocol_checker) / 8 - 1 downto 0) := (others => '0'); + tid : in std_logic_vector(id_length(protocol_checker) - 1 downto 0) := (others => '0'); + tdest : in std_logic_vector(dest_length(protocol_checker) - 1 downto 0) := (others => '0'); + tuser : in std_logic_vector(user_length(protocol_checker) - 1 downto 0) := (others => '0') + ); end entity; architecture a of axi_stream_protocol_checker is - constant rule1_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 1"); - constant rule2_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 2"); - constant rule3_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 3"); - constant rule4_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 4"); - constant rule5_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 5"); - constant rule6_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 6"); - constant rule7_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 7"); - constant rule8_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 8"); - constant rule9_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 9"); - constant rule10_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 10"); - constant rule11_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 11"); - constant rule12_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 12"); - constant rule13_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 13"); - constant rule14_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 14"); - constant rule15_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 15"); - constant rule16_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 16"); - constant rule17_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 17"); - constant rule18_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 18"); - constant rule19_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 19"); - constant rule20_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 20"); - constant rule21_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 21"); - constant rule22_checker : checker_t := new_checker(get_name(protocol_checker.p_logger) & ":rule 22"); - - signal handshake_is_not_x : std_logic; - signal enable_rule1_check : std_logic; - signal enable_rule2_check : std_logic; + type checker_vec_t is array (positive range <>) of checker_t; + + impure function create_checkers(n_rules : positive) return checker_vec_t is + variable checkers : checker_vec_t(1 to n_rules); + begin + for rule in 1 to n_rules loop + checkers(rule) := new_checker(get_name(get_logger(protocol_checker.p_std_cfg)) & ":rule " & to_string(rule)); + end loop; + + return checkers; + end; + + constant n_rules : positive := 23; + constant rule_checkers : checker_vec_t := create_checkers(n_rules); + + constant active_streams : integer_array_t := new_1d(length => 2 ** tid'length); + + signal handshake_is_not_x : std_logic; + signal enable_rule1_check : std_logic; + signal enable_rule2_check : std_logic; signal enable_rule11_check : std_logic; signal enable_rule12_check : std_logic; signal enable_rule13_check : std_logic; signal enable_rule14_check : std_logic; signal enable_rule15_check : std_logic; - signal rule20_check_value : std_logic; + signal rule20_check_value : std_logic; - signal areset_n_d : std_logic := '0'; - signal areset_rose : std_logic; - signal not_tvalid : std_logic; + signal areset_n_d : std_logic := '1'; + signal areset : std_logic; + signal areset_rose : std_logic; + signal tvalid_low, tvalid_not_high : std_logic; begin + + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + + procedure wait_until_all_streams_have_completed is + variable found_active_streams : boolean; + begin + loop + found_active_streams := false; + for i in 0 to length(active_streams) - 1 loop + found_active_streams := get(active_streams, i) /= 0; + exit when found_active_streams; + end loop; + + if not found_active_streams then + return; + end if; + + wait until rising_edge(aclk); + end loop; + end; + begin + receive(net, get_actor(protocol_checker.p_std_cfg), request_msg); + msg_type := message_type(request_msg); + + handle_wait_for_time(net, msg_type, request_msg); + + if msg_type = wait_until_idle_msg then + wait_until_all_streams_have_completed; + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type, protocol_checker.p_std_cfg); + end if; + end process; + handshake_is_not_x <= '1' when not is_x(tvalid) and not is_x(tready) else '0'; -- AXI4STREAM_ERRM_TDATA_STABLE TDATA remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule1_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdata) else '0'; check_stable( - rule1_checker, aclk, enable_rule1_check, tvalid, tready, tdata, + rule_checkers(1), aclk, enable_rule1_check, tvalid, tready, tdata, result("for tdata while waiting for tready")); -- AXI4STREAM_ERRM_TLAST_STABLE TLAST remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule2_check <= '1' when (handshake_is_not_x = '1') and not is_x(tlast) else '0'; check_stable( - rule2_checker, aclk, enable_rule2_check, tvalid, tready, tlast, + rule_checkers(2), aclk, enable_rule2_check, tvalid, tready, tlast, result("for tlast while waiting for tready")); -- AXI4STREAM_ERRM_TVALID_STABLE When TVALID is asserted, then it must remain -- asserted until TREADY is HIGH check_stable( - rule3_checker, aclk, handshake_is_not_x, tvalid, tready, tvalid, + rule_checkers(3), aclk, handshake_is_not_x, tvalid, tready, tvalid, result("for tvalid while waiting for tready")); -- AXI4STREAM_RECS_TREADY_MAX_WAIT Recommended that TREADY is asserted within @@ -102,34 +135,32 @@ begin wait until rising_edge(aclk); n_clock_cycles := n_clock_cycles + 1; end loop; - check(rule4_checker, + check(rule_checkers(4), n_clock_cycles <= protocol_checker.p_max_waits, - result("for performance - tready active " & to_string(n_clock_cycles) & - " clock cycles after tvalid. Expected <= " & to_string(protocol_checker.p_max_waits) & " clock cycles."), + result("for performance - tready active " & to_string(n_clock_cycles) & " clock cycles after tvalid. Expected <= " & to_string(protocol_checker.p_max_waits) & " clock cycles."), level => warning); n_clock_cycles := 0; end process; -- AXI4STREAM_ERRM_TDATA_X A value of X on TDATA is not permitted when TVALID -- is HIGH - check_not_unknown(rule5_checker, aclk, tvalid, tdata, result("for tdata when tvalid is high")); + check_not_unknown(rule_checkers(5), aclk, tvalid, tdata, result("for tdata when tvalid is high")); -- AXI4STREAM_ERRM_TLAST_X A value of X on TLAST is not permitted when TVALID -- is HIGH - check_not_unknown(rule6_checker, aclk, tvalid, tlast, result("for tlast when tvalid is high")); + check_not_unknown(rule_checkers(6), aclk, tvalid, tlast, result("for tlast when tvalid is high")); -- AXI4STREAM_ERRM_TVALID_X A value of X on TVALID is not permitted when not -- in reset - check_not_unknown(rule7_checker, aclk, areset_n, tvalid, result("for tvalid when not in reset")); + check_not_unknown(rule_checkers(7), aclk, areset_n, tvalid, result("for tvalid when not in reset")); -- AXI4STREAM_ERRS_TREADY_X A value of X on TREADY is not permitted when not -- in reset - check_not_unknown(rule8_checker, aclk, areset_n, tready, result("for tready when not in reset")); + check_not_unknown(rule_checkers(8), aclk, areset_n, tready, result("for tready when not in reset")); -- AXI4STREAM_ERRM_STREAM_ALL_DONE_EOS At the end of simulation, all streams have had -- their corresponding TLAST transfer check_complete_packets : block is - constant active_streams : integer_array_t := new_1d(length => 2 ** tid'length); begin assert tid'length <= 8 report "tid must not be more than 8 bits (maximum recommendation)" severity failure; @@ -153,9 +184,9 @@ begin wait_until(runner, test_runner_cleanup); if tid'length = 0 then - check(rule9_checker, get(active_streams, 0) = 0, result("for packet completion.")); + check(rule_checkers(9), get(active_streams, 0) = 0, result("for packet completion.")); else - for i in 0 to 2 * tid'length - 1 loop + for i in 0 to 2 ** tid'length - 1 loop if get(active_streams, i) /= 0 then if incomplete_streams = null then write(incomplete_streams, to_string(i)); @@ -166,10 +197,9 @@ begin end loop; if incomplete_streams /= null then - check_failed(rule9_checker, result("for packet completion for the following streams: " & - incomplete_streams.all & ".")); + check_failed(rule_checkers(9), result("for packet completion for the following streams: " & incomplete_streams.all & ".")); else - check_passed(rule9_checker, result("for packet completion.")); + check_passed(rule_checkers(9), result("for packet completion.")); end if; end if; @@ -180,83 +210,88 @@ begin -- AXI4STREAM_ERRM_TUSER_X A value of X on TUSER is not permitted when not in reset -- is HIGH - check_not_unknown(rule10_checker, aclk, areset_n, tuser, result("for tuser when areset_n is high")); + check_not_unknown(rule_checkers(10), aclk, areset_n, tuser, result("for tuser when areset_n is high")); -- AXI4STREAM_ERRM_TUSER_STABLE TUSER payload signals must remain constant while TVALID is asserted, -- and TREADY is de-asserted enable_rule11_check <= '1' when (handshake_is_not_x = '1') and not is_x(tuser) else '0'; check_stable( - rule11_checker, aclk, enable_rule11_check, tvalid, tready, tuser, + rule_checkers(11), aclk, enable_rule11_check, tvalid, tready, tuser, result("for tuser while waiting for tready")); -- AXI4STREAM_ERRM_TID_STABLE TID remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule12_check <= '1' when (handshake_is_not_x = '1') and not is_x(tid) else '0'; check_stable( - rule12_checker, aclk, enable_rule12_check, tvalid, tready, tid, + rule_checkers(12), aclk, enable_rule12_check, tvalid, tready, tid, result("for tid while waiting for tready")); -- AXI4STREAM_ERRM_TDEST_STABLE TDEST remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule13_check <= '1' when (handshake_is_not_x = '1') and not is_x(tdest) else '0'; check_stable( - rule13_checker, aclk, enable_rule13_check, tvalid, tready, tdest, + rule_checkers(13), aclk, enable_rule13_check, tvalid, tready, tdest, result("for tdest while waiting for tready")); -- AXI4STREAM_ERRM_TSTRB_STABLE TSTRB remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule14_check <= '1' when (handshake_is_not_x = '1') and not is_x(tstrb) else '0'; check_stable( - rule14_checker, aclk, enable_rule14_check, tvalid, tready, tstrb, + rule_checkers(14), aclk, enable_rule14_check, tvalid, tready, tstrb, result("for tstrb while waiting for tready")); -- AXI4STREAM_ERRM_TKEEP_STABLE TKEEP remains stable when TVALID is asserted, -- and TREADY is LOW enable_rule15_check <= '1' when (handshake_is_not_x = '1') and not is_x(tkeep) else '0'; check_stable( - rule15_checker, aclk, enable_rule15_check, tvalid, tready, tkeep, + rule_checkers(15), aclk, enable_rule15_check, tvalid, tready, tkeep, result("for tkeep while waiting for tready")); -- AXI4STREAM_ERRM_TID_X A value of X on TID is not permitted when TVALID -- is HIGH - check_not_unknown(rule16_checker, aclk, tvalid, tid, result("for tid when tvalid is high")); + check_not_unknown(rule_checkers(16), aclk, tvalid, tid, result("for tid when tvalid is high")); -- AXI4STREAM_ERRM_TDEST_X A value of X on TDEST is not permitted when TVALID -- is HIGH - check_not_unknown(rule17_checker, aclk, tvalid, tdest, result("for tdest when tvalid is high")); + check_not_unknown(rule_checkers(17), aclk, tvalid, tdest, result("for tdest when tvalid is high")); -- AXI4STREAM_ERRM_TSTRB_X A value of X on TSTRB is not permitted when TVALID -- is HIGH - check_not_unknown(rule18_checker, aclk, tvalid, tstrb, result("for tstrb when tvalid is high")); + check_not_unknown(rule_checkers(18), aclk, tvalid, tstrb, result("for tstrb when tvalid is high")); -- AXI4STREAM_ERRM_TKEEP_X A value of X on TKEEP is not permitted when TVALID -- is HIGH - check_not_unknown(rule19_checker, aclk, tvalid, tkeep, result("for tkeep when tvalid is high")); + check_not_unknown(rule_checkers(19), aclk, tvalid, tkeep, result("for tkeep when tvalid is high")); -- AXI4STREAM_ERRM_TKEEP_TSTRB If TKEEP is de-asserted, then TSTRB must also be de-asserted -- eschmidscs: Binding this to tvalid. ARM does not include that, but makes more sense this way? - rule20_check_value <= not(or(((not tkeep) and tstrb))); - check_true(rule20_checker, aclk, tvalid, rule20_check_value, result("for tstrb de-asserted when tkeep de-asserted")); + rule20_check_value <= not (or(((not tkeep) and tstrb))); + check_true(rule_checkers(20), aclk, tvalid, rule20_check_value, result("for tstrb de-asserted when tkeep de-asserted")); -- AXI4STREAM_AUXM_TID_TDTEST_WIDTH The value of ID_WIDTH + DEST_WIDTH must not exceed 24 -- eschmidscs: Must wait a short while to allow testing of the rule. process begin wait for 1 ps; - check_true(rule21_checker, tid'length + tdest'length <= 24, result("for tid width and tdest width together must be less than 25")); + check_true(rule_checkers(21), tid'length + tdest'length <= 24, result("for tid width and tdest width together must be less than 25")); wait; end process; -- AXI4STREAM_ERRM_TVALID_RESET TVALID is LOW for the first cycle after ARESETn goes HIGH - process (aclk) is + process(aclk) is begin if rising_edge(aclk) then areset_n_d <= areset_n; end if; end process; - areset_rose <= areset_n and not areset_n_d; - not_tvalid <= not tvalid; - check_implication(rule22_checker, aclk, areset_n, areset_rose, not_tvalid, result("for tvalid de-asserted after reset release")); + areset_rose <= to_x01(areset_n and not areset_n_d); + tvalid_low <= not tvalid; + check_implication(rule_checkers(22), aclk, areset_n, areset_rose, tvalid_low, result("for tvalid de-asserted after reset release")); + + -- Check that tvalid stops being asserted asynchronously when areset_n is asserted + areset <= not areset_n; + tvalid_not_high <= '1' when to_x01(tvalid) /= '1' else '0'; + check_implication(rule_checkers(23), aclk, areset, areset, tvalid_not_high, result("for tvalid de-asserted asynchronously when areset_n is asserted")); -- for * being DATA, KEEP, STRB, ID, DEST or USER -- AXI4STREAM_ERRM_T*_TIEOFF T* must be stable while *_WIDTH has been set to zero diff --git a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd index 59451f142..4f8cfe2c2 100644 --- a/vunit/vhdl/verification_components/src/axi_stream_slave.vhd +++ b/vunit/vhdl/verification_components/src/axi_stream_slave.vhd @@ -13,6 +13,7 @@ use work.stream_slave_pkg.all; use work.axi_stream_pkg.all; use work.axi_stream_private_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; use work.string_ptr_pkg.all; library osvvm; @@ -22,49 +23,65 @@ entity axi_stream_slave is generic ( slave : axi_stream_slave_t); port ( - aclk : in std_logic; - areset_n : in std_logic := '1'; - tvalid : in std_logic; - tready : out std_logic := '0'; - tdata : in std_logic_vector(data_length(slave)-1 downto 0); - tlast : in std_logic := '1'; - tkeep : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); - tstrb : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); - tid : in std_logic_vector(id_length(slave)-1 downto 0) := (others => '0'); - tdest : in std_logic_vector(dest_length(slave)-1 downto 0) := (others => '0'); - tuser : in std_logic_vector(user_length(slave)-1 downto 0) := (others => '0') - ); + aclk : in std_logic; + areset_n : in std_logic := '1'; + tvalid : in std_logic; + tready : out std_logic := '0'; + tdata : in std_logic_vector(data_length(slave)-1 downto 0); + tlast : in std_logic := '1'; + tkeep : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); + tstrb : in std_logic_vector(data_length(slave)/8-1 downto 0) := (others => '0'); + tid : in std_logic_vector(id_length(slave)-1 downto 0) := (others => '0'); + tdest : in std_logic_vector(dest_length(slave)-1 downto 0) := (others => '0'); + tuser : in std_logic_vector(user_length(slave)-1 downto 0) := (others => '0') + ); end entity; architecture a of axi_stream_slave is + constant message_queue, transaction_token_queue : queue_t := new_queue; - constant notify_request_msg : msg_type_t := new_msg_type("notify request"); - constant message_queue : queue_t := new_queue; - signal notify_bus_process_done : std_logic := '0'; - + signal notification : boolean := false; begin main : process - variable request_msg : msg_t; - variable notify_msg : msg_t; - variable msg_type : msg_type_t; + variable request_msg : msg_t; + variable msg_type : msg_type_t; + variable timestamp : time; + + procedure wait_on_pending_transactions is + begin + if not is_empty(transaction_token_queue) then + wait on notification until is_empty(transaction_token_queue); + end if; + end; begin - receive(net, slave.p_actor, request_msg); + receive(net, get_actor(slave.p_std_cfg), request_msg); msg_type := message_type(request_msg); - if msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg then - push(message_queue, request_msg); - elsif msg_type = check_axi_stream_msg then + if msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg or msg_type = check_axi_stream_msg then push(message_queue, request_msg); + push(transaction_token_queue, true); elsif msg_type = wait_for_time_msg then - push(message_queue, request_msg); + wait_on_pending_transactions; + handle_wait_for_time(net, msg_type, request_msg); elsif msg_type = wait_until_idle_msg then - notify_msg := new_msg(notify_request_msg); - push(message_queue, notify_msg); - wait on notify_bus_process_done until is_empty(message_queue); + wait_on_pending_transactions; + loop + timestamp := now; + if slave.p_monitor /= null_axi_stream_monitor then + wait_until_idle(net, as_sync(slave.p_monitor)); + end if; + if slave.p_protocol_checker /= null_axi_stream_protocol_checker then + wait_until_idle(net, as_sync(slave.p_protocol_checker)); + end if; + exit when now = timestamp; + end loop; + if slave.p_monitor /= null_axi_stream_monitor then + wait_until_idle(net, as_sync(slave.p_monitor)); + end if; handle_wait_until_idle(net, msg_type, request_msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, slave.p_std_cfg); end if; end process; @@ -78,8 +95,8 @@ begin end procedure; variable reply_msg, msg : msg_t; - variable msg_type : msg_type_t; - variable report_msg : string_ptr_t; + variable msg_type : msg_type_t; + variable report_msg : string_ptr_t; variable axi_stream_transaction : axi_stream_transaction_t( tdata(tdata'range), tkeep(tkeep'range), @@ -87,25 +104,20 @@ begin tid(tid'range), tdest(tdest'range), tuser(tuser'range) - ); - variable rnd : RandomPType; + ); + variable transaction_token : boolean; + variable rnd : RandomPType; begin rnd.InitSeed(rnd'instance_name); loop - -- Wait for messages to arrive on the queue, posted by the process above + -- Wait for messages to arrive on the queue, posted by the process above wait until rising_edge(aclk) and (not is_empty(message_queue)); while not is_empty(message_queue) loop - msg := pop(message_queue); + msg := pop(message_queue); msg_type := message_type(msg); - if msg_type = wait_for_time_msg then - handle_sync_message(net, msg_type, msg); - wait until rising_edge(aclk); - elsif msg_type = notify_request_msg then - -- Ignore this message, but expect it - elsif msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg or msg_type = check_axi_stream_msg then - + if msg_type = stream_pop_msg or msg_type = pop_axi_stream_msg or msg_type = check_axi_stream_msg then -- stall according to probability configuration probability_stall_axi_stream(aclk, slave, rnd); @@ -137,14 +149,14 @@ begin check_field(tuser, pop_std_ulogic_vector(msg), "TUSER mismatch, " & to_string(report_msg)); end if; + transaction_token := pop(transaction_token_queue); + notification <= not notification; + wait on notification; else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, slave.p_std_cfg); end if; - end loop; - notify_bus_process_done <= '1'; - wait until notify_bus_process_done = '1'; - notify_bus_process_done <= '0'; + end loop; end loop; end process; @@ -152,7 +164,7 @@ begin axi_stream_monitor_inst : entity work.axi_stream_monitor generic map( monitor => slave.p_monitor - ) + ) port map( aclk => aclk, tvalid => tvalid, @@ -164,11 +176,24 @@ begin tid => tid, tdest => tdest, tuser => tuser - ); + ); + + repeater : if slave.p_use_default_monitor generate + process + constant subscriber : actor_t := new_actor; + variable msg : msg_t; + begin + subscribe(subscriber, get_actor(slave.p_monitor.p_std_cfg)); + loop + receive(net, subscriber, msg); + publish(net, get_actor(slave.p_std_cfg), msg); + end loop; + end process; + end generate; end generate axi_stream_monitor_generate; axi_stream_protocol_checker_generate : if slave.p_protocol_checker /= null_axi_stream_protocol_checker generate - axi_stream_protocol_checker_inst: entity work.axi_stream_protocol_checker + axi_stream_protocol_checker_inst : entity work.axi_stream_protocol_checker generic map ( protocol_checker => slave.p_protocol_checker) port map ( diff --git a/vunit/vhdl/verification_components/src/bus2memory.vhd b/vunit/vhdl/verification_components/src/bus2memory.vhd index 7b9f1a20a..8bd4bbfb0 100644 --- a/vunit/vhdl/verification_components/src/bus2memory.vhd +++ b/vunit/vhdl/verification_components/src/bus2memory.vhd @@ -12,28 +12,32 @@ context work.com_context; use work.queue_pkg.all; use work.bus_master_pkg.all; use work.memory_pkg.all; +use work.bus2memory_pkg.all; +use work.sync_pkg.all; +use work.vc_pkg.all; entity bus2memory is generic ( - bus_handle : bus_master_t; - memory : memory_t); + bus2memory_handle : bus2memory_t); end entity; architecture a of bus2memory is - constant my_memory : memory_t := to_vc_interface(memory); + constant my_memory : memory_t := to_vc_interface(bus2memory_handle.p_memory); begin main : process variable request_msg, reply_msg : msg_t; variable msg_type : msg_type_t; - variable address : std_logic_vector(address_length(bus_handle)-1 downto 0); - variable byte_enable : std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); - variable data : std_logic_vector(data_length(bus_handle)-1 downto 0); - constant blen : natural := byte_length(bus_handle); + variable address : std_logic_vector(address_length(bus2memory_handle.p_bus_handle)-1 downto 0); + variable byte_enable : std_logic_vector(byte_enable_length(bus2memory_handle.p_bus_handle)-1 downto 0); + variable data : std_logic_vector(data_length(bus2memory_handle.p_bus_handle)-1 downto 0); + constant blen : natural := byte_length(bus2memory_handle.p_bus_handle); begin while true loop - receive(net, bus_handle.p_actor, request_msg); + receive(net, get_actor(bus2memory_handle.p_bus_handle), request_msg); msg_type := message_type(request_msg); + handle_sync_message(net, msg_type, request_msg); + if msg_type = bus_read_msg then address := pop_std_ulogic_vector(request_msg); data := read_word(my_memory, to_integer(unsigned(address)), bytes_per_word => data'length/8); @@ -53,7 +57,7 @@ begin end if; end loop; else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, get_std_cfg(bus2memory_handle.p_bus_handle)); end if; end loop; end process; diff --git a/vunit/vhdl/verification_components/src/bus2memory_pkg.vhd b/vunit/vhdl/verification_components/src/bus2memory_pkg.vhd new file mode 100644 index 000000000..46a3bc97e --- /dev/null +++ b/vunit/vhdl/verification_components/src/bus2memory_pkg.vhd @@ -0,0 +1,83 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.sync_pkg.all; +use work.vc_pkg.all; +use work.bus_master_pkg.all; +use work.memory_pkg.all; + +package bus2memory_pkg is + + type bus2memory_t is record + p_bus_handle : bus_master_t; + p_memory : memory_t; + end record; + + constant bus2memory_logger : logger_t := get_logger("vunit_lib:bus_master_pkg"); + constant bus2memory_checker : checker_t := new_checker(bus2memory_logger); + + impure function new_bus2memory( + data_length : natural; + address_length : natural; + memory : memory_t; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return bus2memory_t; + + impure function get_std_cfg(bus2memory : bus2memory_t) return std_cfg_t; + impure function as_bus_master(bus2memory : bus2memory_t) return bus_master_t; + impure function as_sync(bus2memory : bus2memory_t) return sync_handle_t; + +end package; + +package body bus2memory_pkg is + impure function new_bus2memory( + data_length : natural; + address_length : natural; + memory : memory_t; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return bus2memory_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + bus2memory_logger, bus2memory_checker, actor, logger, checker, unexpected_msg_type_policy + ); + constant p_bus_handle : bus_master_t := new_bus(data_length, address_length, byte_length, get_logger(p_std_cfg), get_actor(p_std_cfg), get_checker(p_std_cfg), + unexpected_msg_type_policy + ); + begin + return (p_bus_handle => p_bus_handle, + p_memory => memory); + end; + + impure function get_std_cfg(bus2memory : bus2memory_t) return std_cfg_t is + begin + return get_std_cfg(bus2memory.p_bus_handle); + end; + + impure function as_bus_master(bus2memory : bus2memory_t) return bus_master_t is + begin + return bus2memory.p_bus_handle; + end; + + impure function as_sync(bus2memory : bus2memory_t) return sync_handle_t is + begin + return as_sync(bus2memory.p_bus_handle); + end; + + + +end package body; diff --git a/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd b/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd index 46d614bc3..b35ef4638 100644 --- a/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/bus_master_pkg-body.vhd @@ -10,30 +10,52 @@ use ieee.numeric_std.all; use work.queue_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; use work.queue_pkg.all; use work.check_pkg.all; package body bus_master_pkg is - impure function new_bus(data_length : natural; - address_length : natural; - byte_length : natural := 8; - logger : logger_t := bus_logger; - actor : actor_t := null_actor) return bus_master_t is - variable p_actor : actor_t; + impure function new_bus(data_length : natural; + address_length : natural; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return bus_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + bus_logger, bus_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin - p_actor := actor when actor /= null_actor else new_actor; - - return (p_actor => p_actor, + return (p_std_cfg => p_std_cfg, p_data_length => data_length, p_address_length => address_length, - p_byte_length => byte_length, - p_logger => logger); + p_byte_length => byte_length); + end; + + impure function get_std_cfg(master : bus_master_t) return std_cfg_t is + begin + return master.p_std_cfg; + end; + + impure function get_actor(bus_handle : bus_master_t) return actor_t is + begin + return get_actor(bus_handle.p_std_cfg); + end; + + impure function get_logger(bus_handle : bus_master_t) return logger_t is + begin + return get_logger(bus_handle.p_std_cfg); + end; + + impure function get_checker(bus_handle : bus_master_t) return checker_t is + begin + return get_checker(bus_handle.p_std_cfg); end; - function get_logger(bus_handle : bus_master_t) return logger_t is + impure function unexpected_msg_type_policy(bus_handle : bus_master_t) return unexpected_msg_type_policy_t is begin - return bus_handle.p_logger; + return unexpected_msg_type_policy(bus_handle.p_std_cfg); end; impure function data_length(bus_handle : bus_master_t) return natural is @@ -85,7 +107,7 @@ package body bus_master_pkg is end if; push_std_ulogic_vector(request_msg, full_byte_enable); - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure write_bus(signal net : inout network_t; @@ -114,7 +136,7 @@ package body bus_master_pkg is full_data(bus_handle.p_data_length-1 downto 0) := pop(data); push_std_ulogic_vector(request_msg, full_data); end loop; - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure burst_write_bus(signal net : inout network_t; @@ -153,7 +175,7 @@ package body bus_master_pkg is read_bus(net, bus_handle, address, data); if not std_match(data, edata) then - failure(bus_handle.p_logger, base_error); + failure(get_logger(bus_handle), base_error); end if; end procedure; @@ -177,7 +199,7 @@ package body bus_master_pkg is request_msg := new_msg(bus_read_msg); full_address(address'length-1 downto 0) := address; push_std_ulogic_vector(request_msg, full_address); - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure read_bus(signal net : inout network_t; @@ -200,7 +222,7 @@ package body bus_master_pkg is full_address(address'length-1 downto 0) := address; push_std_ulogic_vector(request_msg, full_address); push_integer(request_msg, burst_length); - send(net, bus_handle.p_actor, request_msg); + send(net, get_actor(bus_handle), request_msg); end procedure; procedure burst_read_bus(signal net : inout network_t; @@ -306,9 +328,9 @@ package body bus_master_pkg is end loop; if msg = "" then - failure(bus_handle.p_logger, "Timeout"); + failure(get_logger(bus_handle), "Timeout"); else - failure(bus_handle.p_logger, msg); + failure(get_logger(bus_handle), msg); end if; end; @@ -329,13 +351,13 @@ package body bus_master_pkg is impure function as_sync(bus_master : bus_master_t) return sync_handle_t is begin - return bus_master.p_actor; + return get_actor(bus_master); end; procedure wait_until_idle(signal net : inout network_t; bus_handle : bus_master_t) is begin - wait_until_idle(net, bus_handle.p_actor); + wait_until_idle(net, get_actor(bus_handle)); end; end package body; diff --git a/vunit/vhdl/verification_components/src/bus_master_pkg.vhd b/vunit/vhdl/verification_components/src/bus_master_pkg.vhd index af486356b..7ac6539b1 100644 --- a/vunit/vhdl/verification_components/src/bus_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/bus_master_pkg.vhd @@ -10,8 +10,10 @@ library ieee; use ieee.std_logic_1164.all; use work.logger_pkg.all; +use work.checker_pkg.all; context work.com_context; use work.sync_pkg.all; +use work.vc_pkg.all; use work.queue_pkg.all; package bus_master_pkg is @@ -19,11 +21,10 @@ package bus_master_pkg is -- Handle to VC instance with bus master VCI type bus_master_t is record -- These fields are private, do not use directly - p_actor : actor_t; + p_std_cfg : std_cfg_t; p_data_length : natural; p_address_length : natural; p_byte_length : natural; - p_logger : logger_t; end record; -- Reference to non-blocking bus command @@ -31,16 +32,30 @@ package bus_master_pkg is -- Default logger object for bus master instances constant bus_logger : logger_t := get_logger("vunit_lib:bus_master_pkg"); + constant bus_checker : checker_t := new_checker(bus_logger); -- Create new handle for bus master VC impure function new_bus(data_length : natural; address_length : natural; byte_length : natural := 8; - logger : logger_t := bus_logger; - actor : actor_t := null_actor) return bus_master_t; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return bus_master_t; + + impure function get_std_cfg(master : bus_master_t) return std_cfg_t; + + -- Return the actor used by the bus master + impure function get_actor(bus_handle : bus_master_t) return actor_t; -- Return the logger used by the bus master - function get_logger(bus_handle : bus_master_t) return logger_t; + impure function get_logger(bus_handle : bus_master_t) return logger_t; + + -- Return the checker used by the bus master + impure function get_checker(bus_handle : bus_master_t) return checker_t; + + -- Return true if the bus VC fails on unexpected messages to the actor + impure function unexpected_msg_type_policy(bus_handle : bus_master_t) return unexpected_msg_type_policy_t; -- Return the length of the data on this bus impure function data_length(bus_handle : bus_master_t) return natural; diff --git a/vunit/vhdl/verification_components/src/ram_master.vhd b/vunit/vhdl/verification_components/src/ram_master.vhd index a797f9d8e..740faf625 100644 --- a/vunit/vhdl/verification_components/src/ram_master.vhd +++ b/vunit/vhdl/verification_components/src/ram_master.vhd @@ -11,34 +11,35 @@ use ieee.numeric_std.all; use work.queue_pkg.all; use work.bus_master_pkg.all; +use work.ram_master_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; context work.com_context; entity ram_master is generic ( - bus_handle : bus_master_t; - latency : positive + ram_master : ram_master_t ); port ( clk : in std_logic; en : out std_logic := '0'; - we : out std_logic_vector(byte_enable_length(bus_handle)-1 downto 0); - addr : out std_logic_vector(address_length(bus_handle)-1 downto 0); - wdata : out std_logic_vector(data_length(bus_handle)-1 downto 0); - rdata : in std_logic_vector(data_length(bus_handle)-1 downto 0) + we : out std_logic_vector(byte_enable_length(as_bus_master(ram_master))-1 downto 0); + addr : out std_logic_vector(address_length(as_bus_master(ram_master)) - 1 downto 0); + wdata : out std_logic_vector(data_length(as_bus_master(ram_master)) - 1 downto 0); + rdata : in std_logic_vector(data_length(as_bus_master(ram_master)) - 1 downto 0) ); end entity; architecture a of ram_master is signal rd : std_logic := '0'; - signal rd_pipe : std_logic_vector(0 to latency-1); + signal rd_pipe : std_logic_vector(0 to ram_master.p_latency - 1); constant request_queue : queue_t := new_queue; begin main : process variable request_msg : msg_t; variable msg_type : msg_type_t; begin - receive(net, bus_handle.p_actor, request_msg); + receive(net, get_actor(ram_master), request_msg); msg_type := message_type(request_msg); if msg_type = bus_read_msg then @@ -59,15 +60,15 @@ begin wait until en = '1' and rising_edge(clk); en <= '0'; - elsif msg_type = wait_until_idle_msg then + elsif msg_type = wait_until_idle_msg or msg_type = wait_for_time_msg then while not is_empty(request_queue) loop wait until rising_edge(clk); end loop; - handle_wait_until_idle(net, msg_type, request_msg); + handle_sync_message(net, msg_type, request_msg); + else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, get_std_cfg(ram_master)); end if; - end process; read_return : process diff --git a/vunit/vhdl/verification_components/src/ram_master_pkg.vhd b/vunit/vhdl/verification_components/src/ram_master_pkg.vhd new file mode 100644 index 000000000..659c9404f --- /dev/null +++ b/vunit/vhdl/verification_components/src/ram_master_pkg.vhd @@ -0,0 +1,322 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com +-- Author Slawomir Siluk slaweksiluk@gazeta.pl +library ieee; +use ieee.std_logic_1164.all; + +context work.vunit_context; +context work.com_context; +use work.sync_pkg.all; +use work.vc_pkg.all; +use work.bus_master_pkg.all; +use work.memory_pkg.all; + +package ram_master_pkg is + + type ram_master_t is record + p_bus_handle : bus_master_t; + p_latency : positive; + end record; + + constant ram_master_logger : logger_t := get_logger("vunit_lib:ram_master_pkg"); + constant ram_master_checker : checker_t := new_checker(ram_master_logger); + + impure function new_ram_master( + data_length : natural; + address_length : natural; + latency : positive; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return ram_master_t; + + impure function as_sync(ram_master : ram_master_t) return sync_handle_t; + impure function as_bus_master(ram_master : ram_master_t) return bus_master_t; + + -- Return the actor used by the Wishbone master + impure function get_actor(ram_master : ram_master_t) return actor_t; + + -- Return the logger used by the Wishbone master + impure function get_logger(ram_master : ram_master_t) return logger_t; + + -- Return the checker used by the Wishbone master + impure function get_checker(ram_master : ram_master_t) return checker_t; + + -- Return for handling unexpected messages to the actor + impure function unexpected_msg_type_policy(ram_master : ram_master_t) return unexpected_msg_type_policy_t; + + -- Return the length of the data on the Wishbone bus + impure function data_length(ram_master : ram_master_t) return natural; + + -- Return the length of the address on the Wishbone bus + impure function address_length(ram_master : ram_master_t) return natural; + + -- Return the length of a byte on the Wishbone bus + impure function byte_length(ram_master : ram_master_t) return natural; + + -- Return the length of the byte enable signal on the Wishbone bus + impure function byte_enable_length(ram_master : ram_master_t) return natural; + + -- Convert natural address to std_logic_vector using the correct number of bits + impure function to_address(constant ram_master : ram_master_t; address : natural) return std_logic_vector; + + -- Blocking: Write the Wishbone bus + procedure write_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + procedure write_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + + -- Non blocking: Read the Wishbone bus returning a reference to the future reply + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t); + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + variable reference : inout bus_reference_t); + + -- Blocking: Read the Wishbone bus and check result against expected data + procedure check_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := ""); + procedure check_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := ""); + + -- Blocking: read the Wishbone bus with immediate reply + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector); + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + variable data : inout std_logic_vector); + + -- Blocking: Wait until a read from address equals the value using + -- std_match If timeout is reached error with msg + procedure wait_until_read_equals( + signal net : inout network_t; + ram_master : ram_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Blocking: Wait until a read from address has the bit with this + -- index set to value If timeout is reached error with msg + procedure wait_until_read_bit_equals( + signal net : inout network_t; + ram_master : ram_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Wait until all operations scheduled before this command has finished + procedure wait_until_idle(signal net : inout network_t; + ram_master : ram_master_t); + + impure function get_std_cfg(ram_master : ram_master_t) return std_cfg_t; + +end package; + +package body ram_master_pkg is + impure function new_ram_master( + data_length : natural; + address_length : natural; + latency : positive; + byte_length : natural := 8; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return ram_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + ram_master_logger, ram_master_checker, actor, logger, checker, unexpected_msg_type_policy + ); + constant p_bus_handle : bus_master_t := new_bus(data_length, address_length, byte_length, get_logger(p_std_cfg), get_actor(p_std_cfg), get_checker(p_std_cfg), + unexpected_msg_type_policy + ); + begin + return (p_bus_handle => p_bus_handle, + p_latency => latency); + end; + + impure function as_sync(ram_master : ram_master_t) return sync_handle_t is + begin + return as_sync(ram_master.p_bus_handle); + end; + + impure function as_bus_master(ram_master : ram_master_t) return bus_master_t is + begin + return ram_master.p_bus_handle; + end; + + impure function get_actor(ram_master : ram_master_t) return actor_t is + begin + return get_actor(ram_master.p_bus_handle); + end; + + impure function get_logger(ram_master : ram_master_t) return logger_t is + begin + return get_logger(ram_master.p_bus_handle); + end; + + impure function get_checker(ram_master : ram_master_t) return checker_t is + begin + return get_checker(ram_master.p_bus_handle); + end; + + impure function unexpected_msg_type_policy(ram_master : ram_master_t) return unexpected_msg_type_policy_t is + begin + return unexpected_msg_type_policy(ram_master.p_bus_handle); + end; + + impure function data_length(ram_master : ram_master_t) return natural is + begin + return data_length(ram_master.p_bus_handle); + end; + + impure function address_length(ram_master : ram_master_t) return natural is + begin + return address_length(ram_master.p_bus_handle); + end; + + impure function byte_length(ram_master : ram_master_t) return natural is + begin + return byte_length(ram_master.p_bus_handle); + end; + + impure function byte_enable_length(ram_master : ram_master_t) return natural is + begin + return byte_enable_length(ram_master.p_bus_handle); + end; + + impure function to_address(constant ram_master : ram_master_t; address : natural) return std_logic_vector is + begin + return to_address(ram_master.p_bus_handle, address); + end; + + procedure write_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, ram_master.p_bus_handle, address, data, byte_enable); + end; + + procedure write_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + constant data : std_logic_vector; + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, ram_master.p_bus_handle, address, data, byte_enable); + end; + + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t) is + begin + read_bus(net, ram_master.p_bus_handle, address, reference); + end; + + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + variable reference : inout bus_reference_t) is + begin + read_bus(net, ram_master.p_bus_handle, address, reference); + end; + + procedure check_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, ram_master.p_bus_handle, address, expected, msg); + end; + + procedure check_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, ram_master.p_bus_handle, address, expected, msg); + end; + + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector) is + begin + read_bus(net, ram_master.p_bus_handle, address, data); + end; + + procedure read_bus(signal net : inout network_t; + constant ram_master : ram_master_t; + constant address : natural; + variable data : inout std_logic_vector) is + begin + read_bus(net, ram_master.p_bus_handle, address, data); + end; + + procedure wait_until_read_equals( + signal net : inout network_t; + ram_master : ram_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_equals(net, ram_master.p_bus_handle, addr, value, timeout, msg); + end; + + procedure wait_until_read_bit_equals( + signal net : inout network_t; + ram_master : ram_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_bit_equals(net, ram_master.p_bus_handle, addr, idx, value, timeout, msg); + end; + + procedure wait_until_idle(signal net : inout network_t; + ram_master : ram_master_t) is + begin + wait_until_idle(net, ram_master.p_bus_handle); + end; + + impure function get_std_cfg(ram_master : ram_master_t) return std_cfg_t is + begin + return get_std_cfg(ram_master.p_bus_handle); + end; + +end package body; diff --git a/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd b/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd index 124e6ce40..5aa1e0bdc 100644 --- a/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd +++ b/vunit/vhdl/verification_components/src/signal_checker_pkg.vhd @@ -11,17 +11,23 @@ context work.vunit_context; context work.com_context; use work.sync_pkg.all; +use work.vc_pkg.all; package signal_checker_pkg is type signal_checker_t is record -- Private - p_actor : actor_t; - p_logger : logger_t; + p_std_cfg : std_cfg_t; end record; + constant signal_checker_logger : logger_t := get_logger("vunit_lib:signal_checker_pkg"); + constant signal_checker_checker : checker_t := new_checker(signal_checker_logger); + impure function new_signal_checker( - logger : logger_t := null_logger) - return signal_checker_t; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) + return signal_checker_t; -- Add one value to the expect queue -- Allow event to occur within event_time += margin including end points @@ -33,7 +39,10 @@ package signal_checker_pkg is -- Wait until all expected values have been checked procedure wait_until_idle(signal net : inout network_t; - signal_checker : signal_checker_t); + signal_checker : signal_checker_t); + + impure function as_sync(signal_checker : signal_checker_t) return sync_handle_t; + function get_std_cfg(signal_checker : signal_checker_t) return std_cfg_t; -- Private message type definitions constant expect_msg : msg_type_t := new_msg_type("expect"); @@ -42,15 +51,18 @@ end package; package body signal_checker_pkg is - impure function new_signal_checker(logger : logger_t := null_logger) return signal_checker_t is - variable result : signal_checker_t; + impure function new_signal_checker( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) + return signal_checker_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + signal_checker_logger, signal_checker_checker, actor, logger, checker, unexpected_msg_type_policy + ); + begin - result := (p_actor => new_actor, - p_logger => logger); - if logger = null_logger then - result.p_logger := default_logger; - end if; - return result; + return (p_std_cfg => p_std_cfg); end; procedure expect(signal net : inout network_t; @@ -63,13 +75,25 @@ package body signal_checker_pkg is push_std_ulogic_vector(request_msg, value); push_time(request_msg, event_time); push_time(request_msg, margin); - send(net, signal_checker.p_actor, request_msg); + send(net, get_actor(signal_checker.p_std_cfg), request_msg); end; procedure wait_until_idle(signal net : inout network_t; signal_checker : signal_checker_t) is begin - wait_until_idle(net, signal_checker.p_actor); + wait_until_idle(net, get_actor(signal_checker.p_std_cfg)); + end; + + impure function as_sync(signal_checker : signal_checker_t) return sync_handle_t is + begin + return get_actor(signal_checker.p_std_cfg); + end; + + function get_std_cfg(signal_checker : signal_checker_t) return std_cfg_t is + begin + return signal_checker.p_std_cfg; end; + + end package body; diff --git a/vunit/vhdl/verification_components/src/std_logic_checker.vhd b/vunit/vhdl/verification_components/src/std_logic_checker.vhd index 358621b02..9243811ec 100644 --- a/vunit/vhdl/verification_components/src/std_logic_checker.vhd +++ b/vunit/vhdl/verification_components/src/std_logic_checker.vhd @@ -12,6 +12,7 @@ context work.data_types_context; context work.com_context; use work.signal_checker_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; entity std_logic_checker is generic ( @@ -27,10 +28,9 @@ begin main : process variable request_msg : msg_t; - variable reply_msg : msg_t; variable msg_type : msg_type_t; begin - receive(net, signal_checker.p_actor, request_msg); + receive(net, get_actor(signal_checker.p_std_cfg), request_msg); msg_type := message_type(request_msg); if msg_type = expect_msg then @@ -38,7 +38,7 @@ begin push_time(expect_queue, pop_time(request_msg)); push_time(expect_queue, pop_time(request_msg)); - elsif msg_type = wait_until_idle_msg then + elsif msg_type = wait_until_idle_msg or msg_type = wait_for_time_msg then while not is_empty(expect_queue) loop if value'event then @@ -48,10 +48,9 @@ begin end if; end loop; - reply_msg := new_msg(wait_until_idle_reply_msg); - reply(net, request_msg, reply_msg); + handle_sync_message(net, msg_type, request_msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, signal_checker.p_std_cfg); end if; delete(request_msg); @@ -72,22 +71,22 @@ begin begin wait on value; if is_empty(expect_queue) then - error(signal_checker.p_logger, "Unexpected event with value = " & to_string(value)); + error(get_logger(signal_checker.p_std_cfg), "Unexpected event with value = " & to_string(value)); else expected_value := pop_std_ulogic_vector(expect_queue); event_time := pop_time(expect_queue); margin := pop_time(expect_queue); if value /= expected_value then - error(signal_checker.p_logger, "Got event with wrong value, got " & to_string(value) & + error(get_logger(signal_checker.p_std_cfg), "Got event with wrong value, got " & to_string(value) & " expected " & to_string(expected_value)); elsif now < event_time - margin or now > event_time + margin then - error(signal_checker.p_logger, "Got event at wrong time, occured at " & time'image(now) & + error(get_logger(signal_checker.p_std_cfg), "Got event at wrong time, occured at " & time'image(now) & " expected at " & time'image(event_time) & margin_suffix); else - pass(signal_checker.p_logger, "Got expected event with value = " & to_string(value)); + pass(get_logger(signal_checker.p_std_cfg), "Got expected event with value = " & to_string(value)); end if; end if; end process; diff --git a/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd b/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd index 85a0e6067..956bb768e 100644 --- a/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/stream_master_pkg-body.vhd @@ -11,9 +11,28 @@ context work.vunit_context; context work.com_context; package body stream_master_pkg is - impure function new_stream_master return stream_master_t is + impure function new_stream_master( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return stream_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + stream_master_logger, stream_master_checker, actor, logger, checker, unexpected_msg_type_policy + ); + + begin + return (p_std_cfg => p_std_cfg); + end; + + function get_std_cfg(master : stream_master_t) return std_cfg_t is + begin + return master.p_std_cfg; + end; + + impure function as_sync(master : stream_master_t) return sync_handle_t is begin - return (p_actor => new_actor); + return get_actor(master.p_std_cfg); end; procedure push_stream(signal net : inout network_t; @@ -25,7 +44,7 @@ package body stream_master_pkg is begin push_std_ulogic_vector(msg, normalized_data); push_boolean(msg, last); - send(net, stream.p_actor, msg); + send(net, get_actor(stream.p_std_cfg), msg); end; end package body; diff --git a/vunit/vhdl/verification_components/src/stream_master_pkg.vhd b/vunit/vhdl/verification_components/src/stream_master_pkg.vhd index b61e55a27..e1eb85fbc 100644 --- a/vunit/vhdl/verification_components/src/stream_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/stream_master_pkg.vhd @@ -11,15 +11,28 @@ use ieee.std_logic_1164.all; context work.vunit_context; context work.com_context; +use work.vc_pkg.all; +use work.sync_pkg.all; package stream_master_pkg is -- Stream master handle type stream_master_t is record - p_actor : actor_t; + p_std_cfg : std_cfg_t; end record; + constant stream_master_logger : logger_t := get_logger("vunit_lib:stream_master_pkg"); + constant stream_master_checker : checker_t := new_checker(stream_master_logger); + -- Create a new stream master object - impure function new_stream_master return stream_master_t; + impure function new_stream_master( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return stream_master_t; + + function get_std_cfg(master : stream_master_t) return std_cfg_t; + impure function as_sync(master : stream_master_t) return sync_handle_t; -- Push a data value to the stream procedure push_stream(signal net : inout network_t; diff --git a/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd b/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd index 5b0ca4660..aa285c4aa 100644 --- a/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd +++ b/vunit/vhdl/verification_components/src/stream_slave_pkg-body.vhd @@ -5,9 +5,29 @@ -- Copyright (c) 2014-2021, Lars Asplund lars.anders.asplund@gmail.com package body stream_slave_pkg is - impure function new_stream_slave return stream_slave_t is + + impure function new_stream_slave( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return stream_slave_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + stream_slave_logger, stream_slave_checker, actor, logger, checker, unexpected_msg_type_policy + ); + + begin + return (p_std_cfg => p_std_cfg); + end; + + function get_std_cfg(slave : stream_slave_t) return std_cfg_t is + begin + return slave.p_std_cfg; + end; + + impure function as_sync(slave : stream_slave_t) return sync_handle_t is begin - return (p_actor => new_actor); + return get_actor(slave.p_std_cfg); end; procedure pop_stream(signal net : inout network_t; @@ -15,7 +35,7 @@ package body stream_slave_pkg is variable reference : inout stream_reference_t) is begin reference := new_msg(stream_pop_msg); - send(net, stream.p_actor, reference); + send(net, get_actor(stream.p_std_cfg), reference); end; procedure await_pop_stream_reply(signal net : inout network_t; diff --git a/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd b/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd index fa20b09d9..443381e52 100644 --- a/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/stream_slave_pkg.vhd @@ -12,14 +12,28 @@ use ieee.std_logic_1164.all; context work.vunit_context; context work.com_context; +use work.vc_pkg.all; +use work.sync_pkg.all; + package stream_slave_pkg is -- Stream slave handle type stream_slave_t is record - p_actor : actor_t; + p_std_cfg : std_cfg_t; end record; + constant stream_slave_logger : logger_t := get_logger("vunit_lib:stream_slave_pkg"); + constant stream_slave_checker : checker_t := new_checker(stream_slave_logger); + -- Create a new stream slave object - impure function new_stream_slave return stream_slave_t; + impure function new_stream_slave( + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return stream_slave_t; + + function get_std_cfg(slave : stream_slave_t) return std_cfg_t; + impure function as_sync(slave : stream_slave_t) return sync_handle_t; -- Reference to future stream result alias stream_reference_t is msg_t; diff --git a/vunit/vhdl/verification_components/src/uart_master.vhd b/vunit/vhdl/verification_components/src/uart_master.vhd index 1a4115c1f..2764576e8 100644 --- a/vunit/vhdl/verification_components/src/uart_master.vhd +++ b/vunit/vhdl/verification_components/src/uart_master.vhd @@ -14,6 +14,7 @@ use vunit_lib.stream_master_pkg.all; use vunit_lib.uart_pkg.all; use vunit_lib.queue_pkg.all; use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; entity uart_master is generic ( @@ -26,7 +27,8 @@ architecture a of uart_master is begin main : process - procedure uart_send(data : std_logic_vector; + constant logger : logger_t := get_logger(main'path_name, get_logger(uart.p_std_cfg)); + procedure uart_send(data : std_logic_vector := x"00"; signal tx : out std_logic; baud_rate : integer) is constant time_per_bit : time := (10**9 / baud_rate) * 1 ns; @@ -38,7 +40,9 @@ begin end procedure; begin - debug("Sending " & to_string(data)); + if is_visible(logger, display_handler, debug) then + debug(logger, "Sending x""" & to_hstring(data) & """"); + end if; send_bit(not uart.p_idle_state); for i in 0 to data'length-1 loop send_bit(data(i)); @@ -50,7 +54,7 @@ begin variable baud_rate : natural := uart.p_baud_rate; variable msg_type : msg_type_t; begin - receive(net, uart.p_actor, msg); + receive(net, get_actor(uart.p_std_cfg), msg); msg_type := message_type(msg); handle_sync_message(net, msg_type, msg); @@ -60,7 +64,7 @@ begin elsif msg_type = uart_set_baud_rate_msg then baud_rate := pop(msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, uart.p_std_cfg); end if; end process; diff --git a/vunit/vhdl/verification_components/src/uart_pkg.vhd b/vunit/vhdl/verification_components/src/uart_pkg.vhd index c1ecb5e96..edf14910e 100644 --- a/vunit/vhdl/verification_components/src/uart_pkg.vhd +++ b/vunit/vhdl/verification_components/src/uart_pkg.vhd @@ -11,20 +11,24 @@ context work.com_context; use work.stream_master_pkg.all; use work.stream_slave_pkg.all; use work.sync_pkg.all; +use work.vc_pkg.all; use work.integer_vector_ptr_pkg.all; use work.queue_pkg.all; +use work.logger_pkg.all; +use work.checker_pkg.all; +use work.check_pkg.all; package uart_pkg is type uart_master_t is record - p_actor : actor_t; - p_baud_rate : natural; + p_std_cfg : std_cfg_t; + p_baud_rate : natural; p_idle_state : std_logic; end record; type uart_slave_t is record - p_actor : actor_t; - p_baud_rate : natural; - p_idle_state : std_logic; + p_std_cfg : std_cfg_t; + p_baud_rate : natural; + p_idle_state : std_logic; p_data_length : positive; end record; @@ -36,62 +40,101 @@ package uart_pkg is procedure set_baud_rate(signal net : inout network_t; uart_slave : uart_slave_t; baud_rate : natural); + constant uart_logger : logger_t := get_logger("vunit_lib:uart_pkg"); + constant uart_checker : checker_t := new_checker(uart_logger); constant default_baud_rate : natural := 115200; constant default_idle_state : std_logic := '1'; constant default_data_length : positive := 8; - impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state) return uart_master_t; - impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state; - data_length : positive := default_data_length) return uart_slave_t; + impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return uart_master_t; + impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + data_length : positive := default_data_length; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return uart_slave_t; impure function as_stream(uart_master : uart_master_t) return stream_master_t; impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t; impure function as_sync(uart_master : uart_master_t) return sync_handle_t; impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t; + function get_std_cfg(uart_master : uart_master_t) return std_cfg_t; + function get_std_cfg(uart_slave : uart_slave_t) return std_cfg_t; constant uart_set_baud_rate_msg : msg_type_t := new_msg_type("uart set baud rate"); end package; package body uart_pkg is - impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state) return uart_master_t is + impure function new_uart_master(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return uart_master_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + uart_logger, uart_checker, actor, logger, checker, unexpected_msg_type_policy + ); + begin - return (p_actor => new_actor, - p_baud_rate => initial_baud_rate, + return (p_std_cfg => p_std_cfg, + p_baud_rate => initial_baud_rate, p_idle_state => idle_state); end; - impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; - idle_state : std_logic := default_idle_state; - data_length : positive := default_data_length) return uart_slave_t is + impure function new_uart_slave(initial_baud_rate : natural := default_baud_rate; + idle_state : std_logic := default_idle_state; + data_length : positive := default_data_length; + logger : logger_t := null_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail) return uart_slave_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + uart_logger, uart_checker, actor, logger, checker, unexpected_msg_type_policy + ); begin - return (p_actor => new_actor, - p_baud_rate => initial_baud_rate, - p_idle_state => idle_state, + return (p_std_cfg => p_std_cfg, + p_baud_rate => initial_baud_rate, + p_idle_state => idle_state, p_data_length => data_length); end; impure function as_stream(uart_master : uart_master_t) return stream_master_t is begin - return stream_master_t'(p_actor => uart_master.p_actor); + return stream_master_t'(p_std_cfg => uart_master.p_std_cfg); end; impure function as_stream(uart_slave : uart_slave_t) return stream_slave_t is begin - return stream_slave_t'(p_actor => uart_slave.p_actor); + return (p_std_cfg => uart_slave.p_std_cfg); end; impure function as_sync(uart_master : uart_master_t) return sync_handle_t is begin - return uart_master.p_actor; + return get_actor(uart_master.p_std_cfg); end; impure function as_sync(uart_slave : uart_slave_t) return sync_handle_t is begin - return uart_slave.p_actor; + return get_actor(uart_slave.p_std_cfg); + end; + + function get_std_cfg(uart_master : uart_master_t) return std_cfg_t is + begin + return uart_master.p_std_cfg; + end; + + function get_std_cfg(uart_slave : uart_slave_t) return std_cfg_t is + begin + return uart_slave.p_std_cfg; end; procedure set_baud_rate(signal net : inout network_t; @@ -107,13 +150,13 @@ package body uart_pkg is uart_master : uart_master_t; baud_rate : natural) is begin - set_baud_rate(net, uart_master.p_actor, baud_rate); + set_baud_rate(net, get_actor(uart_master.p_std_cfg), baud_rate); end; procedure set_baud_rate(signal net : inout network_t; uart_slave : uart_slave_t; baud_rate : natural) is begin - set_baud_rate(net, uart_slave.p_actor, baud_rate); + set_baud_rate(net, get_actor(uart_slave.p_std_cfg), baud_rate); end; end package body; diff --git a/vunit/vhdl/verification_components/src/uart_slave.vhd b/vunit/vhdl/verification_components/src/uart_slave.vhd index 8f15359e5..ef127f5eb 100644 --- a/vunit/vhdl/verification_components/src/uart_slave.vhd +++ b/vunit/vhdl/verification_components/src/uart_slave.vhd @@ -13,6 +13,8 @@ context vunit_lib.com_context; use vunit_lib.stream_slave_pkg.all; use vunit_lib.uart_pkg.all; use vunit_lib.queue_pkg.all; +use vunit_lib.sync_pkg.all; +use vunit_lib.vc_pkg.all; entity uart_slave is generic ( @@ -31,9 +33,11 @@ begin variable reply_msg, msg : msg_t; variable msg_type : msg_type_t; begin - receive(net, uart.p_actor, msg); + receive(net, get_actor(uart.p_std_cfg), msg); msg_type := message_type(msg); + handle_sync_message(net, msg_type, msg); + if msg_type = uart_set_baud_rate_msg then baud_rate <= pop(msg); @@ -47,7 +51,7 @@ begin reply(net, msg, reply_msg); else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, uart.p_std_cfg); end if; end process; diff --git a/vunit/vhdl/verification_components/src/vc_pkg.vhd b/vunit/vhdl/verification_components/src/vc_pkg.vhd new file mode 100644 index 000000000..14ba6d77c --- /dev/null +++ b/vunit/vhdl/verification_components/src/vc_pkg.vhd @@ -0,0 +1,143 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com +-- +-- This package contains common functionality for VC designers. + +context work.vunit_context; +context work.com_context; + +package vc_pkg is + type unexpected_msg_type_policy_t is (fail, ignore); + + type std_cfg_t is record + p_actor : actor_t; + p_logger : logger_t; + p_checker : checker_t; + p_unexpected_msg_type_policy : unexpected_msg_type_policy_t; + end record; + + constant null_std_cfg : std_cfg_t := ( + p_actor => null_actor, + p_logger => null_logger, + p_checker => null_checker, + p_unexpected_msg_type_policy => ignore + ); + + -- Creates a standard VC configuration with an actor, a logger, a checker, and the policy for handling unexpected messages + -- + -- * The actor is the actor provided by the actor parameter unless it's the null_actor. In that case a new actor is created + -- * The logger is the logger provided by the logger parameter unless it's the null_logger. In that case the default logger is used which must not be the null_logger. + -- * The checker is the checker provided by the checker parameter unless it's the null_checker. In that case the default checker is used if the logger is the + -- default logger. Otherwise a new checker is created based on the provided logger. The default checker must not be the null_checker + -- * The policy for handling unexpected messages is according to the unexpected_msg_type_policy parameter. + impure function create_std_cfg( + default_logger : logger_t; + default_checker : checker_t; + actor : actor_t := null_actor; + logger : logger_t := null_logger; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return std_cfg_t; + + -- These functions extracts the different parts of a standard VC configuration + impure function get_actor(std_cfg : std_cfg_t) return actor_t; + impure function get_logger(std_cfg : std_cfg_t) return logger_t; + impure function get_checker(std_cfg : std_cfg_t) return checker_t; + impure function unexpected_msg_type_policy(std_cfg : std_cfg_t) return unexpected_msg_type_policy_t; + + -- Handle messages with unexpected message type according to the standard configuration + procedure unexpected_msg_type(msg_type : msg_type_t; std_cfg : std_cfg_t); + +end package; + +package body vc_pkg is + constant vc_logger : logger_t := get_logger("vunit_lib:vc_pkg"); + constant vc_checker : checker_t := new_checker(vc_logger); + constant used_default_loggers : integer_vector_ptr_t := new_integer_vector_ptr; + + procedure register_if_not_used(default_logger : logger_t) is + constant ref : integer := to_integer(default_logger); + begin + for idx in 0 to length(used_default_loggers) - 1 loop + if get(used_default_loggers, idx) = ref then + warning(default_logger, "This logger is already used by another VC. Source VC for log messages is ambiguous."); + report "This logger is already used by another VC. Source VC for log messages is ambiguous." severity warning; + return; + end if; + end loop; + + resize(used_default_loggers, length(used_default_loggers) + 1); + set(used_default_loggers, length(used_default_loggers) - 1, ref); + end; + + impure function create_std_cfg( + default_logger : logger_t; + default_checker : checker_t; + actor : actor_t := null_actor; + logger : logger_t := null_logger; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return std_cfg_t is + variable result : std_cfg_t; + begin + check(vc_checker, default_logger /= null_logger, "A default logger must be provided"); + check(vc_checker, default_checker /= null_checker, "A default checker must be provided"); + + result.p_actor := actor when actor /= null_actor else new_actor; + + if logger /= null_logger then + result.p_logger := logger; + else + result.p_logger := default_logger; + register_if_not_used(default_logger); + end if; + + result.p_unexpected_msg_type_policy := unexpected_msg_type_policy; + + if checker = null_checker then + if logger = null_logger then + result.p_checker := default_checker; + if get_logger(default_checker) /= default_logger then + register_if_not_used(get_logger(default_checker)); + end if; + else + result.p_checker := new_checker(logger); + end if; + else + result.p_checker := checker; + end if; + + return result; + end; + + impure function get_actor(std_cfg : std_cfg_t) return actor_t is + begin + return std_cfg.p_actor; + end; + + impure function get_logger(std_cfg : std_cfg_t) return logger_t is + begin + return std_cfg.p_logger; + end; + + impure function get_checker(std_cfg : std_cfg_t) return checker_t is + begin + return std_cfg.p_checker; + end; + + impure function unexpected_msg_type_policy(std_cfg : std_cfg_t) return unexpected_msg_type_policy_t is + begin + return std_cfg.p_unexpected_msg_type_policy; + end; + + procedure unexpected_msg_type(msg_type : msg_type_t; + std_cfg : std_cfg_t) is + begin + if unexpected_msg_type_policy(std_cfg) = fail then + unexpected_msg_type(msg_type, get_checker(std_cfg)); + end if; + end; +end package body; diff --git a/vunit/vhdl/verification_components/src/wishbone_master.vhd b/vunit/vhdl/verification_components/src/wishbone_master.vhd index a6c3ff97c..8337f986d 100644 --- a/vunit/vhdl/verification_components/src/wishbone_master.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_master.vhd @@ -17,14 +17,15 @@ use work.logger_pkg.all; use work.check_pkg.all; use work.log_levels_pkg.all; use work.sync_pkg.all; +use work.wishbone_pkg.all; +use work.vc_pkg.all; library osvvm; use osvvm.RandomPkg.all; entity wishbone_master is generic ( - bus_handle : bus_master_t; - strobe_high_probability : real range 0.0 to 1.0 := 1.0 + wishbone_master : wishbone_master_t ); port ( clk : in std_logic; @@ -41,10 +42,7 @@ entity wishbone_master is end entity; architecture a of wishbone_master is - constant rd_request_queue : queue_t := new_queue; - constant wr_request_queue : queue_t := new_queue; constant acknowledge_queue : queue_t := new_queue; - constant bus_ack_msg : msg_type_t := new_msg_type("wb master ack msg"); constant wb_master_ack_actor : actor_t := new_actor; signal start_cycle : std_logic := '0'; signal end_cycle : std_logic := '0'; @@ -58,26 +56,25 @@ begin variable rnd : RandomPType; begin rnd.InitSeed(rnd'instance_name); - report rnd'instance_name; + request_msg := null_msg; cycle_type := bus_read_msg; stb <= '0'; - wait until rising_edge(clk); loop - receive(net, bus_handle.p_actor, request_msg); + receive(net, get_actor(wishbone_master), request_msg); msg_type := message_type(request_msg); if msg_type = bus_read_msg or msg_type = bus_write_msg then if msg_type /= cycle_type and cycle then - wait until not cycle; -- todo: is this necessary? the wb spec v4 does not explicitly forbid mixed cycles + wait until not cycle; -- TODO: is this necessary? the wb spec v4 does not explicitly forbid mixed cycles wait until rising_edge(clk); end if; start_cycle <= not start_cycle; cycle_type := msg_type; - while rnd.Uniform(0.0, 1.0) > strobe_high_probability loop + while rnd.Uniform(0.0, 1.0) > wishbone_master.p_strobe_high_probability loop wait until rising_edge(clk); end loop; adr <= pop_std_ulogic_vector(request_msg); @@ -88,31 +85,26 @@ begin sel <= pop_std_ulogic_vector(request_msg); else we <= '0'; - -- TODO why sel is not passed in msg for reading (present for writing)? - --sel <= pop_std_ulogic_vector(request_msg); end if; push(acknowledge_queue, request_msg); wait until rising_edge(clk) and stall = '0'; stb <= '0'; - elsif msg_type = wait_until_idle_msg then + elsif msg_type = wait_until_idle_msg or msg_type = wait_for_time_msg then if cycle then wait until not cycle; end if; - handle_wait_until_idle(net, msg_type, request_msg); + handle_sync_message(net, msg_type, request_msg); else - unexpected_msg_type(msg_type); - + unexpected_msg_type(msg_type, get_std_cfg(wishbone_master)); end if; + end loop; end process; p_cycle : process - variable request_msg : msg_t; - variable ack_msg : msg_t; variable pending : natural := 0; - variable received_acks : natural := 0; begin cyc <= '0'; cycle <= false; @@ -139,7 +131,7 @@ begin end process; acknowledge : process - variable request_msg, reply_msg, ack_msg : msg_t; + variable request_msg, reply_msg : msg_t; begin wait until ack = '1' and rising_edge(clk); request_msg := pop(acknowledge_queue); diff --git a/vunit/vhdl/verification_components/src/wishbone_pkg.vhd b/vunit/vhdl/verification_components/src/wishbone_pkg.vhd index 61263b32a..f2a29ab2a 100644 --- a/vunit/vhdl/verification_components/src/wishbone_pkg.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_pkg.vhd @@ -7,48 +7,368 @@ library ieee; use ieee.std_logic_1164.all; -use work.queue_pkg.all; -use work.logger_pkg.all; -use work.memory_pkg.all; +context work.vunit_context; context work.com_context; +use work.sync_pkg.all; +use work.vc_pkg.all; +use work.bus_master_pkg.all; +use work.memory_pkg.all; package wishbone_pkg is + type wishbone_master_t is record + p_bus_handle : bus_master_t; + p_strobe_high_probability : real range 0.0 to 1.0; + end record; + type wishbone_slave_t is record - ack_high_probability : real range 0.0 to 1.0; - stall_high_probability : real range 0.0 to 1.0; -- Private - p_actor : actor_t; - p_ack_actor : actor_t; - p_memory : memory_t; - p_logger : logger_t; + p_std_cfg : std_cfg_t; + p_ack_high_probability : real range 0.0 to 1.0; + p_stall_high_probability : real range 0.0 to 1.0; + p_ack_actor : actor_t; + p_memory : memory_t; end record; - constant wishbone_slave_logger : logger_t := get_logger("vunit_lib:wishbone_slave_pkg"); + constant wishbone_logger : logger_t := get_logger("vunit_lib:wishbone_pkg"); + constant wishbone_checker : checker_t := new_checker(wishbone_logger); + + impure function new_wishbone_master( + data_length : natural; + address_length : natural; + strobe_high_probability : real range 0.0 to 1.0 := 1.0; + byte_length : natural := 8; + logger : logger_t := wishbone_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return wishbone_master_t; + impure function new_wishbone_slave( - memory : memory_t; - ack_high_probability : real := 1.0; - stall_high_probability : real := 0.0; - logger : logger_t := wishbone_slave_logger) - return wishbone_slave_t; + memory : memory_t; + ack_high_probability : real := 1.0; + stall_high_probability : real := 0.0; + logger : logger_t := wishbone_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return wishbone_slave_t; + + impure function as_sync(master : wishbone_master_t) return sync_handle_t; + impure function as_bus_master(master : wishbone_master_t) return bus_master_t; + impure function as_sync(slave : wishbone_slave_t) return sync_handle_t; + + -- Return the actor used by the Wishbone master + impure function get_actor(wishbone_master : wishbone_master_t) return actor_t; + + -- Return the logger used by the Wishbone master + impure function get_logger(wishbone_master : wishbone_master_t) return logger_t; + + -- Return the checker used by the Wishbone master + impure function get_checker(wishbone_master : wishbone_master_t) return checker_t; + + -- Return policy for handling unexpected messages to the actor + impure function unexpected_msg_type_policy(wishbone_master : wishbone_master_t) return unexpected_msg_type_policy_t; + + -- Return the length of the data on the Wishbone bus + impure function data_length(wishbone_master : wishbone_master_t) return natural; + + -- Return the length of the address on the Wishbone bus + impure function address_length(wishbone_master : wishbone_master_t) return natural; + + -- Return the length of a byte on the Wishbone bus + impure function byte_length(wishbone_master : wishbone_master_t) return natural; + + -- Return the length of the byte enable signal on the Wishbone bus + impure function byte_enable_length(wishbone_master : wishbone_master_t) return natural; + + -- Convert natural address to std_logic_vector using the correct number of bits + impure function to_address(constant wishbone_master : wishbone_master_t; address : natural) return std_logic_vector; + + -- Blocking: Write the Wishbone bus + procedure write_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + procedure write_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + + -- Non blocking: Read the Wishbone bus returning a reference to the future reply + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t); + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + variable reference : inout bus_reference_t); + + -- Blocking: Read the Wishbone bus and check result against expected data + procedure check_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := ""); + procedure check_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := ""); + + -- Blocking: read the Wishbone bus with immediate reply + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector); + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + variable data : inout std_logic_vector); + + -- Blocking: Wait until a read from address equals the value using + -- std_match If timeout is reached error with msg + procedure wait_until_read_equals( + signal net : inout network_t; + wishbone_master : wishbone_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Blocking: Wait until a read from address has the bit with this + -- index set to value If timeout is reached error with msg + procedure wait_until_read_bit_equals( + signal net : inout network_t; + wishbone_master : wishbone_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Wait until all operations scheduled before this command has finished + procedure wait_until_idle(signal net : inout network_t; + wishbone_master : wishbone_master_t); + + impure function get_std_cfg(wishbone_master : wishbone_master_t) return std_cfg_t; + impure function get_std_cfg(wishbone_slave : wishbone_slave_t) return std_cfg_t; end package; + package body wishbone_pkg is + impure function new_wishbone_master( + data_length : natural; + address_length : natural; + strobe_high_probability : real range 0.0 to 1.0 := 1.0; + byte_length : natural := 8; + logger : logger_t := wishbone_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return wishbone_master_t is + constant p_bus_handle : bus_master_t := new_bus(data_length, address_length, byte_length, logger, actor, checker, + unexpected_msg_type_policy + ); + begin + return (p_bus_handle => p_bus_handle, + p_strobe_high_probability => strobe_high_probability); + end; impure function new_wishbone_slave( - memory : memory_t; - ack_high_probability : real := 1.0; - stall_high_probability : real := 0.0; - logger : logger_t := wishbone_slave_logger) - return wishbone_slave_t is + memory : memory_t; + ack_high_probability : real := 1.0; + stall_high_probability : real := 0.0; + logger : logger_t := wishbone_logger; + actor : actor_t := null_actor; + checker : checker_t := null_checker; + unexpected_msg_type_policy : unexpected_msg_type_policy_t := fail + ) return wishbone_slave_t is + constant p_std_cfg : std_cfg_t := create_std_cfg( + wishbone_logger, wishbone_checker, actor, logger, checker, unexpected_msg_type_policy + ); + begin - return (p_actor => new_actor, + return (p_std_cfg => p_std_cfg, p_ack_actor => new_actor, p_memory => to_vc_interface(memory, logger), - p_logger => logger, - ack_high_probability => ack_high_probability, - stall_high_probability => stall_high_probability + p_ack_high_probability => ack_high_probability, + p_stall_high_probability => stall_high_probability ); end; + impure function as_sync(master : wishbone_master_t) return sync_handle_t is + begin + return as_sync(master.p_bus_handle); + end; + + impure function as_bus_master(master : wishbone_master_t) return bus_master_t is + begin + return master.p_bus_handle; + end; + + impure function as_sync(slave : wishbone_slave_t) return sync_handle_t is + begin + return get_actor(slave.p_std_cfg); + end; + + impure function get_actor(wishbone_master : wishbone_master_t) return actor_t is + begin + return get_actor(wishbone_master.p_bus_handle); + end; + + impure function get_logger(wishbone_master : wishbone_master_t) return logger_t is + begin + return get_logger(wishbone_master.p_bus_handle); + end; + + impure function get_checker(wishbone_master : wishbone_master_t) return checker_t is + begin + return get_checker(wishbone_master.p_bus_handle); + end; + + impure function unexpected_msg_type_policy(wishbone_master : wishbone_master_t) return unexpected_msg_type_policy_t is + begin + return unexpected_msg_type_policy(wishbone_master.p_bus_handle); + end; + + impure function data_length(wishbone_master : wishbone_master_t) return natural is + begin + return data_length(wishbone_master.p_bus_handle); + end; + + impure function address_length(wishbone_master : wishbone_master_t) return natural is + begin + return address_length(wishbone_master.p_bus_handle); + end; + + impure function byte_length(wishbone_master : wishbone_master_t) return natural is + begin + return byte_length(wishbone_master.p_bus_handle); + end; + + impure function byte_enable_length(wishbone_master : wishbone_master_t) return natural is + begin + return byte_enable_length(wishbone_master.p_bus_handle); + end; + + impure function to_address(constant wishbone_master : wishbone_master_t; address : natural) return std_logic_vector is + begin + return to_address(wishbone_master.p_bus_handle, address); + end; + + procedure write_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, wishbone_master.p_bus_handle, address, data, byte_enable); + end; + + procedure write_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + constant data : std_logic_vector; + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, wishbone_master.p_bus_handle, address, data, byte_enable); + end; + + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t) is + begin + read_bus(net, wishbone_master.p_bus_handle, address, reference); + end; + + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + variable reference : inout bus_reference_t) is + begin + read_bus(net, wishbone_master.p_bus_handle, address, reference); + end; + + procedure check_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, wishbone_master.p_bus_handle, address, expected, msg); + end; + + procedure check_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, wishbone_master.p_bus_handle, address, expected, msg); + end; + + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector) is + begin + read_bus(net, wishbone_master.p_bus_handle, address, data); + end; + + procedure read_bus(signal net : inout network_t; + constant wishbone_master : wishbone_master_t; + constant address : natural; + variable data : inout std_logic_vector) is + begin + read_bus(net, wishbone_master.p_bus_handle, address, data); + end; + + procedure wait_until_read_equals( + signal net : inout network_t; + wishbone_master : wishbone_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_equals(net, wishbone_master.p_bus_handle, addr, value, timeout, msg); + end; + + procedure wait_until_read_bit_equals( + signal net : inout network_t; + wishbone_master : wishbone_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_bit_equals(net, wishbone_master.p_bus_handle, addr, idx, value, timeout, msg); + end; + + procedure wait_until_idle(signal net : inout network_t; + wishbone_master : wishbone_master_t) is + begin + wait_until_idle(net, wishbone_master.p_bus_handle); + end; + + impure function get_std_cfg(wishbone_master : wishbone_master_t) return std_cfg_t is + begin + return get_std_cfg(wishbone_master.p_bus_handle); + end; + + impure function get_std_cfg(wishbone_slave : wishbone_slave_t) return std_cfg_t is + begin + return wishbone_slave.p_std_cfg; + end; + + + end package body; diff --git a/vunit/vhdl/verification_components/src/wishbone_slave.vhd b/vunit/vhdl/verification_components/src/wishbone_slave.vhd index 11e15fdb9..e1cd4f60b 100644 --- a/vunit/vhdl/verification_components/src/wishbone_slave.vhd +++ b/vunit/vhdl/verification_components/src/wishbone_slave.vhd @@ -17,6 +17,8 @@ context work.vunit_context; context work.com_context; use work.memory_pkg.all; use work.wishbone_pkg.all; +use work.sync_pkg.all; +use work.vc_pkg.all; library osvvm; use osvvm.RandomPkg.all; @@ -40,11 +42,29 @@ entity wishbone_slave is end entity; architecture a of wishbone_slave is - constant slave_write_msg : msg_type_t := new_msg_type("wb slave write"); constant slave_read_msg : msg_type_t := new_msg_type("wb slave read"); + + signal active_transaction : boolean := false; begin + main : process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + receive(net, get_actor(wishbone_slave.p_std_cfg), request_msg); + msg_type := message_type(request_msg); + + if msg_type = wait_for_time_msg or msg_type = wait_until_idle_msg then + while active_transaction or has_message(wishbone_slave.p_ack_actor) loop + wait until rising_edge(clk); + end loop; + handle_sync_message(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type, wishbone_slave.p_std_cfg); + end if; + end process; + request : process variable wr_request_msg : msg_t; variable rd_request_msg : msg_t; @@ -72,14 +92,16 @@ begin variable rnd : RandomPType; begin ack <= '0'; + active_transaction <= false; receive(net, wishbone_slave.p_ack_actor, request_msg); + active_transaction <= true; msg_type := message_type(request_msg); if msg_type = slave_write_msg then addr := pop_integer(request_msg); data := pop_std_ulogic_vector(request_msg); write_word(wishbone_slave.p_memory, addr, data); - while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop + while rnd.Uniform(0.0, 1.0) > wishbone_slave.p_ack_high_probability loop wait until rising_edge(clk); end loop; ack <= '1'; @@ -90,7 +112,7 @@ begin data := (others => '0'); addr := pop_integer(request_msg); data := read_word(wishbone_slave.p_memory, addr, sel'length); - while rnd.Uniform(0.0, 1.0) > wishbone_slave.ack_high_probability loop + while rnd.Uniform(0.0, 1.0) > wishbone_slave.p_ack_high_probability loop wait until rising_edge(clk); end loop; dat_o <= data; @@ -99,14 +121,14 @@ begin ack <= '0'; else - unexpected_msg_type(msg_type); + unexpected_msg_type(msg_type, wishbone_slave.p_std_cfg); end if; end process; stall_stim: process variable rnd : RandomPType; begin - if rnd.Uniform(0.0, 1.0) < wishbone_slave.stall_high_probability then + if rnd.Uniform(0.0, 1.0) < wishbone_slave.p_stall_high_probability then stall <= '1'; else stall <= '0'; diff --git a/vunit/vhdl/verification_components/test/tb_avalon.vhd b/vunit/vhdl/verification_components/test/tb_avalon.vhd index 497910298..3b24a7748 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon.vhd @@ -27,7 +27,14 @@ architecture testbench of tb_avalon is signal av_byteenable : std_logic_vector(3 downto 0); signal av_burstcount : std_logic_vector(3 downto 0); - constant avalon_bus : bus_master_t := new_bus(data_length => 32, address_length => av_address'length); + constant avalon_master : avalon_master_t := new_avalon_master( + data_length => 32, + address_length => av_address'length, + use_readdatavalid => false, + fixed_read_latency => 0 + ); + + constant avalon_bus : bus_master_t := as_bus_master(avalon_master); signal clk : std_logic := '0'; @@ -35,11 +42,9 @@ architecture testbench of tb_avalon is begin - avalon_master : entity vunit_lib.avalon_master + dut : entity vunit_lib.avalon_master generic map ( - bus_handle => avalon_bus, - use_readdatavalid => false, - fixed_read_latency => 0 + avalon_master_handle => avalon_master ) port map ( clk => clk, diff --git a/vunit/vhdl/verification_components/test/tb_avalon_master.vhd b/vunit/vhdl/verification_components/test/tb_avalon_master.vhd index e84003042..58b549971 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_master.vhd @@ -69,9 +69,14 @@ architecture a of tb_avalon_master is constant master_logger : logger_t := get_logger("master"); constant tb_logger : logger_t := get_logger("tb"); constant master_actor : actor_t := new_actor("Avalon-MM Master"); - constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.data_width, - address_length => tb_cfg.addr_width, logger => master_logger, - actor => master_actor); + constant avalon_master : avalon_master_t := new_avalon_master(data_length => tb_cfg.data_width, + address_length => tb_cfg.addr_width, + logger => master_logger, + actor => master_actor, + write_high_probability => tb_cfg.write_prob, + read_high_probability => tb_cfg.read_prob + ); + constant bus_handle : bus_master_t := as_bus_master(avalon_master); constant memory : memory_t := new_memory; constant buf : buffer_t := allocate(memory, tb_cfg.transfers * byteenable'length); @@ -79,7 +84,7 @@ architecture a of tb_avalon_master is memory => memory, readdatavalid_high_probability => tb_cfg.readdatavalid_prob, waitrequest_high_probability => tb_cfg.waitrequest_prob, - name => "Avalon-MM Slave" + actor => new_actor("Avalon-MM Slave") ); procedure gen_rndburst( @@ -290,9 +295,7 @@ begin dut : entity work.avalon_master generic map ( - bus_handle => bus_handle, - write_high_probability => tb_cfg.write_prob, - read_high_probability => tb_cfg.read_prob + avalon_master_handle => avalon_master ) port map ( clk => clk, diff --git a/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd b/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd index 1c28271c2..27663c719 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_slave.vhd @@ -65,7 +65,7 @@ architecture a of tb_avalon_slave is constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * byteenable'length); constant avalon_slave : avalon_slave_t := new_avalon_slave(memory => memory, - name => "avmm_vc", + actor => new_actor("avmm_vc"), readdatavalid_high_probability => tb_cfg.readdatavalid_prob, waitrequest_high_probability => tb_cfg.waitrequest_prob ); diff --git a/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd b/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd index ae94397cf..7324cd4eb 100644 --- a/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd +++ b/vunit/vhdl/verification_components/test/tb_avalon_stream.vhd @@ -15,6 +15,7 @@ context work.data_types_context; use work.avalon_stream_pkg.all; use work.stream_master_pkg.all; use work.stream_slave_pkg.all; +use work.vc_pkg.all; entity tb_avalon_stream is generic (runner_cfg : string); @@ -46,7 +47,7 @@ begin begin test_runner_setup(runner, runner_cfg); set_format(display_handler, verbose, true); - show(avalon_sink_stream.p_logger, display_handler, verbose); + show(get_logger(avalon_sink_stream.p_std_cfg), display_handler, verbose); wait until rising_edge(clk); if run("test single push and pop") then diff --git a/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd b/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd index 935587b43..44c266a39 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd @@ -192,7 +192,7 @@ begin wait until (bready and bvalid) = '1' and rising_edge(clk); bvalid <= '0'; wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "bresp - Got AXI response SLVERR(10) expected OKAY(00)", failure); + check_only_log(bus_logger, "Check failed for bresp - Got AXI response SLVERR(10) expected OKAY(00)", failure); unmock(bus_logger); done <= true; @@ -234,7 +234,7 @@ begin wait until (bready and bvalid) = '1' and rising_edge(clk); bvalid <= '0'; wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "bresp - Got AXI response EXOKAY(01) expected DECERR(11)", failure); + check_only_log(bus_logger, "Check failed for bresp - Got AXI response EXOKAY(01) expected DECERR(11)", failure); unmock(bus_logger); done <= true; @@ -265,7 +265,7 @@ begin wait until (rready and rvalid) = '1' and rising_edge(clk); rvalid <= '0'; wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected OKAY(00)", failure); + check_only_log(bus_logger, "Check failed for rresp - Got AXI response DECERR(11) expected OKAY(00)", failure); unmock(bus_logger); done <= true; @@ -297,7 +297,7 @@ begin wait until (rready and rvalid) = '1' and rising_edge(clk); rvalid <= '0'; wait until mock_queue_length > 0 for 0 ns; - check_only_log(bus_logger, "rresp - Got AXI response DECERR(11) expected EXOKAY(01)", failure); + check_only_log(bus_logger, "Check failed for rresp - Got AXI response DECERR(11) expected EXOKAY(01)", failure); unmock(bus_logger); done <= true; diff --git a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd index 8181a3a8f..2ce841884 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_stream.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_stream.vhd @@ -166,10 +166,11 @@ begin check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); end loop; elsif run("test reset") then - wait until rising_edge(aclk); + -- The protocol checker does all the checking + push_stream(net, master_stream, x"77", true); + wait until tvalid; areset_n <= '0'; wait until rising_edge(aclk); - check_equal(tvalid, '0', result("for valid low check while in reset")); areset_n <= '1'; wait until rising_edge(aclk); @@ -225,13 +226,13 @@ begin elsif run("test single stalled push and pop") then wait until rising_edge(aclk); - wait_for_time(net, master_sync, 30 ns); + wait_for_time(net, master_sync, 25 ns); timestamp := now; push_stream(net, master_stream, x"77", true); pop_stream(net, slave_stream, data, last_bool); check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); -- two extra cycles inserted by alignment + check_equal(now, timestamp + 40 ns, result("for push wait time")); -- two extra cycles inserted by alignment for i in 1 to n_monitors loop get_axi_stream_transaction(axi_stream_transaction); check_equal( @@ -244,33 +245,13 @@ begin elsif run("test single push and stalled pop") then wait until rising_edge(aclk); - wait_for_time(net, slave_sync, 30 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 50 ns, result("for push wait time")); - - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - - elsif run("test single push and stalled pop with non-multiple of clock period") then - wait until rising_edge(aclk); - wait_for_time(net, slave_sync, 29 ns); + wait_for_time(net, slave_sync, 25 ns); timestamp := now; push_stream(net, master_stream, x"77", true); pop_stream(net, slave_stream, data, last_bool); check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); + check_equal(now, timestamp + 40 ns, result("for push wait time")); for i in 1 to n_monitors loop get_axi_stream_transaction(axi_stream_transaction); @@ -282,25 +263,6 @@ begin check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); end loop; - elsif run("test single stalled push and pop with non-multiple of clock period") then - wait until rising_edge(aclk); - wait_for_time(net, master_sync, 29 ns); - timestamp := now; - push_stream(net, master_stream, x"77", true); - pop_stream(net, slave_stream, data, last_bool); - check_equal(data, std_logic_vector'(x"77"), result("for pop stream data")); - check_true(last_bool, result("for pop stream last")); - check_equal(now - 10 ns, timestamp + 40 ns, result("for push wait time")); -- Aligned to clock edge again - for i in 1 to n_monitors loop - get_axi_stream_transaction(axi_stream_transaction); - check_equal( - axi_stream_transaction.tdata, - std_logic_vector'(x"77"), - result("for axi_stream_transaction.tdata") - ); - check_true(axi_stream_transaction.tlast, result("for axi_stream_transaction.tlast")); - end loop; - elsif run("test pop before push") then for i in 0 to 7 loop pop_stream(net, slave_stream, reference); @@ -526,6 +488,34 @@ begin check_equal(now, timestamp + 20 ns, " transaction time incorrect"); + elsif run("test that the master waits on its subcomponents to be idle") then + timestamp := now; + wait_for_time(net, as_sync(master_axi_stream.p_monitor), 1 ns); + wait_until_idle(net, as_sync(master_axi_stream)); + check_equal(now, timestamp + 1 ns); + + timestamp := now; + wait_for_time(net, as_sync(master_axi_stream.p_protocol_checker), 2 ns); + wait_until_idle(net, as_sync(master_axi_stream)); + check_equal(now, timestamp + 2 ns); + + elsif run("test that the slave waits on its subcomponents to be idle") then + timestamp := now; + wait_for_time(net, as_sync(slave_axi_stream.p_monitor), 1 ns); + wait_until_idle(net, as_sync(slave_axi_stream)); + check_equal(now, timestamp + 1 ns); + + timestamp := now; + wait_for_time(net, as_sync(slave_axi_stream.p_protocol_checker), 2 ns); + wait_until_idle(net, as_sync(slave_axi_stream)); + check_equal(now, timestamp + 2 ns); + + elsif run("test that the monitor waits on its subcomponents to be idle") then + timestamp := now; + wait_for_time(net, as_sync(monitor.p_protocol_checker), 1 ns); + wait_until_idle(net, as_sync(monitor)); + check_equal(now, timestamp + 1 ns); + elsif run("test random stall on master") or run("test random pop stall on slave") then wait until rising_edge(aclk); for i in 0 to 100 loop @@ -600,22 +590,22 @@ begin not_valid <= not tvalid; not_valid_data <= '1' when tdata = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_data, "Invalid data not X"); + check_true(aclk, not_valid, not_valid_data, "Invalid data not X", active_clock_edge => falling_edge); not_valid_keep <= '1' when tkeep = std_logic_vector'("X") else '0'; - check_true(aclk, not_valid, not_valid_keep, "Invalid keep not X"); + check_true(aclk, not_valid, not_valid_keep, "Invalid keep not X", active_clock_edge => falling_edge); not_valid_strb <= '1' when tstrb = std_logic_vector'("X") else '0'; - check_true(aclk, not_valid, not_valid_strb, "Invalid strb not X"); + check_true(aclk, not_valid, not_valid_strb, "Invalid strb not X", active_clock_edge => falling_edge); GEN_CHECK_INVALID_ID: if g_id_length > 0 generate not_valid_id <= '1' when tid = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_id, "Invalid id not X"); + check_true(aclk, not_valid, not_valid_id, "Invalid id not X", active_clock_edge => falling_edge); end generate; GEN_CHECK_INVALID_DEST: if g_dest_length > 0 generate not_valid_dest <= '1' when tdest = std_logic_vector'("XXXXXXXX") else '0'; - check_true(aclk, not_valid, not_valid_dest, "Invalid dest not X"); + check_true(aclk, not_valid, not_valid_dest, "Invalid dest not X", active_clock_edge => falling_edge); end generate; GEN_CHECK_INVALID_USER: if g_user_length > 0 generate not_valid_user <= '1' when tuser = std_logic_vector'("00000000") else '0'; - check_true(aclk, not_valid, not_valid_user, "Invalid user not 0"); + check_true(aclk, not_valid, not_valid_user, "Invalid user not 0", active_clock_edge => falling_edge); end generate; axi_stream_slave_inst : entity work.axi_stream_slave diff --git a/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd b/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd index 0e095953c..137b79947 100644 --- a/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd +++ b/vunit/vhdl/verification_components/test/tb_axi_stream_protocol_checker.vhd @@ -7,6 +7,7 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; +use ieee.numeric_std_unsigned.all; context work.vunit_context; context work.com_context; @@ -15,6 +16,7 @@ use work.axi_stream_pkg.all; use work.stream_master_pkg.all; use work.stream_slave_pkg.all; use work.runner_pkg.all; +use work.sync_pkg.all; entity tb_axi_stream_protocol_checker is generic( @@ -46,11 +48,12 @@ architecture a of tb_axi_stream_protocol_checker is ); constant meta_values : std_logic_vector(1 to 5) := "-XWZU"; constant valid_values : std_logic_vector(1 to 4) := "01LH"; - + constant clk_period : time := 10 ns; begin main : process variable rule_logger : logger_t; + variable timestamp : time; procedure pass_stable_test (signal d : out std_logic_vector) is constant zeros : std_logic_vector(d'range) := (others => '0'); @@ -387,14 +390,12 @@ begin elsif run("Test passing check of that tready must not be unknown unless in reset") then wait until rising_edge(aclk); areset_n <= '0'; - tvalid <= '1'; for i in meta_values'range loop tready <= meta_values(i); wait until rising_edge(aclk); end loop; areset_n <= '1'; - tvalid <= '0'; - tready <= valid_values(1); + tready <= valid_values(1); wait until rising_edge(aclk); tvalid <= '1'; for i in valid_values'range loop @@ -423,6 +424,12 @@ begin tvalid <= '1'; tlast <= '0'; tready <= '1'; + tid <= (others => '0'); + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '0'; + tready <= '1'; + tid <= (others => '1'); wait until rising_edge(aclk); tready <= '0'; wait until rising_edge(aclk); @@ -433,6 +440,12 @@ begin tvalid <= '1'; tlast <= '1'; tready <= '1'; + tid <= (others => '0'); + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '1'; + tready <= '1'; + tid <= (others => '1'); wait until rising_edge(aclk); elsif run("Test failing check of that all packets are complete when the simulation ends") then @@ -444,6 +457,12 @@ begin tvalid <= '1'; tlast <= '0'; tready <= '1'; + tid <= (others => '0'); + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '0'; + tready <= '1'; + tid <= (others => '1'); wait until rising_edge(aclk); tlast <= '0'; wait until rising_edge(aclk); @@ -452,10 +471,21 @@ begin notify(runner); entry_gate(runner); - check_only_log(rule_logger, "Unconditional check failed for packet completion for the following streams: 0.", error); + check_only_log(rule_logger, "Unconditional check failed for packet completion for the following streams: 0, 15.", error); unmock(rule_logger); + elsif run("Test waiting until idle") then + wait until rising_edge(aclk); + tvalid <= '1'; + tlast <= '0', '1' after 10.5 * clk_period; + tready <= '1'; + tid <= (others => '0'); + wait until rising_edge(aclk); + timestamp := now; + wait_until_idle(net, as_sync(protocol_checker)); + check_equal(now, timestamp + 10 * clk_period); + elsif run("Test passing check of that tuser must not be unknown unless in reset") then pass_unknown_test(tuser, areset_n, areset_n); @@ -599,6 +629,45 @@ begin unmock(rule_logger); + elsif run("Test passing check of that tvalid must go low immediately on assert") then + areset_n <= '1'; + tvalid <= '1'; + tready <= '1'; + wait until rising_edge(aclk); + wait for 0 ns; + areset_n <= '0'; + wait for clk_period * 9 / 10; + tvalid <= '0'; + wait until rising_edge(aclk); + areset_n <= '1'; + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + + elsif run("Test failing check of that tvalid must go low immediately on assert") then + rule_logger := get_logger(get_name(logger) & ":rule 23"); + mock(rule_logger); + + areset_n <= '1'; + tvalid <= '1'; + tready <= '1'; + wait until rising_edge(aclk); + wait for 0 ns; + areset_n <= '0'; + wait until rising_edge(aclk); + tvalid <= '0'; + areset_n <= '1'; + wait until rising_edge(aclk); + tvalid <= '1'; + wait until rising_edge(aclk); + + check_only_log( + rule_logger, + "Implication check failed for tvalid de-asserted asynchronously when areset_n is asserted", + error); + + unmock(rule_logger); + end if; test_runner_cleanup(runner); @@ -624,5 +693,5 @@ begin tuser => tuser ); - aclk <= not aclk after 5 ns; + aclk <= not aclk after clk_period / 2; end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd b/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd index bc9af90d5..577575cd1 100644 --- a/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd +++ b/vunit/vhdl/verification_components/test/tb_bus_master_pkg.vhd @@ -16,6 +16,7 @@ use work.queue_pkg.all; use work.bus_master_pkg.all; use work.memory_pkg.all; use work.logger_pkg.all; +use work.bus2memory_pkg.all; entity tb_bus_master_pkg is generic (runner_cfg : string); @@ -23,7 +24,8 @@ end entity; architecture a of tb_bus_master_pkg is constant memory : memory_t := new_memory; - constant bus_handle : bus_master_t := new_bus(data_length => 32, address_length => 32); + constant bus2memory_handle : bus2memory_t := new_bus2memory(data_length => 32, address_length => 32, memory => memory); + constant bus_handle : bus_master_t := as_bus_master(bus2memory_handle); constant logger : logger_t := get_logger("logger"); constant actor : actor_t := new_actor("actor"); constant custom_logger_and_actor_bus_handle : bus_master_t := @@ -92,14 +94,13 @@ begin unmock(bus_logger); elsif run("test custom logger and actor bus master") then - check(custom_logger_and_actor_bus_handle.p_logger = logger); - check(custom_logger_and_actor_bus_handle.p_actor = actor); + check(get_logger(custom_logger_and_actor_bus_handle) = logger); + check(get_actor(custom_logger_and_actor_bus_handle) = actor); end if; test_runner_cleanup(runner); end process; bus2memory_inst : entity work.bus2memory generic map ( - bus_handle => bus_handle, - memory => memory); + bus2memory_handle => bus2memory_handle); end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_ram_master.vhd b/vunit/vhdl/verification_components/test/tb_ram_master.vhd index c16d1ed9c..93ca965c3 100644 --- a/vunit/vhdl/verification_components/test/tb_ram_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_ram_master.vhd @@ -13,6 +13,7 @@ context vunit_lib.vunit_context; context work.com_context; use work.queue_pkg.all; +use work.ram_master_pkg.all; use work.bus_master_pkg.all; entity tb_ram_master is @@ -30,14 +31,14 @@ architecture a of tb_ram_master is signal wdata : std_logic_vector(31 downto 0); signal rdata : std_logic_vector(31 downto 0) := (others => '0'); - constant bus_handle : bus_master_t := new_bus(data_length => wdata'length, address_length => addr'length); + constant ram_master : ram_master_t := new_ram_master(data_length => wdata'length, address_length => addr'length, latency => latency); signal start, done : boolean := false; begin main : process variable reference : bus_reference_t; - variable reference_queue : queue_t := new_queue; + constant reference_queue : queue_t := new_queue; variable tmp : std_logic_vector(rdata'range); begin test_runner_setup(runner, runner_cfg); @@ -45,18 +46,18 @@ begin wait for 0 ns; if run("Test single write") then - write_bus(net, bus_handle, x"77", x"11223344"); + write_bus(net, ram_master, x"77", x"11223344"); elsif run("Test single write with byte enable") then - write_bus(net, bus_handle, x"77", x"11223344", byte_enable => "0101"); + write_bus(net, ram_master, x"77", x"11223344", byte_enable => "0101"); elsif run("Test single read") then - read_bus(net, bus_handle, x"33", tmp); + read_bus(net, ram_master, x"33", tmp); check_equal(tmp, std_logic_vector'(x"55667788"), "read data"); elsif run("Test read back to back") then for i in 1 to num_back_to_back_reads loop - read_bus(net, bus_handle, std_logic_vector(to_unsigned(i, addr'length)), reference); + read_bus(net, ram_master, std_logic_vector(to_unsigned(i, addr'length)), reference); push(reference_queue, reference); end loop; @@ -143,8 +144,7 @@ begin dut : entity work.ram_master generic map ( - bus_handle => bus_handle, - latency => latency) + ram_master => ram_master) port map ( clk => clk, en => en, diff --git a/vunit/vhdl/verification_components/test/tb_vc_pkg.vhd b/vunit/vhdl/verification_components/test/tb_vc_pkg.vhd new file mode 100644 index 000000000..fcad4e53a --- /dev/null +++ b/vunit/vhdl/verification_components/test/tb_vc_pkg.vhd @@ -0,0 +1,74 @@ +-- 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-2021, Lars Asplund lars.anders.asplund@gmail.com + +library vunit_lib; +context vunit_lib.vunit_context; +context work.com_context; +use work.vc_pkg.all; + +entity tb_vc_pkg is + generic(runner_cfg : string); +end entity; + +architecture a of tb_vc_pkg is +begin + + main : process + variable std_cfg : std_cfg_t; + constant default_logger1 : logger_t := get_logger("default_logger1"); + constant default_checker1 : checker_t := new_checker(get_logger("default_checker1")); + constant default_checker_based_on_default_checker1 : checker_t := new_checker(default_logger1); + constant default_logger2 : logger_t := get_logger("default_logger2"); + constant default_checker2 : checker_t := new_checker(get_logger("default_checker2")); + begin + test_runner_setup(runner, runner_cfg); + while test_suite loop + if run("Test that no warnings are issued for first std cfg using the default logger") then + mock(default_logger1, warning); + std_cfg := create_std_cfg(default_logger1, default_checker1); + std_cfg := create_std_cfg(default_logger2, default_checker2); + check_no_log; + unmock(default_logger1); + + elsif run("Test that no warnings are issued for when a std cfg use the default logger for the default checker") then + mock(default_logger1, warning); + std_cfg := create_std_cfg(default_logger1, default_checker_based_on_default_checker1); + check_no_log; + unmock(default_logger1); + + elsif run("Test that no warnings are issued for when the input logger to std cfg reuse the default logger") then + mock(default_logger1, warning); + std_cfg := create_std_cfg(default_logger1, default_checker1); + std_cfg := create_std_cfg(default_logger2, default_checker2, logger => default_logger1); + check_no_log; + unmock(default_logger1); + + elsif run("Test that a warning is issued when a std cfg is created with an already used default logger") then + mock(default_logger1, warning); + std_cfg := create_std_cfg(default_logger1, default_checker1); + std_cfg := create_std_cfg(default_logger1, default_checker2); + check_only_log(default_logger1, "This logger is already used by another VC. Source VC for log messages is ambiguous.", warning); + unmock(default_logger1); + + elsif run("Test that a warning is issued when a std cfg is created with a default checker using an already used default logger") then + mock(default_logger1, warning); + std_cfg := create_std_cfg(default_logger1, default_checker1); + std_cfg := create_std_cfg(default_logger2, default_checker_based_on_default_checker1); + check_only_log(default_logger1, "This logger is already used by another VC. Source VC for log messages is ambiguous.", warning); + unmock(default_logger1); + + elsif run("Test that a warning is issued when a std cfg is created with a default checker using an logger already used by a default checker") then + mock(get_logger(default_checker1), warning); + std_cfg := create_std_cfg(default_logger1, default_checker1); + std_cfg := create_std_cfg(default_logger2, default_checker1); + check_only_log(get_logger(default_checker1), "This logger is already used by another VC. Source VC for log messages is ambiguous.", warning); + unmock(get_logger(default_checker1)); + + end if; + end loop; + test_runner_cleanup(runner, fail_on_warning => true); + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd b/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd index 98e7f6886..df3774d9f 100644 --- a/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_wishbone_master.vhd @@ -63,8 +63,8 @@ architecture a of tb_wishbone_master is constant master_logger : logger_t := get_logger("master"); constant tb_logger : logger_t := get_logger("tb"); - constant bus_handle : bus_master_t := new_bus(data_length => tb_cfg.dat_width, - address_length => tb_cfg.adr_width, logger => master_logger); + constant wishbone_master : wishbone_master_t := new_wishbone_master(data_length => tb_cfg.dat_width, + address_length => tb_cfg.adr_width, strobe_high_probability => tb_cfg.strobe_prob, logger => master_logger); constant memory : memory_t := new_memory; constant buf : buffer_t := allocate(memory, tb_cfg.num_cycles * sel'length); @@ -79,7 +79,7 @@ begin main_stim : process variable tmp : std_logic_vector(dat_i'range); variable chk_time : time; - variable value : std_logic_vector(dat_i'range) := (others => '1'); + constant value : std_logic_vector(dat_i'range) := (others => '1'); variable bus_rd_ref1 : bus_reference_t; variable bus_rd_ref2 : bus_reference_t; type bus_reference_arr_t is array (0 to tb_cfg.num_cycles-1) of bus_reference_t; @@ -96,42 +96,42 @@ begin if run("wr single rd single") then info(tb_logger, "Writing..."); - write_bus(net, bus_handle, 0, value); + write_bus(net, wishbone_master, 0, value); wait until ack = '1' and rising_edge(clk); wait until rising_edge(clk); wait for 100 ns; info(tb_logger, "Reading..."); - read_bus(net, bus_handle, 0, tmp); + read_bus(net, wishbone_master, 0, tmp); check_equal(tmp, value, "read data"); -- elsif run("wr block") then -- -- TODO not sure if is allowed to toggle signal we during -- -- wishbone single cycle --- write_bus(net, bus_handle, 0, value); --- read_bus(net, bus_handle, 0, tmp); +-- write_bus(net, wishbone_master, 0, value); +-- read_bus(net, wishbone_master, 0, tmp); -- check_equal(tmp, value, "read data"); elsif run("wr block rd single") then info(tb_logger, "Writing..."); for i in 0 to tb_cfg.num_cycles-1 loop - write_bus(net, bus_handle, i*(sel'length), + write_bus(net, wishbone_master, i*(sel'length), std_logic_vector(to_unsigned(i, dat_i'length))); end loop; info(tb_logger, "Reading..."); for i in 0 to tb_cfg.num_cycles-1 loop - read_bus(net, bus_handle, i*(sel'length), tmp); + read_bus(net, wishbone_master, i*(sel'length), tmp); check_equal(tmp, std_logic_vector(to_unsigned(i, dat_i'length)), "read data"); end loop; elsif run("wr block rd block") then info(tb_logger, "Writing..."); for i in 0 to tb_cfg.num_cycles-1 loop - write_bus(net, bus_handle, i*(sel'length), + write_bus(net, wishbone_master, i*(sel'length), std_logic_vector(to_unsigned(i, dat_i'length))); end loop; info(tb_logger, "Reading..."); for i in 0 to tb_cfg.num_cycles-1 loop - read_bus(net, bus_handle, i*(sel'length), rd_ref(i)); + read_bus(net, wishbone_master, i*(sel'length), rd_ref(i)); end loop; info(tb_logger, "Get reads by references..."); @@ -143,11 +143,11 @@ begin elsif run ("wait for idle") then for i in 0 to 5 loop chk_time := now; - read_bus(net, bus_handle, 0, bus_rd_ref1); - write_bus(net, bus_handle, 0, value); - read_bus(net, bus_handle, 0, bus_rd_ref2); + read_bus(net, wishbone_master, 0, bus_rd_ref1); + write_bus(net, wishbone_master, 0, value); + read_bus(net, wishbone_master, 0, bus_rd_ref2); check_equal(chk_time, now, "tb concept broken, command issueing takes time"); - wait_until_idle(net, bus_handle); + wait_until_idle(net, wishbone_master); check_equal(cyc, '0', "bus not idle"); info(tb_logger, "Waited for " & to_string(now-chk_time)); chk_time := now; @@ -159,7 +159,7 @@ begin end loop; elsif run("slave comb ack") then - write_bus(net, bus_handle, 0, value); + write_bus(net, wishbone_master, 0, value); wait until ack = '1' and rising_edge(clk); wait for 20 ns; @@ -174,8 +174,7 @@ begin dut : entity work.wishbone_master generic map ( - bus_handle => bus_handle, - strobe_high_probability => tb_cfg.strobe_prob) + wishbone_master => wishbone_master) port map ( clk => clk, adr => adr, diff --git a/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd b/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd index eacfcd03e..7cd86c2ed 100644 --- a/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd +++ b/vunit/vhdl/verification_components/test/tb_wishbone_slave.vhd @@ -69,8 +69,6 @@ architecture a of tb_wishbone_slave is begin main_stim : process - variable tmp : std_logic_vector(dat_i'range); - variable value : std_logic_vector(dat_i'range) := (others => '1'); begin test_runner_setup(runner, runner_cfg); set_format(display_handler, verbose, true); diff --git a/vunit/vhdl_parser.py b/vunit/vhdl_parser.py index 7d5277fea..17b0af866 100644 --- a/vunit/vhdl_parser.py +++ b/vunit/vhdl_parser.py @@ -303,7 +303,8 @@ def add_generic(self, identifier, subtype_code, init_value=None): """ self.generics.append( VHDLInterfaceElement( - identifier, + "constant", + [identifier], VHDLSubtypeIndication.parse(subtype_code), init_value=init_value, ) @@ -315,7 +316,8 @@ def add_port(self, identifier, mode, subtype_code, init_value=None): """ self.ports.append( VHDLInterfaceElement( - identifier, + "signal", + [identifier], VHDLSubtypeIndication.parse(subtype_code), init_value=init_value, mode=mode, @@ -521,7 +523,7 @@ def _parse_generic_clause(cls, code): # Ignore function generics continue - generic_list.append(VHDLInterfaceElement.parse(interface_element)) + generic_list.append(VHDLInterfaceElement.parse(interface_element, default_entity_class="constant")) return generic_list @@ -539,7 +541,7 @@ def _parse_port_clause(cls, code): port_list = [] # Add interface elements to the port list for interface_element in interface_elements: - port_list.append(VHDLInterfaceElement.parse(interface_element, is_signal=True)) + port_list.append(VHDLInterfaceElement.parse(interface_element, default_entity_class="signal")) return port_list @@ -618,8 +620,16 @@ class VHDLInterfaceElement(object): Represents a VHDL interface element """ - def __init__(self, identifier, subtype_indication, mode=None, init_value=None): - self.identifier = identifier + def __init__( + self, + entity_class, + identifier_list, + subtype_indication, + mode=None, + init_value=None, + ): + self.entity_class = entity_class + self.identifier_list = identifier_list self.mode = mode self.subtype_indication = subtype_indication self.init_value = init_value @@ -628,21 +638,30 @@ def without_mode(self): """ @returns A copy of this interface element without a mode """ - return VHDLInterfaceElement(self.identifier, self.subtype_indication, init_value=self.init_value) + return VHDLInterfaceElement( + self.entity_class, + self.identifier_list, + self.subtype_indication, + init_value=self.init_value, + ) @classmethod - def parse(cls, code, is_signal=False): + def parse(cls, code, default_entity_class=None): """ Returns a new instance by parsing the code """ - if is_signal: - # Remove 'signal' string if a signal is beeing parsed - code = code.replace("signal", "") + entity_class_split = code.split(None, 1) + if cls._is_interface_entity_class(entity_class_split[0]): + entity_class = entity_class_split[0] + code = entity_class_split[1] + else: + entity_class = default_entity_class interface_element_string = code - # Extract the identifier - identifier = interface_element_string.split(":")[0].strip() + # Extract the identifier_list + identifier_list_string = interface_element_string.split(":")[0].strip() + identifier_list = [identifier.strip() for identifier in identifier_list_string.split(",")] # Extract subtype indication and mode (if any) @@ -661,7 +680,7 @@ def parse(cls, code, is_signal=False): else: init_value = None - return cls(identifier, subtype_indication, mode, init_value) + return cls(entity_class, identifier_list, subtype_indication, mode, init_value) @staticmethod def _is_mode(code): @@ -670,8 +689,15 @@ def _is_mode(code): """ return code in ("in", "out", "inout", "buffer", "linkage") + @staticmethod + def _is_interface_entity_class(code): + """ + Return True if the code is an interface entity class + """ + return code in ("constant", "variable", "signal", "file") + def __str__(self): - code = self.identifier + " : " + code = self.identifier_list + " : " if self.mode is not None: code += self.mode + " " @@ -717,14 +743,6 @@ def find(cls, code): yield cls(identifier, literals) -class VHDLElementDeclaration(object): - """Represents a VHDL element declaration""" - - def __init__(self, identifier_list, subtype_indication): - self.identifier_list = identifier_list - self.subtype_indication = subtype_indication - - class VHDLRecordType(object): """Represents a VHDL record type""" @@ -760,7 +778,7 @@ def find(cls, code): identifier_list_and_subtype_indication = element.split(":") identifier_list = [i.strip() for i in identifier_list_and_subtype_indication[0].split(",")] subtype_indication = VHDLSubtypeIndication.parse(identifier_list_and_subtype_indication[1].strip()) - parsed_elements.append(VHDLElementDeclaration(identifier_list, subtype_indication)) + parsed_elements.append(VHDLInterfaceElement(None, identifier_list, subtype_indication)) yield cls(identifier, parsed_elements) @@ -934,7 +952,7 @@ class VHDLReference(object): Reference to design unit """ - _reference_types = ("package", "context", "entity", "configuration") + _reference_types = ("package", "context", "entity", "configuration", "library") _uses_re = re.compile( r""" @@ -949,31 +967,32 @@ class VHDLReference(object): re.MULTILINE | re.IGNORECASE | re.VERBOSE, ) - @classmethod - def _find_uses(cls, code): - """ - Find all the libraries and use clasues within the code + @staticmethod + def get_ids(match): """ + Get all ids found within the match taking the optinal extra ids of + library and use clauses into account such as: - def get_ids(match): - """ - Get all ids found within the match taking the optinal extra ids of - library and use clauses into account such as: + use foo, bar; - use foo, bar; + or - or + library foo, bar; + """ + ids = [match.group("id").strip()] + if match.group("extra"): + ids += [name.strip() for name in match.group("extra").split(",")[1:]] + return ids - library foo, bar; - """ - ids = [match.group("id").strip()] - if match.group("extra"): - ids += [name.strip() for name in match.group("extra").split(",")[1:]] - return ids + @classmethod + def _find_uses(cls, code): + """ + Find all the use and context clauses within the code + """ references = [] for match in cls._uses_re.finditer(code): - for uses in get_ids(match): + for uses in cls.get_ids(match): uses = uses.split(".") names_within = uses[2:] if len(uses) > 2 else (None,) @@ -1040,6 +1059,33 @@ def _find_package_instance_references(cls, code): references.append(cls("package", match.group("lib"), match.group("name"))) return references + _library_re = re.compile( + r""" + \b # Word boundary + library # library keyword + \s+ # At least one whitespace + (?P[a-zA-Z][\w]*) # First library identifier + (?P(\s*,\s*[a-zA-Z][\w]*)*) # Extra library identifiers + \s* # Potential whitespaces + ; # Semi-colon + """, + re.MULTILINE | re.IGNORECASE | re.VERBOSE, + ) + + @classmethod + def _find_library_references(cls, code): + """ + Finds all library clauses + """ + + references = [] + for match in cls._library_re.finditer(code): + for library in cls.get_ids(match): + ref = cls(reference_type="library", library=library) + references.append(ref) + + return references + @classmethod def find(cls, code): """ @@ -1050,9 +1096,10 @@ def find(cls, code): + cls._find_entity_references(code) + cls._find_configuration_references(code) + cls._find_package_instance_references(code) + + cls._find_library_references(code) ) - def __init__(self, reference_type, library, design_unit, name_within=None): + def __init__(self, reference_type, library, design_unit=None, name_within=None): assert reference_type in self._reference_types self.reference_type = reference_type self.library = library @@ -1061,6 +1108,14 @@ def __init__(self, reference_type, library, design_unit, name_within=None): # String "all" may be used to denote all names within self.name_within = name_within + @property + def library_name(self): + return self.library + + @property + def design_unit_name(self): + return self.design_unit + def __repr__(self): return f"VHDLReference({self.reference_type!r}, {self.library!r}, {self.design_unit!r}, {self.name_within!r})" @@ -1081,6 +1136,12 @@ def is_entity_reference(self): def is_package_reference(self): return self.reference_type == "package" + def is_context_reference(self): + return self.reference_type == "context" + + def is_library_reference(self): + return self.reference_type == "library" + def reference_all_names_within(self): return self.name_within == "all" @@ -1089,6 +1150,52 @@ def reference_all_names_within(self): VHDL_REMOVE_COMMENT_COMPILED_RE = re.compile(VHDL_REMOVE_COMMENT_RE, re.MULTILINE) +class VHDLFunctionSpecification(object): + """ + Represents a VHDL function specification. + """ + + def __init__(self, identifier, return_type_mark, parameter_list=None): + self.identifier = identifier + self.return_type_mark = return_type_mark + + self.parameter_list = parameter_list if parameter_list is not None else [] + + _function_specification_re = re.compile( + r""" + \b # Word boundary + function # Function keyword + \s+ # At least one whitespace + (?P[a-zA-Z]\w*) # Function identifier + \s* # Potential whitespaces + (?P\(.*?\))? # Parameter list + \s+ # At least one whitespace + return # Return keyword + \s+ # At least one whitespace + (?P[a-zA-Z]\w*) # Return type mark + \s* # Potential whitespaces + ; # Semi-colon + """, + re.MULTILINE | re.IGNORECASE | re.VERBOSE | re.DOTALL, + ) + + @classmethod + def find(cls, code): + """ + Iterate over new instances of VHDLFunctionSpecification for all function specifications within the code. + """ + for function_specification in cls._function_specification_re.finditer(code): + identifier = function_specification.group("id") + return_type_mark = function_specification.group("return_type_mark") + + parsed_parameter_list = [] + if function_specification.group("parameter_list"): + for element in function_specification.group("parameter_list")[1:-1].split(";"): + parsed_parameter_list.append(VHDLInterfaceElement.parse(element, default_entity_class="constant")) + + yield VHDLFunctionSpecification(identifier, return_type_mark, parsed_parameter_list) + + def _comment_repl(match): """ Replace comment with equal amount of whitespace to make