Search code examples
vhdl

If-else statement with same conditions as other if-else statement do not produce same output


So I have to do an assigment where I have to write a counter which hase upcount mode = 1 -> increment = 5 and downcount mode = 0 -> decrement = -9. There is an invalid value of -69 which the counter should jump over.

There are also upper and lower bounds: -250 to 248.

To test our counter, a testbench was given.

I used the following if-else statement inside a process with the clk signal as sensitivity list entry.

if((cnt_intern + 5) <= 248) then
    cnt_intern <= cnt_intern + 5;
end if;


if(cnt_intern = -69) then
    cnt_intern <= cnt_intern + 5;
end if;

this did not work as it set the cnt_intern to -69, which the second if statement should prevent. I rewrote the if statement to the following:

if(cnt_intern <= 243) then#
    if(cnt_intern = -73) then
        cnt_intern <= cnt_intern + 10;
    else
        cnt_intern <= cnt_intern + 5;
    end if;
end if;

This time it did work and it jumped over the vlaue -69 directly to -64.

Anyone knows why? what is wrong with the first way?

best regards


Solution

  • The reason for the observed behavior is that a signal assignment inside a process does not immediately change the signal value. Instead, a transaction is scheduled on the signal, which will take effect when the process suspends (i.e. when the end of the process or a wait statement is reached).

    In your first example, if cnt_intern is -74 at the beginning of the process, the first if statement schedules as transaction, that means a change of the signal's value to -69 will take place at the end of the process if no other assignment schedules a transaction on cnt_intern. However, the actual value of cnt_intern stays -74 until the end of the process. So the second if statement will evaluate to false and do nothing. At the end of the process, the value of -69 is assigned to cnt_intern.

    This concept seems confusing if you start working with hardware description languages, but it is essential.

    You could use a helper variable to circumvent that issue and keep the code readable:

    process(clk)
        variable v_cnt_intern : integer;
    begin
        if rising_edge(clk) then
            v_cnt_intern := cnt_intern;
    
            if((v_cnt_intern + 5) <= 248) then
                v_cnt_intern := v_cnt_intern + 5;
            end if;
    
            if (v_cnt_intern = -69) then
                v_cnt_intern := v_cnt_intern + 5;
            end if;
    
            cnt_intern <= v_cnt_intern;
        end if;
    end process;
    

    Other solutions are something like the code in your second example, which looks fine.