We can also use a number of statements within process blocks which are specifically designed to control the way signals are assigned.
These statements are collectively known as sequential statements and can only be used within a process block.
In the rest of this post we look at the most commonly used sequential statement in more depth.
The if statement is a conditional statement which uses boolean conditions to determine which blocks of VHDL code to execute.
Whenever a given condition evaluates as true, the code branch associated with that condition is executed.
This statement is similar to conditional statements used in other programming languages such as C.
The code shown below shows the basic syntax for the if statement.
if (<expression1>) then -- Code to execute elsif (<expression2>) then -- Code to execute else -- Code to execute end if;
We can exclude the else and elseif branches from the statement if we don't need them.
We have seen this in previous posts where we used the if statement to detect a rising edge on a clock signal. This is a very typical use case which we use to model flip flop circuits.
It is also possible to include as many elsif branches as necessary to properly model the underlying circuit.
The if statement uses boolean conditions to determine which lines of code to execute. In the snippet above, these expressions are given by <expression1> and <expression2>.
These expressions are sequentially evaluated and the code associated with a branch is executed when an expression evaluates as true.
Only one branch of an if statement can ever be executed. This is normally the first expression which evaluates as true.
The only exception to this occurs when none of the expressions are true. In this instance, the code in the else branch will execute. If no else branch is associated with the code, then none of the code branches will be executed.
The code associated with each branch can include any valid VHDL code, including further if statements.
This approach is known as nested if statements. When using this type of code, we should take care to limit the number of nested statements as it can lead to difficulties in meeting timing.
We have already seen practical examples of the if statement when modelling flip flops in the post on VHDL process blocks.
To demonstrate the construct more thoroughly, let's consider an example of a clocked multiplexor. In this instance, we will use an asynchronously resettable D type flip flop to register the output of a multiplexor.
The circuit diagram below shows the circuit which we will use in this example.
There are several ways in which could model this circuit in VHDL. However, the code snippet below shows a single process implementation which uses the if statement.
clocked_mux: process (reset, clock) is begin if (reset = '1') then Q <= '0'; elsif rising_edge(clock) then if (addr = '0') then Q <= a; else Q <= b; end if; end if; end process clocked_mux;
In this example, we can see the use of all the keywords associated with the if statement. In addition to this, we also have an example of nested if statements.
The first if statement sets the output of the flip flop to 0b whenever reset is active. If the reset is not active then we want to register the output on the rising edge of the clock.
The elsif branch of the outer if statement handles this. As we ignore all other conditions, there is no need to write an an else clause for the outer if statement.
We use a second if statement to model the behaviour of the multiplexor circuit.
We expect the output to be assigned to the value of input a when the addr signal is 0b. This is simple to model, as shown in the first line of the second if statement.
We then use an else statement to capture the case when the addr signal is 1b. We could also use an elsif type statement here but the else statement is more succinct. The behaviour is the same in both cases as the signal can only ever be 0b or 1b in a real circuit.
We use the VHDL case statement to select a block of code to execute based on the value of a signal.
When we write a case statement in VHDL we specify an input signal to monitor and evaluate.
The value of this signal is then compared with the values specified in each branch of the case statement. Once a match is found for the input signal value, the branch associated with that value is executed.
The VHDL case statement performs the same function as the switch statement in the C programming language.
The code snippet below shows the general syntax for the case statement in VHDL.
case <control_signal> is when <value1> => -- Code to execute when <value2> => -- Code to execute when others => -- Code to execute end case;
It is possible to exclude the others branch of the statement, although this is not advisable.
If the others branch is excluded then all valid values of the <control_signal> must have its own branch. This includes values other than 0 or 1 when using a type such as std_logic or std_logic_vector.
As with the if statement, the code associated with each branch can include any valid VHDL code. This includes further sequential statements, such as if or case statements. Again, we should try to limit the number of nested statements as it makes it easier to meet our timing requirements.
In the post on VHDL signal assignments, we saw how we can model multiplexors using the with-select statement.
However, we can also use the the case statement to model such components.
To demonstrate this, let's consider the example of a basic four to one multiplexor.
The circuit diagram and code below show this implementation.
mux_example: process (all) is begin case addr is when "00" => q <= a; when "01" => q <= b; when "10" => q <= c; when others => q <= d; end case; end process mux_example;
As shown in the circuit diagram, the addr signal determines the value of the output q. Each of the branches represents a different multiplexer address, with the exception of the others branch. This branch actually captures all of the addr values which aren't explicitly listed elsewhere. Whilst the only logical value not covered in another branch is 11b, we must also consider other signal states such as high impedance. Therefore, it is good practise to use an others branch which captures these states.
As we talked about in the post on sequential logic in VHDL, the all keyword used in the process declaration was introduced in VHDL-2008.
When using a standard prior to this, such as VHDL-93, we need to include all the inputs in the sensitivity list.
The code snippet below shows the exact syntax we would need to use when declaring the process for earlier standards of VHDL.
process (addr, a, b, c, d) is
We can also associate more than one value with a branch of our code. The VHDL syntax includes a method for or-ing different values to select a single branch.
As an example, say we wanted to change our multiplexor so that q is equal to b when the addr signal is either 01 or 10.
To do this, we can include both values in the branch selection criterion and separate them using the | character.
The snippet below shows the exact way we would code this.
case addr is when "00" => q <= a; when "01" | "10" => q <= b; when others => q <= d; end case;
We use loops in VHDL to execute the same code a number of times.
When we want to limit the number of times the code executes , we must use either a while loop or a for loop.
We can also allow the loop to run continuously, creating an infinite loop.
As looping can result in non-synthesizeable code, we mainly use loops for test bench code. However, we can also use them to write synthesizable code in some circumstances.
We often use an infinite loop to generate test stimulus within a VHDL testbench. We should never use infinite loops in code which we wish to synthesize.
The code snippet below shows the syntax for an infinite loop.
<loop_label>: loop -- Code to execute end loop <loop_label>;
The infinite loop is easy to understand - the code in the block runs repeatedly until the execution is prevented in some way.
Although it is generally helpful to label the loop, it is not mandatory to include the <label_loop> field.
Execution of the loop stops either when the simulation finishes running or through the use of the exit keyword.
The code syntax below shows an example using the exit keyword to stop the loop when the iter variable is equal to 4.
example_loop: loop exit example_loop when iter = 4; iter <= iter + 1; end loop;
We use the while loop to execute a part of our VHDL code for as long as a given condition is true.
The specified condition is evaluated before each iteration of the loop. We can think of the while loop as an if statement that executes repeatedly.
As while loops are generally not synthesizable, we often use them in our testbenches to generate stimulus.
The code snippet below shows the general syntax for a while loop in VHDL.
<loop_label>: while <condition> loop -- Code to execute end loop <loop_label>;
The basic concept of the while loop is similar to the infinite loop, as the code within the block executes continually.
The execution of the code is stopped when the <condition> field evaluates as false. The <condition> field is evaluated at the beginning of each iteration.
As a result of this, all of the code in the block will execute in each valid iteration. This happens even if the <condition> field changes so that it no longer evaluates to true whilst the code in the block is running.
As with the infinite loop, the <label_loop> field is optional. The code snippet below shows an example which loops until the iter variable is 4 or more.
example_loop: while (iter < 4) loop iter <= iter + 1; end loop;
When writing VHDL code, we use the for loop to execute a block of code a fixed number of times.
We specify the number of times that the code executes in the for loop declaration.
Although it is commonly used in testbenches, we can also use the for loop in synthesizable VHDL code.
The code snippet below shows the syntax we use in a for loop.
<loop_label> : for <parameter> in <range> loop -- Code to execute end loop <loop_label>;
As with the other types of loop, the <loop_label> field is not mandatory.
We use the <range> field to specify how many times the for loop code is repeated.
We can use the <parameter> field as a variable within the loop and it is not necessary to declare this as a signal or variable separately. However, we can't change the value of this parameter within the loop code.
The code snippet below shows an example which executes the loop four times, with the value of i increasing from 0 to 3 iteratively.
for i in 0 to 3 loop -- Code to execute end loop;
Rewrite the if statement example so that it uses a 4 to 1 mux.show answer
mux_example: process (reset, clock) is begin if (reset = '1') then Q <= '0'; elsif rising_edge(clock) then if (addr = "00") then Q <= a; elsif (addr = "01") Q <= b; elsif (addr = "10") Q <= c; else Q <= d; end if; end if; end process mux_example;
Use a case statement to rewrite from the previous exercise.show answer
mux_example: process (reset, clock) is begin if (reset = '1') then Q <= '0'; elsif rising_edge(clock) then case addr is when "00" => Q <= a; when "01" => Q <= b; when "10" => Q <= c; when others => Q <= d; end case; end if; end process mux_example;
Which two types of loop are not generally suitable for sythesizable code?show answer
Infinite loops and while loops.hide answer
Write a process which uses a while loop to decrease a signal for 4 to 0show answer
while_example: process is begin iter <= 4; while (iter > 0) loop iter <= iter - 1; end loop; end process while_example;