.. rst-class:: break_before Simulation ========== The best way to test your HDL code is to simulate it. This allows you to test your design without having to flash it to the FPGA, which is usually a time-consuming process. XiBIF supports multiple commands to help you simulate and test your design. Testbench --------- XiBIF incorporates the hdltbgen tool to generate a testbench for your design. This tool generates a VHDL testbench that can be used to simulate your design. The testbench can be generated using the following command: .. code-block:: console xibif testbench -f This will generate a testbench for the specified VHDL file. The file must be a VHDL file that is present in the ``hw/src`` directory of your project. The tool will generate two testbenches: One that uses VUnit as simulation framework (created in the folder ``hw/tb/vunit``) and one that uses no framework (created in the folder ``hw/tb``). The generated testbench integrates your design as a component and instantiates it. Additionally, it can include a clock generator and a reset generator if you specify the clock and reset signals using the parameters ``-c`` or ``-rn | -rp``. For instance, the command to generate the testbench might look like this: .. code-block:: console xibif testbench -f adder.vhd -c clk_in -rn resetn_in The testbench processes stimulus and response pairs from a ``.csv`` file, which you can create using tools like Python scripts. A sample ``.csv`` file is also generated in the ``hw/tb`` folder. Simulation ---------- XiBIF supports the simulation of your design using the VUnit framework. VUnit is a testbench framework that allows you to run your testbenches in a simple and efficient way. To run all your testbenches, you can use the following command: .. code-block:: console xibif simulation This will run all the testbenches in the ``hw/tb/vunit`` folder. The testbenches are automatically detected and run. The results of the simulation are printed to the console. If you are using Questasim, the coverage of the simulations is also gathered, and an HTML report is generated in the ``hw/results/coverage`` folder. The coverage report can be opened in a web browser. It is also possible to run a specific testbench. To do this, you can use the following command: .. code-block:: console xibif simulation -t This will run the specified testbench. The testbench must be present in the ``hw/tb/vunit`` folder and must be a VUnit testbench. To get a list of all available testbenches, you can use the following command: .. code-block:: console xibif simulation -l If you need to debug your testbench, you can add the flag ``-g`` to the command. This will open the simulation in the GUI of the simulator. Adding Xilinx simulation libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using Xilinx primitives in your design, you need to add the Xilinx simulation libraries to your simulation. For this, the ``run.py`` script in the ``hw/tb/vunit`` folder needs to be modified. Add the following lines to the script: .. code-block:: python libraries['unisim'] = vu.add_library('unisim') path_unisim = str(project.vivado_binary.parent.parent.joinpath('data/vhdl/src/unisims')) unisim_files = glob.glob(path_unisim + '/*.vhd', recursive=True) unisim_files.extend(glob.glob(path_unisim + '/primitive/*.vhd', recursive=True)) unisim_files = [w.replace('\\', '/') for w in unisim_files] for designfile in unisim_files: libraries['unisim'].add_source_files(designfile) .. note:: It is recommended to only add the primitives you need in your design. This will speed up the compilation. The above code adds all primitives in the unisim library. If you only need a few, you can change the glob pattern to only include the files you need. For example, if you only need the FlipFlop primitives, you can use the following line: .. code-block:: python unisim_files = glob.glob(path_unisim + '/primitive/FD*.vhd', recursive=True) Compilation of simulation libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In rare cases, you might need to compile the simulation libraries for your simulator. XiBIF supports the compilation of the Xilinx simulation libraries as well as the VUnit libraries for use with questasim. To compile the libraries, you can use the following command: .. code-block:: console xibif simulation -cx # for Xilinx libraries xibif simulation -cv # for VUnit libraries This will compile the libraries and place them in the ``hw/results/compile_xyz`` folder. For questasim, the libraries need then to be defined in the ``modelsim.ini`` file. .. note:: This compilation is only needed if you do not want to use VUnit as simulation framework or your simulator is not supported by VUnit. VUnit ----- VUnit is an open-source unit testing framework for VHDL and SystemVerilog. It is inspired by modern software testing tools (like JUnit, pytest, etc.) and is designed to improve the automation, modularity, and scalability of testbenches in FPGA/ASIC development. The full documentation can be found at `VUnit Documentation `_. Key Features ~~~~~~~~~~~~ * Automated test discovery and execution * Test result reporting (pass/fail, logs) * Dependency management (e.g., for libraries and external files) * OS-independent command-line runner (Python-based) How to transform a traditional VHDL testbench to VUnit ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Step 1: Add VUnit Libraries ^^^^^^^^^^^^^^^^^^^^^^^^^^^ VUnit provides a set of libraries that need to be included in your testbench. These libraries are used to provide the test runner functionality and the checker functions. You can add the libraries by including the following lines at the beginning of your testbench file: .. code-block:: vhdl library vunit_lib; context vunit_lib.vunit_context; Step 2: Add a generic to your entity. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You need to add a generic to your testbench entity. This generic will be used to pass the test runner configuration to the testbench. The generic should be of type string and should have a default value of ``runner_cfg_default``. Additionally, it is possible to add a generic ``tb_path`` to your testbench entity. This generic is set by VUnit to the path of the testbench. The entity declaration should look like this: .. code-block:: vhdl entity tb_my_module is generic ( runner_cfg : string := runner_cfg_default; tb_path : string ); end entity; Step 3: Modify your testbench process ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You need to modify your testbench process to use VUnit's test runner. This is done by adding a call to the ``test_runner_setup`` procedure at the beginning of your process and a call to ``test_runner_cleanup`` at the end. Additionally, you need to wrap your test logic in a conditional statement that checks if the test is being run. This is done using the ``run`` function. The ``run`` function takes a string argument that specifies the name of the test. You can use this to specify the name of your test. Before (classic testbench): .. code-block:: vhdl process begin -- Your test logic here a <= '1'; wait for 10 ns; assert b = '1' report "Test failed" severity error; wait; end process; After (VUnit testbench): .. code-block:: vhdl process begin test_runner_setup(runner, runner_cfg); if run("basic_test") then -- Your test logic here end if; test_runner_cleanup(runner); end process; Checker functions ~~~~~~~~~~~~~~~~~ VUnit provides a set of checker functions that can be used to check the results of your test. These functions are similar to the assert statements in a classic testbench but provide more information about the test. The documentation for the checker functions can be found at `VUnit Check Library `_. Boolean Checks ^^^^^^^^^^^^^^ - ``check_true(expr, msg="")`` Assert that the boolean expression ``expr`` is ``true``. - ``check_false(expr, msg="")`` Assert that the boolean expression ``expr`` is ``false``. Equality / Inequality Checks ^^^^^^^^^^^^^^^^^^^^^^^^ These are overloaded for types like ``integer``, ``real``, ``std_logic``, ``std_logic_vector``, ``string``, etc. - ``check_equal(actual, expected, msg="")`` Passes if ``actual = expected``. - ``check_not_equal(actual, expected, msg="")`` Passes if ``actual /= expected``. Relational Checks ^^^^^^^^^^^^^^^^^ - ``check_less(actual, expected, msg="")`` Passes if ``actual < expected``. - ``check_less_or_equal(actual, expected, msg="")`` Passes if ``actual <= expected``. - ``check_greater(actual, expected, msg="")`` Passes if ``actual > expected``. - ``check_greater_or_equal(actual, expected, msg="")`` Passes if ``actual >= expected``. Range Checks ^^^^^^^^^^^^ - ``check_in_range(actual, low, high, msg="")`` Passes if ``low <= actual <= high``. - ``check_not_in_range(actual, low, high, msg="")`` Passes if ``actual < low or actual > high``. Delta / Tolerance Checks ^^^^^^^^^^^^^^^^^^^^^^^^ For real and fixed-point values, allows for numerical tolerances. - ``check_equal(actual, expected, tolerance, msg="")`` Passes if ``abs(actual - expected) <= tolerance``. - ``check_within(actual, center, tolerance, msg="")`` Passes if ``center - tolerance <= actual <= center + tolerance``. - ``check_not_within(actual, center, tolerance, msg="")`` Passes if ``actual`` lies outside the tolerance range. Meta Checks ^^^^^^^^^^^ - ``fail(msg="")`` Unconditionally fails the test with the given message. - ``pass(msg="")`` Marks a test as explicitly passed with a note (useful in parameterized tests). - ``check_stable(signal)`` Ensures that a signal has not changed between simulation cycles. - ``check_implication(premise, consequence, msg="")`` Asserts logical implication: if ``premise`` is true, then ``consequence`` must also be true. Logging and Info-Level Messages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Useful for reporting information without affecting pass/fail status. - ``note(msg="")`` Logs a non-intrusive note. - ``info(msg="")`` Logs an informational message. - ``warning(msg="")`` Logs a warning message (does not fail the test). - ``error(msg="")`` Logs an error message (fails the test).