Search code examples
vhdlbehaviormodelsim

VHDL equal operator: different behavior for std_logic and std_ulogic


I have two designs:

library ieee;
use ieee.std_logic_1164.all;

entity eq_test1 is

  port (a,b : IN std_logic_vector (1 downto 0);
        o   : OUT std_logic); 
end eq_test1;

architecture strange_behavior of eq_test1 is
begin  
    P: process (a,b)
    begin
        if a = b then o <= '1';
        else o <= '0';
        end if;
    end process P;
end strange_behavior;

Forcing in Modelsim to a have "00" and b have "0L" shows that o becomes '0'. So L is NOT interpreted as 0, "00" = "0L" is false. Ok.

But when I take the same design and add

use ieee.std_logic_unsigned.all;

to the list, the behavior is different. Then "00" = "0L" returns true, so L IS the same as 0 (0 becomes '1'). With that unsigned packages included, even "0X" = "0Z" returns true.

Can anyone explain why?


Solution

  • Adding use ieee.std_logic_unsigned.all; is like opening Pandora's box to Synopsys libraries. After a deep dive shown below, the conclusion is that the std_logic_unsigned package in this case makes the values '0' and 'L' equal through an table.

    The call starts when the "=" operator is redefined to:

    function "="(L: STD_LOGIC_VECTOR; R: STD_LOGIC_VECTOR) return BOOLEAN is
    begin
        return   UNSIGNED(L) = UNSIGNED(R);
    end;
    

    That is however just the beginning, since std_logic_unsigned includes use IEEE.std_logic_arith.all;, which defined the type:

    type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;
    

    The compare of UNSIGNED in "=" thus result in call of std_logic_arith function:

    function "="(L: UNSIGNED; R: UNSIGNED) return BOOLEAN is
        -- synopsys subpgm_id 341
        constant length: INTEGER := max(L'length, R'length);
    begin
        return bitwise_eql( STD_ULOGIC_VECTOR( CONV_UNSIGNED(L, length) ),
                STD_ULOGIC_VECTOR( CONV_UNSIGNED(R, length) ) );
    end;
    

    In this function the CONV_UNSIGNED is interesting:

    function CONV_UNSIGNED(ARG: UNSIGNED; SIZE: INTEGER) return UNSIGNED is
        constant msb: INTEGER := min(ARG'length, SIZE) - 1;
        subtype rtype is UNSIGNED (SIZE-1 downto 0);
        variable new_bounds: UNSIGNED (ARG'length-1 downto 0);
        variable result: rtype;
        -- synopsys built_in SYN_ZERO_EXTEND
        -- synopsys subpgm_id 372
    begin
        -- synopsys synthesis_off
        new_bounds := MAKE_BINARY(ARG);
        if (new_bounds(0) = 'X') then
            result := rtype'(others => 'X');
            return result;
        end if;
        result := rtype'(others => '0');
        result(msb downto 0) := new_bounds(msb downto 0);
        return result;
        -- synopsys synthesis_on
    end;
    

    Now we are getting close, since the above calls:

    function MAKE_BINARY(A : UNSIGNED) return UNSIGNED is
        -- synopsys built_in SYN_FEED_THRU
        variable one_bit : STD_ULOGIC;
        variable result : UNSIGNED (A'range);
    begin
        -- synopsys synthesis_off
            for i in A'range loop
                if (IS_X(A(i))) then
                    assert false
                    report "There is an 'U'|'X'|'W'|'Z'|'-' in an arithmetic operand, the result will be 'X'(es)."
                    severity warning;
                    result := (others => 'X');
                    return result;
                end if;
                result(i) := tbl_BINARY(A(i));
            end loop;
            return result;
        -- synopsys synthesis_on
    end;
    

    And in here we have the reason for 'L' being equal to '0', since the tbl_BINARY is a constant defined as:

    type tbl_type is array (STD_ULOGIC) of STD_ULOGIC;
    constant tbl_BINARY : tbl_type :=
        ('X', 'X', '0', '1', 'X', 'X', '0', '1', 'X');
    

    To understand this mapping, it is useful to align with the definition of values in STD_ULOGIC:

    std_ulogic: ( 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-');
    
    tbl_BINARY: ( 'X', 'X', '0', '1', 'X', 'X', '0', '1', 'X');
    

    This shows that after conversion through tbl_BINARY then the equivalent groups are ('U', 'X', 'Z', 'W', '-'), ('0', 'L'), and ('1', 'H').

    A closing comment is that even through the std_logic_unsigned package resides in a library called "ieee", the package is not IEEE standard like VHDL, but a Synopsys package. The package, and the other related Synopsys packages, have been around for a while and are widely used.

    However, you may consider using the IEEE standard package numeric_std instead.