|
10.10 Sequential Statements
Chapter start Previous page Next page
10.10 Sequential Statements
A sequential statement [VHDL
LRM8] is defined as follows:
sequential_statement ::=
wait_statement | assertion_statement
| signal_assignment_statement
| variable_assignment_statement | procedure_call_statement
| if_statement | case_statement | loop_statement
| next_statement | exit_statement
| return_statement | null_statement | report_statement
Sequential statements may only appear in processes
and subprograms. In the following sections I shall describe each of these
different types of sequential statements in turn.
10.10.1 Wait Statement
The wait statement is central to VHDL, here
are the BNF definitions [VHDL 93LRM8.1]:
wait_statement ::= [label:] wait [sensitivity_clause]
[condition_clause] [timeout_clause] ;
sensitivity_clause ::= on sensitivity_list
sensitivity_list ::= signal_name { , signal_name }
condition_clause ::= until condition
condition ::= boolean_expression
timeout_clause ::= for time_expression
A wait statement suspends (stops)
a process or procedure (you cannot use a wait statement in a function).
The wait statement may be made sensitive to events (changes) on
static signals (the value of the signal must be known at analysis time)
that appear in the sensitivity list after the keyword on . These
signals form the sensitivity set of a wait statement. The process will resume
(restart) when an event occurs on any signal (and only signals) in the sensitivity
set. A wait statement
may also contain a condition to be met before the process resumes. If there
is no sensitivity clause (there is no keyword on ) the sensitivity
set is made from signals (and only signals) from the condition clause that
appears after the keyword until (the rules are quite complicated
[VHDL 93LRM8.1]). Finally a wait statement may also contain
a timeout (following the keyword for ) after which the process
will resume. Here is the expanded BNF definition, which makes the structure
of the wait statement easier to see (but we lose the definitions
of the clauses and the sensitivity list):
wait_statement ::= [label:]
wait
[on signal_name {, signal_name}]
[until boolean_expression]
[for time_expression] ;
For example, the statement, wait on light
, makes you wait until a traffic light changes (any change). The statement,
wait until light = green , makes you wait (even
at a green light) until the traffic signal changes to green. The statement,
if light = (red or yellow) then wait
until light = green;
end
if;
accurately describes the basic rules at a
traffic intersection. The most
common use of the wait statement is to describe synchronous logic,
as in the following model of a D flip-flop:
entity DFF is port (CLK, D : BIT; Q : out BIT); end;
architecture Behave of DFF is
process begin wait
until C
lk = '1'; Q <= D ; end process;
end;
Notice that the statement in line 3 above,
wait until C lk = '1', is equivalent to wait on Clk until C
lk = '1', and detects a clock edge and not the clock level. Here are some
more complex examples of the use of the wait statement:
entity Wait_1 is port (Clk, s1, s2 :in BIT); end;
architecture Behave of Wait_1 is
signal x : BIT_VECTOR (0 to 15);
begin process variable v : BIT; begin
wait; -- Wait forever, stops simulation.
wait on s1 until s2 = '1'; -- Legal, but s1, s2 are signals so
-- s1 is in sensitivity list, and s2 is not in the sensitivity set.
-- Sensitivity set is s1 and process will not resume at event on s2.
wait on s1, s2; -- resumes at event on signal s1 or s2.
wait on s1 for 10 ns; -- resumes at event on s1 or after 10 ns.
wait on x; -- resumes when any element of array x has an event.
-- wait on x(1 to v); -- Illegal, nonstatic name, since v is a variable.
end process;
end;
entity Wait_2 is port (Clk, s1, s2:in BIT); end;
architecture Behave of Wait_2 is
begin process variable v : BIT; begin
wait on Clk; -- resumes when Clk has an event: rising or falling.
wait until Clk = '1'; -- resumes on rising edge.
wait on Clk until Clk = '1'; -- equivalent to the last statement.
wait on Clk until v = '1';
-- The above is legal, but v is a variable so
-- Clk is in sensitivity list, v is not in the sensitivity set.
-- Sensitivity set is Clk and process will not resume at event on v.
wait on Clk until s1 = '1';
-- The above is legal, but s1 is a signal so
-- Clk is in sensitivity list, s1 is not in the sensitivity set.
-- Sensitivity set is Clk, process will not resume at event on s1.
end process;
end;
You may only use interface signals that may
be read (port modes in , inout , and buffer --see
Section 10.7) in the sensitivity list of a wait statement.
10.10.2 Assertion and Report Statements
You can use an assertion statement to conditionally
issue warnings. The report statement (VHDL-93 only) prints an expression
and is useful for debugging.
assertion_statement ::= [label:] assert
boolean_expression [report expression] [severity expression] ;
report_statement
::= [label:] report expression [severity expression] ;
Here is an example of an assertion statement:
entity Assert_1 is port (I:INTEGER:=0); end;
architecture Behave of Assert_1 is
begin process begin
assert (I > 0) report "I is negative or zero"; wait;
end process;
end;
The expression after the keyword report
must be of type STRING (the default is "Assertion violation"
for the assertion statement), and the expression after the keyword
severity must be of type SEVERITY_LEVEL (default ERROR
for the assertion statement, and NOTE for the report
statement) defined in the STANDARD package. The assertion statement
prints if the assertion condition (after the keyword assert ) is
FALSE . Simulation normally halts for severity of ERROR
or FAILURE (you can normally control this threshold in the simulator).
10.10.3 Assignment Statements
There are two sorts of VHDL assignment statements:
one for signals and one for variables [VHDL
93LRM8.4-8.5]. The difference
is in the timing of the update of the LHS. A variable assignment statement
is the closest equivalent to the assignment statement in a computer programming
language. Variable assignment statements are always sequential statements
and the LHS of a variable assignment statement is always updated immediately.
Here is the definition and an example:
variable_assignment_statement ::=
[label:] name|aggregate := expression ;
entity Var_Assignment is end;
architecture Behave of Var_Assignment is
signal s1 : INTEGER := 0;
begin process variable v1,v2 : INTEGER := 0; begin
assert (v1/=0) report "v1 is 0" severity note ; -- this prints
v1 := v1 + 1; -- after this statement v1 is 1
assert (v1=0) report "v1 isn't 0" severity note ; -- this prints
v2 := v2 + s1; -- signal and variable types must match
wait;
end process;
end;
This is the output from Cadence Leapfrog for
the preceding example:
ASSERT/NOTE (time 0 FS) from : (design unit WORK.VAR_ASSIGNMENT:BEHAVE) v1 is 0
ASSERT/NOTE (time 0 FS) from : (design unit WORK.VAR_ASSIGNMENT:BEHAVE) v1 isn't 0
A signal assignment statement schedules a future
assignment to a signal:
signal_assignment_statement::=
[label:] target <=
[transport | [ reject time_expression ] inertial ] waveform ;
The following example shows that, even with
no delay, a signal is updated at the end of a simulation cycle after all
the other assignments have been scheduled, just before simulation time is
advanced:
entity Sig_Assignment_1 is end;
architecture Behave of Sig_Assignment_1 is
signal s1,s2,s3 : INTEGER := 0;
begin process variable v1 : INTEGER := 1; begin
assert (s1 /= 0) report "s1 is 0" severity note ; -- this prints.
s1 <= s1 + 1; -- after this statement s1 is still 0.
assert (s1 /= 0) report "s1 still 0" severity note ; -- this prints.
wait;
end process;
end;
ASSERT/NOTE (time 0 FS) from : (design unit WORK.SIG_ASSIGNMENT_1:BEHAVE) s1 is 0
ASSERT/NOTE (time 0 FS) from : (design unit WORK.SIG_ASSIGNMENT_1:BEHAVE) s1 still 0
Here is an another example to illustrate how
time is handled:
entity Sig_Assignment_2 is end;
architecture Behave of Sig_Assignment_2 is
signal s1, s2, s3 : INTEGER := 0;
begin process variable v1 : INTEGER := 1; begin
-- s1, s2, s3 are initially 0; now consider the following:
s1 <= 1 ; -- schedules updates to s1 at end of 0 ns cycle.
s2 <= s1; -- s2 is 0, not 1.
wait for 1 ns;
s3 <= s1; -- now s3 will be 1 at 1 ns.
wait;
end process;
end;
The Compass simulator produces the following
trace file for this example:
Time(fs) + Cycle s1 s2 s3
---------------------- ------------ ------------ ------------
0+ 0: 0 0 0
0+ 1: * 1 * 0 0
...
1000000+ 1: 1 0 * 1
Time is indicated in femtoseconds for each
simulation cycle plus the number of delta cycles (we call this delta time,
measured in units of delta) needed to calculate all transactions on signals.
A transaction consists of a new value for a signal (which may be the same
as the old value) and the time delay for the value to take effect. An asterisk
'*' before a value in the preceding trace indicates that a transaction has
occurred and the corresponding signal updated at that time. A transaction
that does result in a change in value is an event. In the preceding simulation
trace for Sig_Assignment_2:Behave
At 0 ns + 0 delta: all signals are
0 .
At 0 ns + 1 delta: s1 is
updated to 1 , s2 is updated to 0 (not to 1
).
At 1 ns + 1 delta: s3 is
updated to a 1 .
The following example shows the behavior of
the different delay models: transport and inertial (the default):
entity Transport_1 is end;
architecture Behave of Transport_1 is
signal s1, SLOW, FAST, WIRE : BIT := '0';
begin process begin
s1 <= '1' after 1 ns, '0' after 2 ns, '1' after 3 ns ;
-- schedules s1 to be '1' at t+1 ns, '0' at t+2 ns,'1' at t+3 ns
wait; end process;
-- inertial delay: SLOW rejects pulsewidths less than 5ns:
process (s1) begin SLOW <= s1 after 5 ns ; end process;
-- inertial delay: FAST rejects pulsewidths less than 0.5ns:
process (s1) begin FAST <= s1 after 0.5 ns ; end process;
-- transport delay: WIRE passes all pulsewidths...
process (s1) begin WIRE <= transport s1 after 5 ns ; end process;
end;
Here is the trace file from the Compass simulator:
Time(fs) + Cycle s1 slow fast wire
---------------------- ---- ---- ---- ----
0+ 0: '0' '0' '0' '0'
500000+ 0: '0' '0' *'0' '0'
1000000+ 0: *'1' '0' '0' '0'
1500000+ 0: '1' '0' *'1' '0'
2000000+ 0: *'0' '0' '1' '0'
2500000+ 0: '0' '0' *'0' '0'
3000000+ 0: *'1' '0' '0' '0'
3500000+ 0: '1' '0' *'1' '0'
5000000+ 0: '1' '0' '1' *'0'
6000000+ 0: '1' '0' '1' *'1'
7000000+ 0: '1' '0' '1' *'0'
8000000+ 0: '1' *'1' '1' *'1'
Inertial delay mimics the behavior of real
logic gates, whereas transport delay more closely models the behavior of
wires. In VHDL-93 you can also add a separate pulse
rejection limit for the inertial delay model as in the following example:
process (s1) begin RJCT <= reject 2 ns s1 after 5 ns ; end process;
10.10.4 Procedure Call
A procedure call in VHDL corresponds to calling
a subroutine in a conventional programming language [VHDL
LRM8.6]. The parameters in a procedure call statement are the actual
procedure parameters (or actuals); the parameters in the procedure definition
are the formal procedure parameters (or formals). The two are linked using
an association list, which may use either positional or named association
(association works just as it does for ports--see Section 10.7.1):
procedure_call_statement ::=
[label:] procedure_name [(parameter_association_list)];
Here is an example:
package And_Pkg is
procedure V_And(a, b : BIT; signal c : out BIT);
function V_And(a, b : BIT) return BIT;
end;
package body And_Pkg is
procedure V_And(a, b : BIT; signal c: out BIT) is
begin c <= a and b; end;
function V_And(a, b: BIT) return BIT is
begin return a and b; end;
end And_Pkg;
use work.And_Pkg.all; entity Proc_Call_1 is end;
architecture Behave of Proc_Call_1 is signal A, B, Y: BIT := '0';
begin process begin V_And (A, B, Y); wait; end process;
end;
Table 10.13 on page 416 explains the rules
for formal procedure parameters. There is one other way to call procedures,
which we shall cover in Section 10.13.3.
10.10.5 If Statement
An if statement evaluates one or more Boolean
expressions and conditionally executes a corresponding sequence of statements
[VHDL LRM8.7].
if_statement ::=
[if_label:] if boolean_expression then {sequential_statement}
{elsif boolean_expression then {sequential_statement}}
[else {sequential_statement}]
end if [if_label];
The simplest form of an if statement
is thus:
if boolean_expression then {sequential_statement} end if;
Here are some examples of the if
statement:
entity If_Then_Else_1 is end;
architecture Behave of If_Then_Else_1 is signal a, b, c: BIT :='1';
begin process begin
if c = '1' then c <= a ; else c <= b; end if; wait;
end process;
end;
entity If_Then_1 is end;
architecture Behave of If_Then_1 is signal A, B, Y : BIT :='1';
begin process begin
if A = B then Y <= A; end if; wait;
end process;
end;
10.10.6 Case Statement
A case statement [VHDL
LRM8.8] is a multiway decision statement that selects a sequence of
statements by matching an expression with a list of (locally static [VHDL LRM7.4.1]) choices.
case_statement ::=
[case_label:] case expression is
when choice {| choice} => {sequential_statement}
{when choice {| choice} => {sequential_statement}}
end case [case_label];
Case statements are useful to model state
machines. Here is an example of a Mealy state machine with an asynchronous
reset:
library IEEE; use IEEE.STD_LOGIC_1164.all;
entity sm_mealy is
port (reset, clock, i1, i2 : STD_LOGIC; o1, o2 : out STD_LOGIC);
end sm_mealy;
architecture Behave of sm_mealy is
type STATES is (s0, s1, s2, s3); signal current, new : STATES;
begin
synchronous : process (clock, reset) begin
if To_X01(reset) = '0' then current <= s0;
elsif rising_edge(clock) then current <= new; end if;
end process;
combinational : process (current, i1, i2) begin
case current is
when s0 =>
if To_X01(i1) = '1' then o2 <='0'; o1 <='0'; new <= s2;
else o2 <= '1'; o1 <= '1'; new <= s1; end if;
when s1 =>
if To_X01(i2) = '1' then o2 <='1'; o1 <='0'; new <= s1;
else o2 <='0'; o1 <='1'; new <= s3; end if;
when s2 =>
if To_X01(i2) = '1' then o2 <='0'; o1 <='1'; new <= s2;
else o2 <= '1'; o1 <= '0'; new <= s0; end if;
when s3 => o2 <= '0'; o1 <= '0'; new <= s0;
when others => o2 <= '0'; o1 <= '0'; new <= s0;
end case;
end process;
end Behave;
Each possible value of the case expression
must be present once, and once only, in the list of choices (or arms) of
the case statement (the list must be exhaustive). You can use '|' (that
means 'or') or 'to' to denote a range in the expression for choice
. You may also use the keyword others as the last, default choice
(even if the list is already exhaustive, as in the preceding example).
10.10.7 Other Sequential Control Statements
A loop statement repeats execution of a series
of sequential statements [VHDL LRM8.9]:
loop_statement ::=
[loop_label:]
[while boolean_expression|for identifier in discrete_range]
loop
{sequential_statement}
end loop [loop_label];
If the loop variable (after the keyword for
) is used, it is only visible inside the loop. A while loop evaluates
the Boolean expression before each execution of the sequence of statements;
if the expression is TRUE , the statements are executed. In a for
loop the sequence of statements is executed once for each value of the discrete
range.
package And_Pkg is function V_And(a, b : BIT) return BIT; end;
package body And_Pkg is function V_And(a, b : BIT) return BIT is
begin return a and b; end; end And_Pkg;
entity Loop_1 is port (x, y : in BIT := '1'; s : out BIT := '0'); end;
use work.And_Pkg.all;
architecture Behave of Loop_1 is
begin loop
s <= V_And(x, y); wait on x, y;
end loop;
end;
The next statement [VHDL
LRM8.10] forces completion of the current iteration of a loop (the containing
loop unless another loop label is specified). Completion is forced if the
condition following the keyword then is TRUE (or if there
is no condition).
next_statement ::=
[label:] next [loop_label] [when boolean_expression];
An exit statement [VHDL
LRM8.11] forces an exit from a loop.
exit_statement ::=
[label:] exit [loop_label] [when condition] ;
As an example:
loop wait on Clk; exit when Clk = '0'; end loop;
-- equivalent to: wait until Clk = '0';
The return statement [VHDL
LRM8.12] completes execution of a procedure or function.
return_statement ::= [label:] return [expression];
A null statement [VHDL
LRM8.13] does nothing (but is useful in a case statement where
all choices must be covered, but for some of the choices you do not want
to do anything).
null_statement ::= [label:] null;
Chapter start Previous page Next page
|