Why use concurrent statements in VHDL? - vhdl

I am just starting with learning vhdl.
Consider the code here : - http://esd.cs.ucr.edu/labs/tutorial/jkff.vhd
I can't understand what are concurrent statements and why are they needed here?
Will it be correct if we modify Q and Qbar directly in process p without using internal signal 'state'? Also why are J,K not in sensitivity list of process p in the snippet?

Concurrent statements, as you may know, in a pure functional sense (i.e. not considering hardware implementation) do not incur any delay. So when you write
Q <= state;
Functionally, Q exactly follows state without any delay.
I am going to guess that the reason an intermediate signal state was used, instead of directly assigning Q inside the process, is that if you directly assign one of your outputs Q in the process, then you cannot "read" the output to derive your Qbar signal.
That is, you couldn't do this:
Qbar <= not Q;
This is because it is not strictly allowable to read an output signal in VHDL. By using "state" you have an internal signal from which you can derive both Q and Qbar.
An alternative, equivalent implementation to this would be to assign both outputs Q and Qbar in each of the cases in the state machine, and eliminate the intermediate state signal completely. However, this seems a bit more complicated since you will have nearly twice as many lines of code for an equivalent functionality.
To answer your second question: J,K are not in the sensitivity list because the process p is a synchronous process. You are describing a memory element (JK FlipFlop), which by definition only updates its outputs when clock or reset change. Input signals J and K can change and the process will not update its outputs. Every time there is a clock edge, or reset is asserted, the process "wakes up" and evaluates inputs, and determines what the output should be. Even in J,K were included in the sensitivity list, provided your ouputs were only updated on rising_edge(clock), then the overall function would be the same (although your code would be confusing).

There is no reason not to have the Q and Qbar assignments inside the process. You need to be slightly careful though.
Whenever a signal is assigned to, the value does not update until the simulator moves on to the next "delta-cycle". This means that within processes, when you assign to a signal, you are axtually only cheduling and update and if you read the signal you will get the "old" value. In order to have the sort of sequential updates you might expect, you use a variable. So you could model the JKFF like this:
architecture behv of JK_FF is
begin
p : process(clock, reset) is
variable state : std_logic;
variable input : std_logic_vector(1 downto 0);
begin
if (reset = '1') then
state := '0';
elsif (rising_edge(clock)) then
input := J & K;
case (input) is
when "11" =>
state := not state;
when "10" =>
state := '1';
when "01" =>
state := '0';
when others =>
null;
end case;
end if;
Q <= state;
Qbar <= not state;
end process;
end behv;
A synthesis note: the assignments to Q and Qbar occur outside of the if rising_edge(clk) so will be interpreted as just like concurrent drivers.

Related

Assign multiple values to a signal during 1 process

