Search code examples
vhdl

Unexpected function output when function parameter is negated


I have a priority encoding function that returns a vector containing a 1 at the position where the first 1 is found in the input vector. The function works as expected, unless I try to negate the input vector. Here's an example that demonstrates the unexpected behavior:


LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

entity tb IS
end tb;

architecture run of tb is
    constant N : natural := 5;

    function get_first_one_in_vec (vec_in: std_logic_vector) return std_logic_vector is
        variable ret: std_logic_vector(vec_in'high downto vec_in'low);
    begin
        ret := (others => '0');
        for i in vec_in'low to vec_in'high loop
            if vec_in(i)='1' then
                ret(i) := '1';
                exit;
            end if;
        end loop;

        return ret;
    end get_first_one_in_vec;

    signal a            : std_logic_vector(N-1 downto 0);
    signal abar         : std_logic_vector(N-1 downto 0);

    signal first_a      : std_logic_vector(N-1 downto 0);
    signal first_nota   : std_logic_vector(N-1 downto 0);
    signal first_abar   : std_logic_vector(N-1 downto 0);
begin
    process
    begin
        a <= "10100";
        wait for 10 ns;
        a <= "01011";
        wait for 10 ns;
        wait;
    end process;

    abar        <= not(a);
    first_a     <= get_first_one_in_vec(a);
    first_nota  <= get_first_one_in_vec(not(a));
    first_abar  <= get_first_one_in_vec(abar);

end run;

To my understanding, first_nota should be the same as first_abar. However, my simulator (ModelSim - Intel FPGA Starter Edition 10.5b, rev. 2016.10) thinks otherwise, as you can see here:

enter image description here


What am I missing here?


Solution

  • This works OK:

    function get_first_one_in_vec (vec_in: std_logic_vector) return std_logic_vector is
        variable ret: std_logic_vector(vec_in'length downto 1);
        variable inp: std_logic_vector(vec_in'length downto 1) := vec_in;
    begin
        ret := (others => '0');
        for i in inp'right to inp'left loop
            if inp(i)='1' then
                ret(i) := '1';
                exit;
            end if;
        end loop;
    
        return ret;
    end get_first_one_in_vec;
    

    https://www.edaplayground.com/x/3zP_

    Why does yours not work? Well, when you call your function with the not operator* as part of the expression:

    first_nota  <= get_first_one_in_vec(not a);
    

    the numbering of the input to the function is changed to 1 to by the not operator. Why? Here is the code for the not operator and you can see why:

    -------------------------------------------------------------------    
    -- not
    -------------------------------------------------------------------    
    FUNCTION "not"  ( l : std_logic_vector ) RETURN std_logic_vector IS
        -- pragma built_in SYN_NOT
    -- pragma subpgm_id 204
        --synopsys synthesis_off
        ALIAS lv : std_logic_vector ( 1 TO l'LENGTH ) IS l;
        VARIABLE result : std_logic_vector ( 1 TO l'LENGTH ) := (OTHERS => 'X');
        --synopsys synthesis_on
    BEGIN
        --synopsys synthesis_off
        FOR i IN result'RANGE LOOP
            result(i) := not_table( lv(i) );
        END LOOP;
        RETURN result;
        --synopsys synthesis_on
    END;
    ---------------------------------------------------------------------
    

    Anyway, this breaks your code (which starts scanning from the other end of the word).

    One way of making function agnostic to the ordering of the numbering of its input is to normalise the inputs like this:

    variable inp: std_logic_vector(vec_in'length downto 1) := vec_in;
    

    Once you have done this, you're in control. So, instead of loops from 'high downto 'low, we can be more explicit and loop from 'right to 'left:

    for i in inp'right to inp'left loop
    

    • not is an operator not a function. You don't need the brackets.