Home » Programming » 8. Behavioral Modeling: Sequential Statements

8. Behavioral Modeling: Sequential Statements

 

As discussed earlier, VHDL provides means to represent digital circuits at different levels of representation of abstraction, such as the behavioral and structural modeling. In this section we will discuss different constructs for describing the behavior of components and circuits in terms of sequential statements. The basis for sequential modeling is the process construct. As you will see, the process construct allows us to model complex digital systems, in particular sequential circuits.

 

  1. Process

 

A process statement is the main construct in behavioral modeling that allows you to use sequential statements to describe the behavior of a system over time. The syntax for a process statement is

 

[process_label:] process [ (sensitivity_list) ] [is]

[ process_declarations]

begin

list of sequential statements such as:

                                    signal assignments

                                    variable assignments 

case statement

                                    exit statement

                                    if statement

                                    loop statement

                                    next statement           

                                    null statement

                                    procedure call

                                    wait statement

            end process [process_label];

 

An example of a positive edge-triggered D flip-flop with asynchronous clear input follows.

 

library ieee;

use ieee.std_logic_1164.all;

entity DFF_CLEAR is

     port (CLK, CLEAR, D : in std_logic;

Q : out std_logic);

end DFF_CLEAR;

 

architecture BEHAV_DFF of DFF_CLEAR is

begin

DFF_PROCESS: process (CLK, CLEAR)

begin

           if (CLEAR = ‘1’) then

Q <= ‘0’;

elsif (CLK’event and CLK = ‘1’) then

Q <= D;

           end if;

     end process;

end BEHAV_DFF;

 

A process is declared within an architecture and is a concurrent statement. However, the statements inside a process are executed sequentially. Like other concurrent statements, a process reads and writes signals and values of the interface (input and output) ports to communicate with the rest of the architecture. One can thus make assignments to signals that are defined externally (e.g. interface ports) to the process, such as the Q output of the flip-flop in the above example. The expression CLK’event and CLK = ‘1’ checks for a positive clock edge (clock event AND clock high).

 

The sensitivity list is a set of signals to which the process is sensitive. Any change in the value of the signals in the sensitivity list will cause immediate execution of the process. If the sensitivity list is not specified, one has to include a wait statement to make sure that the process will halt. Notice that one cannot include both a sensitivity list and a wait statement. Variables and constants that are used inside a process have to be defined in the process_declarations part before the keyword begin. The keyword begin signals the start of the computational part of the process. The statements are sequentially executed, similarly as a conventional software program. It should be noted that variable assignments inside a process are executed immediately and denoted by the “:=” operator. This is in contrast to signal assignments denoted by “<=” and which changes occur after a delay. As a result, changes made to variables will be available immediately to all subsequent statements within the same process. For an example that illustrates the difference between signal and variable assignments see the section on Data Types (difference between signals and variables).

 

The previous example of the D flip-flop illustrates how to describe a sequential circuit with the process statement. Although the process is mainly used to describe sequential circuits, one can also describe combinational circuits with the process construct. The following example illustrates this for a Full Adder, composed of two Half Adders. This example also illustrates how one process can generate signals that will trigger other processes when events on the signals in its sensitivity list occur [3]. We can write the Boolean expression of a Half Adder and Full Adder as follows:

 

S_ha = (AÅB)             and C_ha = AB

 

For the Full Adder:

 

Sum = (AÅB)ÅCin = S_ha ÅCin

Cout = (AÅB)Cin + AB = S_ha.Cin + C_ha

 

Figure 5 illustrates how the Full Adder has been modeled.

 

 

 

Figure 5: Full Adder composed of two Half Adders, modeled with two processes P1 and P2.

 

library ieee;

use ieee.std_logic_1164.all;

entity FULL_ADDER is

     port (A, B, Cin : in std_logic;

Sum, Cout : out std_logic);

end FULL_ADDER;

 

architecture BEHAV_FA of FULL_ADDER is

signal int1, int2, int3: std_logic;

begin

— Process P1 that defines the first half adder

P1: process (A, B)

begin

           int1<= A xor B;

           int2<= A and B;

     end process;

— Process P2 that defines the second half adder and the OR — gate

P2: process (int1, int2, Cin)