If you assign a value to a signal in a process, does it only become the correct value of the signal at the end of the process?
So there would be no point in assigning a value to a signal more than once per process, because the last assignment would be the only one that would be implemented, correct?
I'm a bit desperate because I'm trying to implement the booth algorithm in VHDL with signals and I can't get it baked. It wasn't a problem with variables, but signals make it all more difficult.
I tried a for loop, but that doesn't work because I have to update the values within the loop.
My next idea is a counter in the testbench.
Would be very thanksful for an idea!
my current Code look like this:
architecture behave of booth is
signal buffer_result1, buffer_result2, buffer_result3: std_logic_vector(7 downto 0) := "0000"&b;
signal s: std_logic:= '0';
signal count1, count2: integer:=0;
begin
assignment: process(counter) is
begin
if counter = "000" then
buffer_result1 <= "0000"&b;
end if;
end process;
add_sub: process(counter) is
begin
if counter <= "011" then
if(buffer_result1(0) = '1' and s = '0') then
buffer_result2 <= buffer_result1(7 downto 4)-a;
else if (buffer_result1(0) = '0' and s = '1') then
buffer_result2 <= buffer_result1(7 downto 4)+a;
end if;
end if;
end process;
shift:process(counter) is
begin
if counter <= "011"
buffer_result3(7) <= buffer_result2(7);
buffer_result3(6 downto 0) <= buffer_result2(7 downto 1);
s<= buffer_result3(0);
else
result<=buffer_result3;
end if;
end behave;
Short answer: that's correct. A signal's value will not update until the end of your process.
Long answer: A signal will only update when its assignment takes effect. Some signal assignments will use after and specify a time, making the transaction time explicit. Without an explicit time given, signals will update after the default "time-delta," an "instant" of simulation time that passes as soon as all concurrently executing statements at the given sim time have completed (e.g. a process). So your signals will hold their initial values until the process completes, at which point sim time moves forward one "delta," and the values update.
That does not mean that multiple signal assignment statements to the same signal don't accomplish anything in a process. VHDL will take note of all assignments, but of a series of assignments given with the same transaction time, only the last assignment will take effect. This can be used for a few tricky things, although I've encountered differences of opinion on how often they should be tried. For instance:
-- Assume I have a 'clk' coming in
signal pulse : std_ulogic;
signal counter : unsigned(2 downto 0);
pulse_on_wrap : process(clk) is
begin
clock : if rising_edge(clk):
pulse <= '0'; -- Default assignment to "pulse" is 0
counter <= counter + 1; -- Counter will increment each clock cycle
if counter = 2**3-1 then
pulse <= '1'; -- Pulse high when the counter drops to 0 (after this cycle)
end if;
end if clock;
end process pulse_on_wrap;
Here, the typical behavior is to assign the value '0' to pulse on each clock cycle. But if counter hits its max value, there will be a following assignment to pulse, which will set it to '1' once simulation time advances. Because it comes after the '0' assignment and also has a "delta" transaction delay, it will override the earlier assignment. So this process will cause the signal pulse, fittingly, to go high for one cycle each time the counter wraps to zero and then drop the next - it's a pulse, after all! :^)
I provide that example only to illustrate the potential benefit of multiple assignments within a process, as you mention that in your question. I do not advise trying anything fancy with assignments until you're clear on the difference between variable assignment and signal assignment - and how that needs to be reflected in your code!
Try to think of things in terms of simulation time and hardware when it comes to signals. Everything is static until time moves forward, then you can handle the new values. It's a learning curve, but it'll happen! ;^)

Query for VHDL synthesis for IC Design (Not FPGA), specifically in case of variable assignment

