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
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.