begin

          Sum <= int1 xor Cin;

int3 <= int1 and Cin;

           Cout <= int2 or int3;

     end process;

end BEHAV_FA;

 

Of course, one could simplify the behavioral model significantly by using a single process.

 

  1. If Statements

 

The if statement executes a sequence of statements whose sequence depends on one or more conditions. The syntax is as follows:

 

if condition then

sequential statements

[elsif condition then

           sequential statements ]

[else

           sequential statements ]

end if;

 

Each condition is a Boolean expression. The if statement is performed by checking each condition in the order they are presented until a “true” is found. Nesting of if statements is allowed. An example of an if statement was given earlier for a D Flip-flop with asynchronous clear input. The if statement can be used to describe combinational circuits as well. The following example illustrates this for a 4-to-1 multiplexer with inputs A, B, C and D, and select signals S0 and S1. This statement must be inside a process construct. We will see that other constructs, such as the Conditional Signal Assignment (“When-else”) or “Select” construct may be more convenient for these type of combinational circuits.

 

entity MUX_4_1a is

port (S1, S0, A, B, C, D: in std_logic;

Z: out std_logic);

end MUX_4_1a;

architecture behav_MUX41a of MUX_4_1a is

begin

P1: process (S1, S0, A, B, C, D)

begin

if (( not S1 and not S0 )=’1’) then

Z <= A;

elsif (( not S1 and S0) = ‘1’) then

Z<=B;

elsif ((S1 and not S0) =’1’) then

Z <=C;

else

Z<=D;

end if;

   end process P1;

end behav_MUX41a;

 

A slightly different way of modeling the same multiplexer is shown below,

 

if S1=’0’ and S0=’0’ then

Z <= A;

elsif S1=’0’ and S0=’1’ then

Z <= B;

elsif S1=’1’ and S0=’0’ then

Z <= C;

elsif S1=’1’ and S0=’1’ then

Z <= D;

end if;

 

If statements are often used to implement state diagrams. For an example of a Mealy machine see Example Mealy Machine later on.

 

  1. Case statements

 

The case statement executes one of several sequences of statements, based on the value of a single expression. The syntax is as follows,

 

case expression is

           when choices =>

                sequential statements

           when choices =>

                sequential statements

                — branches are allowed

           [ when others => sequential statements ]

end case;

 

The expression must evaluate to an integer, an enumerated type of a one-dimensional array, such as a bit_vector. The case statement evaluates the expression and compares the value to each of the choices. The when clause corresponding to the matching choice will have its statements executed. The following rules must be adhered to:

 

  • no two choices can overlap (i.e. each choice can be covered only once)
  • if the “when others” choice is not present, all possible values of the expression must be covered by the set of choices.

 

An example of a case statement using an enumerated type follows. It gives an output D=1 when the signal GRADES has a value between 51 and 60, C=1 for grades between 61 and 70, the when others covers all the other grades and result in an F=1.

 

library ieee;

use ieee.std_logic_1164.all;

entity GRD_201 is

port(VALUE: in integer range 0 to 100;

A, B, C, D: out bit);

end GRD_201;

architecture behav_grd of GRD_201 is

begin

     process (VALUE)

A <= ’0’;

B <= ’0’;

C <= ’0’;

D <= ’0’;

F <= ’0’;

begin

case VALUE is

     when 51 to 60 =>

D <= ’1’;

    when 61 to 70 | 71 to 75 =>

C <= ’1’;

     when 76 to 85 =>

B <= ’1’;

when 86 to 100 =>

A <= ’1’;

when others =>

F <= ‘1’;

end case;

     end process;

end behav_grd;

    

We used the vertical bar ( | ) which is equivalent to the “or” operator, to illustrate how to express a range of values. This is a useful operator to indicate ranges that are not adjacent (e.g. 0 to 4 | 6 to 10).

 

Another example using the case construct is a 4-to-1 MUX.

 

entity MUX_4_1 is

port ( SEL: in std_logic_vector(2 downto 1);

A, B, C, D: in std_logic;

Z: out std_logic);

end MUX_4_1;

architecture behav_MUX41 of MUX_4_1 is

begin

PR_MUX: process (SEL, A, B, C, D)

begin

case SEL is

when “00” => Z <= A;