If for a given process, I declare a variable (let's say a 1 bit variable, variable temp : std_logic;) then can I assign a value to the variable if a given condition returns true, i.e.
if (xyz=1) then --Assuming that this condition returns TRUE
temp:= '1';
?? Will this logic be synthesizable for ASICs?
Yes. Variables are synthesisable for both FPGA and IC. A process is a little bit of software that models a little bit of hardware. That little bit of software can use variables, but as variables are only in scope within a process, ultimately you do have to drive a signal - the output of the little bit of hardware.
For example, here is some combinational logic:
process (A, B, C, D)
variable TMP : std_logic;
begin
if A = '1' then
TMP := B and C;
TMP := TMP and D;
else
TMP := '0';
end if;
F <= TMP;
end process;
Here is an example of using a variable that will synthesise to combinational logic on the D input of a flip-flop (because it is in a clocked process):
process (CLOCK)
variable TMP : std_logic;
begin
if rising_edge(CLOCK) then
TMP := A and B;
Q <= TMP;
end if;
end process;
And here is an example of using a variable in a clocked process that will synthesise to a flip-flop (with an AND gate on its D input):
process (CLOCK)
variable TMP : std_logic;
begin
if rising_edge(CLOCK) then
Q <= TMP;
TMP := A and B;
end if;
end process;
The only difference between the two clocked processes is the order. In the first, the variable is assigned to before being accessed; in the second, it is accessed before it is assigned to.
If you assign to a variable before accessing it in a clocked process combinational logic will be inferred;
if you access a
variable before assigning to it in a clocked process, a flip-flop
will be inferred.
Do not ever access a variable before assigning
to it in a combinational process: latches will be inferred.
Variables retain their value between executions of a process. Therefore, if a variable is accessed before being assigned to in a clocked process, the value read must have been written on a previous execution of the process. In a clocked process, that previous execution will have been on a previous clock edge: hence, a flip-flop is inferred.

What's wrong with this simple VHDL for loop?

For some reason the OutputTmp variable will always be uninitialized in the simulation. I can make it work without a for loop but I really want to automate it so I can later move on to bigger vectors. The intermediate variable works fine.
Note: I'm a DBA and C# programmer, really new to VHDL, sorry if this is a stupid question.
architecture Arch of VectorMultiplier4 is
signal Intermediate : std_logic_vector(0 to 4);
signal OutputTmp : std_logic;
begin
process (Intermediate)
begin
for i in 0 to 4 loop
Intermediate(i) <= (VectorA(i) AND VectorB_Reduced(i));
end loop;
--THIS IS WHAT DOES NOT WORK APPARENTLY
OutputTmp <= '0';
for i in 0 to 4 loop
OutputTmp <= OutputTmp XOR Intermediate(i);
end loop;
Output <= OutputTmp;
end process;
end architecture;
Thanks!
This is slightly different from the answer fru1tbat points to.
One characteristic of a signal assignment is that it is scheduled for the current or a future simulation time. No signal assignment actually takes effect while any simulation process is pending (and all signal involved statements are devolved into either block statements preserving hierarchy and processes or just processes).
You can't rely on the signal value you have just assigned (scheduled for update) during the same simulation cycle.
The new signal value isn't available in the current simulation cycle.
A signal assignment without a delay in the waveform (no after Time) will be available in the next simulation cycle, which will be a delta cycle. You can only 'see' the current value of signal.
Because OutputTmp appears to be named as an intermediary value you could declare it as a variable in the process (deleting the signal declaration, or renaming one or the other).
process (VectorA, VectorB_Reduced)
variable OutputTmpvar: std_logic;
variable Intermediate: std_logic_vector (0 to 4);
begin
for i in 0 to 4 loop
Intermediate(i) := (VectorA(i) AND VectorB_Reduced(i));
end loop;
-- A variable assignment takes effect immediately
OutputTmpvar := '0';
for i in 0 to 4 loop
OutputTmpvar := OutputTmpv XOR Intermediate(i);
end loop;
Output := OutputTmpvar;
end process;
And this will produce an odd parity value of the elements of the Intermediate array elements.
Note that Intermediate has also been made a variable for the same reason and VectorA and VectorB_Reduced have been placed in the sensitivity list instead of Intermediate.
And all of this can be further reduced.
process (VectorA, VectorB_Reduced)
variable OutputTmpvar: std_logic;
begin
-- A variable assignment takes effect immediately
OutputTmpvar := '0';
for i in 0 to 4 loop
OutputTmpvar := OutputTmpvar XOR (VectorA(i) AND VectorB_Reduced(i));
end loop;
Output <= OutputTmpvar;
end process;
Deleting Intermediate.
Tailoring for synthesis and size extensibility
And if you need to synthesis the loop:
process (VectorA, VectorB_Reduced)
variable OutputTmp: std_logic_vector (VectorA'RANGE) := (others => '0');
begin
for i in VectorA'RANGE loop
if i = VectorA'LEFT then
OutputTmp(i) := (VectorA(i) AND VectorB_Reduced(i));
else
OutputTmp(i) := OutputTmp(i-1) XOR (VectorA(i) AND VectorB_Reduced(i));
end if;
end loop;
Output <= OutputTmp(VectorA'RIGHT);
end process;
Where there's an assumption VectorA and VectorB_reduced have the same dimensionality (bounds).
What this does is provide ever node of the synthesis result 'netlist' with a unique name and will generate a chain of four XOR gates fed by five AND gates.
This process also shows how to deal with any size matching bounds input arrays (VectorA and VectorB_Reduced in the example) by using attributes. If you need to deal with the case of the two inputs having different bounds but the same length you can create
variable copies of them with the same bounds, something you'd like do as a matter of form if this were implemented in a function.
Flattening the chain of XORs is something handled in the synthesis domain using performance constraints. (For a lot of FPGA architectures the XOR's will fit in one LUT because of XOR's commutative and associative properties).
(The above process has been analyzed, elaborated and simulated in a VHDL model).
When you enter a VHDL process, signals keeps their value until the process is done (or a wait is reached). So, all the lines that assign OutputTmp can be replaced by
OutputTmp <= OutputTmp XOR Intermediate(4);
Which clearly keep OutputTmp unknown if it is unknown when you enter the process.
When programming, all statement are executed one after the other. In HDL, all statement are executed at the same time. You can use variables in VHDL to achieve the same comportment as in C, but I would not recommend it for a beginner willing to learn VHDL for synthesis.

sensitivity list VHDL process

I'm trying to learn VHDL using Peter Ashenden's book 'The Designer's Guide to VHDL', but can't seem to shake the feeling that I have missed a fundamental item related to sensitivity lists.
for example a question is "Write a model that represents a simple ALU with integer inputs and output, and a function select input of type bit. if the function select is '0', the ALU output should be the sum of the inputs otherwise the output should be the difference of the inputs."
My solution to this is
entity ALU is
port (
a : in integer; -- A port
b : in integer; -- B port
sel : in bit; -- Fun select
z : out integer); -- result
end entity ALU;
architecture behav of ALU is
begin -- architecture behav
alu_proc: process is
variable result : integer := 0;
begin -- process alu_proc
wait on sel;
if sel = '0' then
result := a + b;
else
result := a - b;
end if;
z <= result;
end process alu_proc;
end architecture behav;
with the test bench
entity alu_test is
end entity alu_test;
architecture alu_tb of alu_test is
signal a, b, z : integer;
signal sel : bit;
begin -- architecture alu_tb
dut: entity work.alu(behav)
port map (a, b, sel, z);
test_proc: process is
begin -- process test_proc
a <= 5; b <= 5; wait for 5 ns; sel <= '1'; wait for 5 ns;
assert z = 0;
a <= 10; b <= 5; wait for 5 ns; sel <= '0'; wait for 5 ns;
assert z = 15;
wait;
end process test_proc;
end architecture alu_tb;
my issue has to do with the sensitivity list in the process. Since it is sensitive to changes of the select bit I must do the functions sequentially, first an subtraction, then an addition then a subtraction again in the test bench. In the question I get the feeling that you should be able to do several additions sequentially, no subtraction between. Of course I can add an enable signal and have the process be sensitive to that but I think that should be told in the questions then. Am I missing something in the language or is my solution "correct"?
The problem with the ALU process is that the wait on sel; does not include
a and b, thus the process does not wake up and the output is not
recalculated at changes to these inputs. One way to fix this is to add a and
´b´ to the wait statement, like:
wait on sel, a, b;
However, the common way to write this for processes is with a sensitivity list,
which is a list of signals after the process keyword, thus not with the
wait statement.
Ashendens book 3rd edition page 68 describes that a sensitivity list:
The process statement includes a sensitivity list after the keyword process.
This is a list of signals to which the process is sensitive. When any of
these signals changes value, the process resumes and executes the sequential
statements. After it has executed the last statement, the process suspends
again.
The use of sensitivity list as equivalent to wait statement is also described
in Ashendens book on page 152.
If the process is rewritten to use a sensitivity list, it will be:
alu_proc: process (sel, a, b) is
begin -- process alu_proc
if sel = '0' then
z <= a + b;
else
z <= a - b;
end if;
end process alu_proc;
Note that I removed the result variable, since the z output can just as
well be assigned directly in this case.
The above will recalculate z when any of the values used in the calculation
changes, since all the arguments for calculating z are included in the
sensitivity list. The risk of doing such continuous calculations in this way,
is that if one or more of the arguments are forgotten in the sensitivity list,
a new value for z is not recalculated if the forgotten argument changes.
VHDL-2008 allows automatic inclusion of all signals and ports in the
sensitivity list if all is used like:
alu_proc: process (all) is
A final comment, then for a simple process doing asynchronous calculation, like
for the shown ALU, it is possible to do without a process, if the generation of
z is written like:
z <= (a + b) when (sel = '0') else (a - b);
Using a concurrent assignment, like the above, make it possible to skip the
sensitivity list, and thus the risk of forgetting one of the signals or ports
that are part of the calculation.

VHDL if condition not working properly

I'm having some difficulties determining why my code is not working properly. I'm trying to create an ALU with a 3-bit op-code.
All but one condition doesn't work properly; op code 011 (SEQ). It's defined as if(a==b) z<='0' and output<='0'. a and b are the inputs, and z is the zero flag. I expected to get this functionality with the following VHDL code
....
BEGIN
result <= dummy_result;
PROCESS (a, b, op)
VARIABLE carry: STD_LOGIC:='0';
--DEFINE ALIAS TO SEPERATE ALU-OP SIGNAL
alias NEG_TSEL : STD_LOGIC IS op(2);
alias ALU_SELECT : STD_LOGIC_VECTOR(1 downto 0) IS op(1 downto 0);
BEGIN
if ALU_SELECT="11" THEN
if NEG_TSEL='0' THEN -- SEQ
if a = b THEN
dummy_result <="00000";
end if;
elsif NEG_TSEL='1' THEN --SCO
cout <= '1';
result <= "XXXXX";
end if;
elsif ALU_SELECT="00" THEN...
With this code, when op = 011, results is always set to zero.
When I change the code to:
.....
if a = b THEN
dummy_result <="00000";
else
dummy_result <= "10101";
end if;
.....
it works fine, but results must not change so instead of the "10101" vector, I change it to "dummy_result <= dummy_result;" but that gives me the same results as the original case gives me.
Any suggestions? Am I doing something wrong?
Here are my issues with your code:
Personally, I feel that on every cycle you should be outputting something to result, cout, and zero. Currently, you always output to zero, but you only conditionally output to the other two ports. This likely is creating latches, which is probably not what you want. So, for example, the SCO operation, should also push something to cout, and the SCO operation should push something to the dummy_result signal.
Your subtraction implementation is not working how you might expect.
when "110" => -- SUB
tmp_b <= NOT b;
carry := '1';
for i in 0 to 4 loop
dummy_result(i) <= carry XOR a(i) XOR tmp_b(i);
carry := (a(i) AND tmp_b(i)) OR (tmp_b(i) AND carry) OR (carry AND a(i));
end loop;
cout <= carry;
tmp_b is a signal. The logic that happens there will happen concurrently with everything else in this process, not sequentially. You likely want that to be a variable, just like carry is.
I also wanted to let you know about case statements (versus if chains). Your code could look like this:
PROCESS (a, b, op)
VARIABLE carry: STD_LOGIC:='0';
BEGIN
case op is
when "011" => -- SEQ
dummy_result <= "00000";
when "111" => -- SCO
cout <= '1';
when "000" => -- AND
dummy_result <= a AND b;
...
when others =>
dummy_result <= "00000";
cout <= '0';
end case;
end process;
To get back to the original problem, SEQ, your original code looked like:
when op = "011" =>
if a = b then
dummy_result <= "00000";
end if;
The problem here, as I mentioned above, is that this is likely a latch. You need to output what you expect the value to be when a /= b, and that can't just be dummy_result <= dummy_result. What do you expect that to become if you pushed it to physical wires and chips?
Instead, you should pass into this entity the value of the last dummy_result, or if a particular operation should hold the value of result, you should output "00000" and also output an additional signal saying that whatever is holding the previous value (in a register), shouldn't update it.
The intent here is that unless otherwise assigned, dummy_result is intended to retain the previous value of "result". Unfortunately, this unit has been implemented as a combinational process, without a clock.
Therefore the storage cannot be reliably implemented in this unit.
Therefore it must be implemented outside this unit.
It almost certainly already is; in a register implemented as a clocked process.
So, bring that register's output back in as a new input port "prev_result" and use a default assignment to dummy_result. That will overcome not only the specific failure you have found so far, but all the other missing assignments to "dummy_result" (there is another) preserving the old value for "result" in a synchronous manner.
PROCESS (a, b, op, prev_result)
-- declarations here
BEGIN
-- default assignment
dummy_result <= prev_result;
if ALU_SELECT="11" THEN
if NEG_TSEL='0' THEN -- SEQ
if a = b THEN
dummy_result <="00000";
end if;
...
END PROCESS;
I think you would be better restructuring the design to make the ALU a clocked process, but if you are under instructions not to, then you will have to adopt this (or similar) solution.

Resources