Search code examples
vhdlvunit

Test for Assertion Failure in VUnit


I may have some functions that assert and fail if they're not happy.

How do I test this functionality with VUnit to ensure that these functions do in-fact throw the failure in the right conditions?


For instance, lets say I wanted to check this function:

function to_normalised_float(slv: std_logic_vector) return half_float is
variable temp_hf: half_float;
begin
    assert slv'high = 15 report "SLV provided is incorrect length for an FP16";
    -- ConstructFloat
    return normalise_float(temp_hf);
end function;

I can quite easily test that it returns the expected value if I pass in a value and then assert on the output in my testbench.

However, I also want to be able to test, using VUnit, that if I pass in a 22 bit SLV, that the assertion throws.

This is obviously a very simplified example, but it should explain what I mean.

The equivalent in C# would be Assert.Throws(function) if that helps.


Solution

  • The ability to inspect asserts will improve with VHDL-2019 support in your simulator but since you're using VUnit I recommend using VUnit mocking (http://vunit.github.io/logging/user_guide.html#mocking and https://github.com/VUnit/vunit/blob/f02c21452a505c527db575b10db94195ceb7ed2f/vunit/vhdl/logging/src/logger_pkg.vhd#L342) which is provided exactly to support your use case.

    First replace your assert with a VUnit check:

    check(slv'high = 15, "SLV provided is incorrect length for an FP16");
    

    When that check fails you will see an error message looking something like this:

    0 ps - check - ERROR - SLV provided is incorrect length for an FP16
    

    check is the VUnit logger managing this message. You can get this logger by name (get_logger("check")) and mock it. Mocking means that all output messages (of a specific severity level) will be placed in a queue rather than passed to stdout. The messages in this queue can be inspected to determine if the function works as expected. Here is a slightly modified example testbench to show the principle

    library vunit_lib;
    context vunit_lib.vunit_context;
    
    library ieee;
    use ieee.std_logic_1164.all;
    
    entity tb_example is
      generic (runner_cfg : string);
    end entity;
    
    architecture tb of tb_example is
    begin
      main : process
        procedure dummy(slv : std_logic_vector) is
        begin
          check(slv'length = 16, "SLV provided is incorrect length for an FP16");
        end;
    
        constant logger : logger_t := get_logger("check");
      begin
        test_runner_setup(runner, runner_cfg);
    
        while test_suite loop
          if run("Test to see dummy fail") then
            dummy(x"17");
          elsif run("Test that dummy fails with the correct message") then
            mock(logger, error);
            dummy(x"17");
            check_log(logger, "SLV provided is incorrect length for an FP16", error);
            unmock(logger);
          elsif run("Test that dummy passes with 16 bit inputs") then
            mock(logger, error);
            dummy(x"1718");
            check_no_log;
            unmock(logger);
          end if;
        end loop;
    
        test_runner_cleanup(runner);
      end process;
    
    end architecture;
    

    The first test case will fail (which is your problem) but the last two will pass

    enter image description here

    I can also recommend using check_equal for a more informative output.

    check_equal(slv'length, 16, result("for input length"));
    

    will give you the following error output:

    0 fs - check - ERROR - Equality check failed for input length - Got 8. Expected 16.