when “01” => Z <= B;

when “10” => Z <= C;

when “11” => Z <= D;

when others => Z <= ‘X’;

end case;

   end process PR_MUX;

end behav_MUX41;

 

The “when others” covers the cases when SEL=”0X”, “0Z”, “XZ”, “UX”, etc. It should be noted that these combinational circuits can be expressed in other ways, using concurrent statements such as the “With – Select” construct. Since the case statement is a sequential statement, one can have nested case statements.

 

  1. Loop statements

 

A loop statement is used to repeatedly execute a sequence of sequential statements. The syntax for a loop is as follows:

 

[ loop_label :]iteration_scheme loop

           sequential statements

           [next [label] [when condition];

           [exit [label] [when condition];

     end loop [loop_label];

 

Labels are optional but are useful when writing nested loops. The next and exit statement are sequential statements that can only be used inside a loop.

 

  • The next statement terminates the rest of the current loop iteration and execution will proceed to the next loop iteration.
  • The exit statement skips the rest of the statements, terminating the loop entirely, and continues with the next statement after the exited loop.

 

There are three types of iteration schemes:

 

  • basic loop
  • while … loop
  • for … loop

 

Basic Loop statement

 

This loop has no iteration scheme. It will be executed continuously until it encounters an exit or next statement.

 

[ loop_label :] loop

           sequential statements

           [next [label] [when condition];

           [exit [label] [when condition];

     end loop [ loop_label];

 

The basic loop (as well as the while-loop) must have at least one wait statement. As an example, lets consider a 5-bit counter that counts from 0 to 31. When it reaches 31, it will start over from 0. A wait statement has been included so that the loop will execute every time the clock changes from ‘0’ to ‘1’.

 

 

Example of a basic loop to implement a counter that counts from 0 to 31
entity COUNT31 is

port ( CLK: in std_logic;

COUNT: out integer);

end COUNT31;

architecture behav_COUNT of COUNT31 is

begin

P_COUNT: process

variable intern_value: integer :=0;

begin

COUNT <= intern_value;

loop

wait until CLK=’1’;

intern_value:=(intern_value + 1) mod 32;

COUNT <= intern_value;

end loop;

end process P_COUNT;

end behav_COUNT;

 

 

We defined a variable intern_value inside the process because output ports cannot be read inside the process.

 

While-Loop statement

 

The while … loop evaluates a Boolean iteration condition. When the condition is TRUE, the loop repeats, otherwise the loop is skipped and the execution will halt. The syntax for the while…loop is as follows,

 

[ loop_label :] while condition loop

           sequential statements

           [next [label] [when condition];

           [exit [label] [when condition];

     end loop[ loop_label ];

 

The condition of the loop is tested before each iteration, including the first iteration. If it is false, the loop is terminated.

 

For-Loop statement

 

The for-loop uses an integer iteration scheme that determines the number of iterations. The syntax is as follows,

 

[ loop_label :] for identifier in range loop

           sequential statements

           [next [label] [when condition];

           [exit [label] [when condition];

     end loop[ loop_label ];

 

  • The identifier (index) is automatically declared by the loop itself, so one does not need to declare it separately. The value of the identifier can only be read inside the loop and is not available outside its loop. One cannot assign or change the value of the index. This is in contrast to the while-loop whose condition can involve variables that are modified inside the loop.
  • The range must be a computable integer range in one of the following forms, in which integer_expression must evaluate to an integer:
    • integer_expression to integer_expression
    • integer_expression downto integer_expression

 

  1. Next and Exit Statement

 

The next statement skips execution to the next iteration of a loop statement and proceeds with the next iteration. The syntax is

 

next [label] [when condition];

 

The when keyword is optional and will execute the next statement when its condition evaluates to the Boolean value TRUE.

 

The exit statement skips the rest of the statements, terminating the loop entirely, and continues with the next statement after the exited loop. The syntax is as follows:

 

exit [label] [when condition];

 

The when keyword is optional and will execute the next statement when its condition evaluates to the Boolean value TRUE.

 

Notice that the difference between the next and exit statement, is that the exit statement terminates the loop.

 

  1. Wait statement

 

The wait statement will halt a process until an event occurs. There are several forms of the wait statement,

 

wait until condition;

            wait for time expression;

            wait on signal;

            wait;

 

The Xilinx Foundation Express has implemented only the first form of the wait statement. The syntax is as follows,

 

wait until signal = value;

wait until signal’event and signal = value;

wait until not signal’stable and signal = value;

 

The condition in the “wait until” statement must be TRUE for the process to resume. A few examples follow.

 

wait until CLK=’1’;

wait until CLK=’0’;

wait until CLK’event and CLK=’1’;

wait until not CLK’stable and CLK=’1’;

 

For the first example the process will wait until a positive-going clock edge occurs, while for the second example, the process will wait until a negative-going clock edge arrives. The last two examples are equivalent to the first one (positive-edge or 0-1 transitions). The hardware implementation for these three statements will be identical.

 

It should be noted that a process that contains a wait statement can not have a sensitivity list. If a process uses one or more wait statements, the Foundation Express synthesizer will use sequential logic. The results of the computations are stored in flip-flops.

 

  1. Null statement

 

The null statement states that no action will occur. The syntax is as follows,

 

null;

 

It can be useful in a case statement where all choices must be covered, even if some of them can be ignored. As an example, consider a control signal CNTL in the range 0 to 31. When the value of CNTL is 3 or 15, the signals A and B will be xor-ed, otherwise nothing will occur.

 

entity EX_WAIT is

port ( CNTL: in integer range 0 to 31;

           A, B: in std_logic_vector(7 downto 0);

Z: out std_logic_vector(7 downto 0) );

end EX_WAIT;

architecture arch_wait of EX_WAIT is

begin

P_WAIT: process (CNTL)

begin

Z <=A;

case CNTL is

           when 3 | 15 =>

Z <= A xor B;

when others =>

null;

end case;

end process P_WAIT;

end arch_wait;

 

  1. Example of a Mealy Machine

The sequence following detector recognizes the input bit sequence X: “1011”. The machine will keep checking for the proper bit sequence and does not reset to the initial state after it recognizes the string. In case we are implementing a Mealy machine, the output is associated with the transitions as indicated on the following state diagram (Figure 6).

Figure 6: Sequence detector (1011), realized as a Mealy Machine.

 

The VHDL file is given below.

 

VHDL file for a sequence detector (1011) implemented as a Mealy Machine
library ieee;

use ieee.std_logic_1164.all;

 

entity myvhdl is

port (CLK, RST, X: in STD_LOGIC;

Z: out STD_LOGIC);

end;

 

architecture myvhdl_arch of myvhdl is

— SYMBOLIC ENCODED state machine: Sreg0

type Sreg0_type is (S1, S2, S3, S4);

signal Sreg0: Sreg0_type;

begin

–concurrent signal assignments

Sreg0_machine: process (CLK)

begin

if CLK’event and CLK = ‘1’ then

if RST=’1′ then

Sreg0 <= S1;

else

case Sreg0 is

when S1 =>

if X=’0′ then

Sreg0 <= S1;

elsif X=’1′ then

Sreg0 <= S2;

end if;

when S2 =>

if X=’1′ then

Sreg0 <= S2;

elsif X=’0′ then

Sreg0 <= S3;

end if;

when S3 =>

if X=’1′ then

Sreg0 <= S4;

elsif X=’0′ then

Sreg0 <= S1;

end if;

when S4 =>

if X=’0′ then

Sreg0 <= S3;

elsif X=’1′ then

Sreg0 <= S2;

end if;

when others =>

null;

end case;

end if;

end if;

end process;

— signal assignment statements for combinatorial outputs

Z_assignment:

Z <= ‘0’ when (Sreg0 = S1 and X=’0′) else

‘0’ when (Sreg0 = S1 and X=’1′) else

‘0’ when (Sreg0 = S2 and X=’1′) else

‘0’ when (Sreg0 = S2 and X=’0′) else

‘0’ when (Sreg0 = S3 and X=’1′) else

‘0’ when (Sreg0 = S3 and X=’0′) else

‘0’ when (Sreg0 = S4 and X=’0′) else

‘1’ when (Sreg0 = S4 and X=’1′) else

‘1’;

end myvhdl_arch;

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Name *
Email *
Website

December 2024
M T W T F S S
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

Archives