In the behavioral modeling style in VHDL, we describe the behavior of an entity using sequential statements. And this makes it very similar to high-level programming languages in syntax and semantics. The primary mechanism to write a program in behavioral style is by using something called a “process”. In this article, we will study process statements and various sequential statements.
The process block
A process block contains statements that the VHDL compiler executes sequentially. Statements inside the process block may describe the working of the entity or a portion of the entity. The sequential execution of statements means that the compiler will execute them in the same order as we write them. This way of execution is very similar to other high-level programming languages like C, C++, JAVA, etc.
The syntax of a process block:
process-label : process ( sensitivity-list ) process-data_object-declarations begin sequential-statements; these are -> variable-assignment-statement signal-assignment-statement case statement loop statement if-else statement assertion-statement wait-statement end process process-label;
There too many new things in this syntax, so let’s understand them one by one.
It is optional and its absence would not create any errors. It is nothing but just a name for a particular process. Giving a proper label to the process is a good programming habit. It will be helpful for co-developers while writing large code sets and will help other people while reading.
It contains the name/ label of all the signals that the process is sensitive to. This is also optional, but a process without a sensitivity list runs forever, and this should be avoided. So to avoid that, when we write a process without a sensitivity list, we write a wait statement at the end of the process. That helps in suspending the process. We’ll see how that works later in this article.
These are data-objects like signals, variables, constants, generics, etc. Data objects defined inside a process only belong to that particular process; no other process can access those data-objects. You can think of them as the local variables in C or any other high-level language.
The VHDL compiler allows sequential statements in the process block only. There are various sequential statements, some of them are listed below:
- case statement
- loop statement
- if-else statement
We will study them all in detail in this article.
After the execution of all the statements inside the process block, the compiler suspends the process.
Sequential assignment statements
Variable assignment statement
Variables are declared inside a process before the
begin keyword. The variable declared inside the process only belongs to it. We can declare a variable using the following syntax.
Variable variable_name: datatype := value;
We can assign value to the variable using a variable assignment statement, as shown below:
variable_name:= value; --OR variable_name:= expression;
The VHDL compiler will evaluate the expression during execution and will assign the value to the variable object almost instantly. The VHDL compiler creates the variables when we run the program, and these variables retain their value throughout the simulation. This is because the compiler never terminates/exits the process block. While simulation is running, a process can only be in either an active state or a suspended state. In the active state, the process is running, and a suspended state means it is waiting for a certain event to occur.
Signal assignment statement
Signals can be defined inside a process or outside a process. When they appear inside a process, they behave as sequential signal assignment statements. And when they appear outside a process, then they behave as concurrent signal assignment statements. We can declare a signal using the following syntax
Signal signal_label : datatype := value;
We can assign value to the signal using a signal assignment statement, as shown below:
signal_label <= value; --OR signal_label <= expression;
You can also introduce a delay in the signal assignment statement using
after clause. When we don’t provide any delay, then the VHDL compiler assumes a default delta delay. You can learn more about delays here.
The case statement is a type of conditional statement. We provide an expression as a case, and when the compiler executes this block, it evaluates the expression first. Then it goes through all the choices, and when a choice matches the expression’s value, it executes the corresponding statements.
Syntax of a case statement:
case expression is when choice1 => sequential-statements; when choice2 => another sequential-statements; . . . . when others => more sequential-statements; end case;
Let’s understand this with an interesting example. Nowadays the prices of tickets at some places like amusement parks, circuses, etc. vary depending on the day of the week.
People generally do not visit these kinds of places at the beginning of the week. So to encourage people to visit these days, management decreases the fare for the first few days of the week. So we will write a VHDL program to decide fare according to the day of the week. Interesting? Let’s do it.
First, we create a user-defined datatype ‘WEEK’ to store the names of all days in a week.
type WEEK is (MON, TUE, WED, THU, FRI, SAT, SUN);
Then we create a variable ‘DAY’ of type ‘WEEK’ to store the value/name of the current day.
variable DAY: WEEK;
Then we again create user-defined datatype ‘DOLLARS’ and give it a range for the price of the ticket. We can also make it a subtype of integer data type, but again that depends on us.
type DOLLARS is range 150 to 350;
Then we create a variable of type ‘DOLLARS’ to store the value of ticket price.
variable TICKET_PRICE: DOLLARS;
Now we use case-statement for conditional operation. We use the variable ‘DAY’ as evaluating parameters. As we have discussed earlier, that at the beginning of the week, the ticket price is meager. And on the weekend most of the people are free to make them as high as possible.
case DAY is when MON | TUE => TICKET_PRICE := 150; when WED => TICKET_PRICE := 200; when FRI to SUN => TICKET_PRICE := 350; when others => TICKET_PRICE := 250; end case;
In the above code, in first choice ‘MON | TUE,’ the vertical line is the logical OR operator in VHDL. That means when the value of the case ‘DAY’ is either ‘MON’ or ‘TUE,’ the compiler will assign the same value to TICKET_PRICE.
And in the third choice, ‘FRI to SUN,’ it captures the entire range from its datatype. That means when the value of ‘DAY’ matches any of these three ‘FRI,’ ‘SAT,’ ‘SUN,’ TICKET_PRICE will be 350.
And the last choice covers all the remaining cases. Here it is ‘THU’ only, but when we have a large number of choices, it helps in covering all corner cases.
‘ if ’ statements are also conditional statements. Now, you may wonder then what is the difference between case and if statements.
The difference is that the case statement matches the condition with choices. But ‘if’ condition just checks the condition, whether it is true or not. That is why it only takes conditions as a Boolean expression. And the statement evaluates to a Boolean value, either ‘1’ or ‘0’, or you can say ‘HIGH’ or ‘LOW.’
Syntax of an if statement:
if boolean-expression then sequential-statements; elsif boolean-expression then sequential-statements; else sequential-statements; end if;
When the VHDL compiler executes the ‘ if ’ statement, it checks all the conditions sequentially. It keeps checking until it finds a true condition. When it finds a true condition, it executes the sequential statements associated with it. And if it does not find any true condition, then it executes the statements under the
else clause. Though,
else clauses are optional,
if statement can have zero or more
All sequential statements in VHDL support nesting. So we can also create nested if statements as shown in the below example.
if MODE = '1' then if Enable = '1' then OUTPUT<= "0010"; else null; end if; else if Enable = '1' then OUTPUT <= "1000"; else null; end if; end if;
In the above code, we first check the mode; if MODE is ‘1’, then we check for Enable. If enable is also high, then we assign a value to OUTPUT, else we run a
null statement and end the ‘ if ’ block.
And if MODE is not equal to ‘1’ compiler will run the
else part of the main/outer if statement. That also has a similar logical structure.
A process with a sensitivity list will be suspended after the execution of the last sequential statement. But is the process does not have any sensitivity list; then we use the
wait statement to suspend the process. In VHDL, we have three forms of the wait statement:
wait on sensitivity-list; ------------------------------- wait for time-expression ; ------------------------------- wait until boolean-expression ;
We can also combine these three according to our needs.
wait on sensitivity-list until boolean-expression for time-expression;
Some examples of wait statements are:
wait on signal1, signal2, signal3; ---------------------------------- wait for 20 ns; ---------------------------------- wait on CLK for 10ns; ---------------------------------- wait until (A & B = 1); ---------------------------------- wait until (SUM > 100) for 50 ns;
loop statements to iterate through a set of sequential statements. The syntax of the loop statement is:
loop-label : iteration-scheme loop set of sequential-statements end loop loop-label ;
Loop statement has three types of the iteration scheme
for identifier in range ------------------------ while boolean-expression ------------------------ Unspecified
In the first iteration scheme, the compiler will execute the body of loop N-1 times. The starting and ending value of the identifier is specified in range. After every loop execution, the value of the identifier increased by 1 and continues until it reaches the maximum of the range.
In the second iteration scheme, we provide a Boolean expression. While the value of that expression is true, the loop will run continuously. This could create a problem if we provide an expression that is always true. For example, is we write
while A and B
and our program is written in a way that the value of A and B is always 1. This will create an infinite loop, and we need some control over the situation. So for this, we have our next sequential statement the
In VHDL, we use the
exit statement to finish a
loop. Therefore this statement can only exist inside a
loop. It causes the execution to suspend the innermost loop, or the loop whose label we provide in the statement. Its syntax is:
Exit loop-label when condition;
If we don’t provide any loop label, then only the innermost loop will be suspended/exited.
We can also provide a condition in the form of a Boolean expression if that becomes true loop will be terminated. For example:
SUM := 1; Factor:=1; Binary_growth: loop SUM := SUM * 2; Factor := Factor +1; exit when SUM > 1024 end loop Binary_growth;
In VHDL, we use the
next statement to skip the current iteration of the
loop. Therefore this statement can only exist inside a
loop. It has the same syntax as of exit statement just the
exit keyword is replaced by
next. Its syntax is:
next loop-label when condition;
The execution of the
next statement causes skipping of the remaining statement of the specified loop in the current iteration. If we don’t provide any label, then the only innermost loop is considered. So as a comparison, the
exit statement terminates the loop while the
next statement terminates the current iteration only.
Let’s understand this with an example.
We will write a code that will traverse through an array ‘Number’ containing ten integers values. And it will find the number of elements greater than 20.
Number_found := 1; Loop1 : for J in 10 downto 1 loop next Loop1 when Number(J) < 20; Number_found := Number_found + 1; end loop Loop1;
In the above code, firstly, we initialize a variable ‘Number_found.’ And after the execution of the loop, it will have a total count of numbers greater than 20.
Then we create a loop with the label(Loop1) and give it a range of 10 to 1.
Then inside the loop, we use the next statement with a condition. The condition says, when the number at Jth position is less than 20, skip this iteration. This is because, just after this statement, we increment our variable. If the current number is smaller than 20, we don’t want to increase the count, so we skip it.
And if the number is greater than 20, the loop will execute further and will increase the count.
In VHDL, we use assertion statements to model constraints for an entity. These are very helpful in verifying the test results automatically. For example, you can check if setup and hold times for a signal arriving at any point are okay or not. You can also check if the output from an entity or value of a signal lies within a range. If the check fails, an error message can be reported.
assert boolean-expression report error-message severity expression;
In the above syntax, the
assert keyword is used along with a Boolean expression, which is a constraint. If the value gets out of constraint, the Boolean expression equates to ‘0’ or ‘FALSE.’ And it triggers the report statement which will print the given error message on the log window.
We can also provide a severity level. You may remember that
severity level is a datatype in VHDL. If you want to learn more about it, refer to our article on Datatypes in VHDL.
Generally, we use severity levels to do some action based on its value. The simulator may terminate the simulation if the severity is ERROR or at least display it.
Let’s understand it better with an example. We will take the SR flip-flop as an example here.
We know that in SR flip-flop S= R=‘0’, it is a violation, and in this condition, the output is undefined. So in practice, we try not to implement those situations which create ambiguity.
But when we have a large circuit where a lot of signals are propagating simultaneously. It’s not easy or sometimes not even feasible to keep track of all in simulation results. So we write some extra lines of code for the job. If those specific constraints or conditions are violated, the compiler will perform an appropriate action.
entity SR_FF is port (S, R, clk: in BIT; Q, Qbar: out BIT); end SR_FF; architecture SR_ASSERTION of SR_FF is begin process(S,R,clk) begin assert (not(S = '0' and R = '0')) report "Invalid inputs: R and S are both low" severity ERROR; -- Rest of model for SR flip-flop here. end SR_ASSERTION;
In the above code, whenever ( S= R=‘0’) condition is TRUE, and a
not operator is used to make it FALSE. And as mentioned earlier, when the Boolean expression of assert statement is FALSE, it triggers. So, a message that is given along with the
report keyword will be displayed as a log.
We have studied about delay earlier in our article on dataflow modeling in VHDL. There we studied about delta delay and delay using
But when we use
after clause inside a
process it behaves differently. And here we call it inertial delay.
Inertial delay model
We use inertial delay generally in digital circuits. A major problem in digital circuits is switching noise. Switching signals have a shape of the square wave, which leads to noise in a vast spectrum. This can lead to electrical spikes of significant amplitude inside the circuit. This will lead to data loss and injection of error bits in the information bitstream. To avoid it, we specify a time period for which the input signal should be stable. Only after that, the signal is allowed to propagate to the output. This rejects all the spikes as they have a relatively short time period.
In the above figure, only the pulses having a time period longer than 10ns have appeared on the output. And remaining pulses are considered as noise/spikes and are rejected.
We can also explicitly give a rejection limit. Its syntax with the
reject limit is
Z <= reject 5 ns inertial A after 10 ns;
Spikes of time period shorter than the
reject limit will not be allowed to propagate to Z from A.
Transport delay model
This can be thought of as a normal propagation delay. Transport delay in behavioral modeling is the same as “delay using
after clause” in dataflow modeling. Its syntax is
Z <= transport A after 10 ns;
The above statement will transfer all input pulses to the output with a delay of 10 nanoseconds. These are used to model natural propagation delay, which exists in circuits due to the physical limitation of the materials.
In the above code, a simple buffer is modeled with a transport delay of 10 nanoseconds.
In this case, spikes will also propagate through, but that’s practical because spikes can also travel in the circuit in wires.
That sums up the behavioral modeling style in VHDL. In our next article in this VHDL course, we will study the structural modeling style. As always, your comments and queries are welcome in the comments section below.