VUnit / vunit

VUnit is a unit testing framework for VHDL/SystemVerilog
http://vunit.github.io/
Other
722 stars 261 forks source link

[Bug] `check_equal` and `check_true` raises error for values other than `'0'` and `'1'` #1061

Open nselvara opened 1 week ago

nselvara commented 1 week ago

Hey, there I noticed something strange, while using check_equal or check_true for (un-)signed types. It actually raises an error if we pass any value except '0' and '1' for got resp. expected.

-- This passes
check_equal(got => std_ulogic_vector'("U"), expected => std_ulogic_vector'("U"), msg => "test std_ulogic_vector");
-- These not
check_equal(got => signed'("U"), expected => signed'("U"), msg => "test signed");
check_equal(got => unsigned'("X"), expected => unsigned'("X"), msg => "test unsigned");
check_true(expr => unsigned'("Z") = unsigned'("Z"), msg => "test equality unsigned");

I checked the definition of the (un-)signed types and seems to be that those are just arrays of std_ulogic type:

type UNRESOLVED_UNSIGNED is array (NATURAL range <>) of STD_ULOGIC;
type UNRESOLVED_SIGNED is array (NATURAL range <>) of STD_ULOGIC;

I guess, equality check with values other than number don't pass for (un-)signed types in VHDL resp. excluded in LRM?

if got = expected then
nselvara commented 1 week ago

Okay, I've dug through the definition of the "="-operator. I thought, it was an implicitly defined one, however, it has been explicitly defined.

  -- Id: C.25
function "=" (L, R : UNRESOLVED_UNSIGNED) return BOOLEAN is
    constant L_LEFT : INTEGER := L'length-1;
    constant R_LEFT : INTEGER := R'length-1;
    alias XL        : UNRESOLVED_UNSIGNED(L_LEFT downto 0) is L;
    alias XR        : UNRESOLVED_UNSIGNED(R_LEFT downto 0) is R;
    constant SIZE   : NATURAL := MAXIMUM(L'length, R'length);
    variable L01    : UNRESOLVED_UNSIGNED(L_LEFT downto 0);
    variable R01    : UNRESOLVED_UNSIGNED(R_LEFT downto 0);
begin
    if ((L'length < 1) or (R'length < 1)) then
      assert NO_WARNING
        report "NUMERIC_STD.""="": null argument detected, returning FALSE"
        severity warning;
      return false;
    end if;
    L01 := TO_01(XL, 'X');
    R01 := TO_01(XR, 'X');
    if ((L01(L01'left) = 'X') or (R01(R01'left) = 'X')) then
      assert NO_WARNING
        report "NUMERIC_STD.""="": metavalue detected, returning FALSE"
        severity warning;
      return false;
    end if;
    return UNSIGNED_EQUAL(RESIZE(L01, SIZE), RESIZE(R01, SIZE));
end function "=";

The non-number values are converted to '0' and '1':

L01 := TO_01(XL, 'X');
R01 := TO_01(XR, 'X');

So, I don't know if it's worth to change the check_equal wrapper for (un-)signed types to allow these kind of conditions.

LarsAsplund commented 1 week ago

VHDL is not always obvious and consistent. However, it would only create more confusion if VUnit was to check equality in a way that is not compatible with the = operator

nselvara commented 1 week ago

Yeah, I understand that totally. I think there's also this function UNSIGNED_EQUAL in numeric-std-body, which converts the (un-)signed types to std_ulogic_vector beforehand. I think maybe expanding (either add. option, another name or completely something else) the check_equal-procedure with this sort of conversion would be cool though. But I understand if it's not desired.