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); endarchitecture 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 :$PROCESS_000 (design unit WORK.VAR_ASSIGNMENT:BEHAVE) v1 is 0
ASSERT/NOTE (time 0 FS) from :$PROCESS_000 (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 endarchitecture 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 :$PROCESS_000 (design unit WORK.SIG_ASSIGNMENT_1:BEHAVE) s1 is 0
ASSERT/NOTE (time 0 FS) from :$PROCESS_000 (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 endarchitecture 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

The following example shows the behavior of the different delay models: transport and inertial (the default):

entity Transport_1 is endarchitecture 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 endarchitecture 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 endarchitecture 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 endarchitecture 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.allentity 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.allarchitecture Behave of Loop_1 is 
        begin loop
                s <= V_And(x, y); wait on x, y; 
        end loopend;

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