5. Behavioral modeling

5.1. Introduction

In Chapter 2, 2-bit comparator is designed using behavior modeling. In that chapter, ‘if’ keyword was used in the ‘process’ statement block. This chapter presents some more such keywords.

5.2. Process block

All the statements inside the process block execute sequentially. Further, if the architecture contains more than one process block, then all the process blocks execute in parallel, i.e. process blocks are the concurrent blocks. Also, if a signal is assigned values multiple times, then only last assignments will be considered as shown in Listing 5.1. In the listing, value to port ‘z’ is assigned at Lines 19 and 21. In this case, last assignment will be considered i.e. ‘and’ gate will implemented by Line 21, as shown in Fig. 5.1

Listing 5.1 Multiple assignments to same signal
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- deltaDelayEx2.vhd

library ieee; 
use ieee.std_logic_1164.all;

entity deltaDelayEx2 is
port(
    x : inout std_logic;
    z : out std_logic
);
end entity;

architecture arch of deltaDelayEx2 is
    signal s : std_logic;
begin
    process(x)
    begin
        -- this line has no effect as value to z is assign below again
        z <= s; -- ignore by the compiler 
        s <= x after 2 ns;
        z <= x and s; -- only last assigment will be considered
    end process; 
end arch; 
../_images/multipleAssgEx.jpg

Fig. 5.1 Design generated by Listing 5.1

Note

  • If values are assigned to a signal multiple times, then only last assignment will be considered in the ‘sequential design (Listing 5.1)’, whereas error will be generated for ‘concurrent design (Listing 4.1)’.
  • We can write the complete design using sequential programming, but this may result in very complex hardware design, or to the design which can not be synthesized at all (see Section 5.9 also). The best way of designing is to make small units using behavioral and dataflow modeling along with FSM design (Chapter 9), and then use the structural modeling style to create the large system.

5.3. If-else statement

In this section, \(4\times 1\) multiplexed is designed using If-else statement. We already see the working of ‘if’ statement in Chapter 2. In lines of 17-27 of Listing 5.2, ‘elsif’ and ‘else’ are added to ‘if’ statement. Note that, If-else block can contain multiple ‘elsif’ statements between one ‘if’ and one ‘else’ statement. Further, ‘null’ is added in line 26 of Listing 5.2, whose function is same as ‘unaffected’ in concurrent signal assignment as shown in Listing 4.4. Fig. 5.3 shows the waveform generated by Modelsim for Listing 5.2.

Note

  • The ‘multiplexer design’ in Fig. 5.2 (generated by if-else in Listing 5.2) is exactly same as the design in Fig. 4.5 (generated by when-else in Listing 4.3).
  • Further, in Section 4.3, it is said that the ‘combinational logic can be implemented using both the concurrent statements and the sequential statements. Note that, the multiplexer is the ‘combinational design’ as the output depends only on current input values. And it implemented using ‘concurrent statements’ and ‘sequential statements’ in Listing 4.3 and Listing 5.2 respectively.
Listing 5.2 Multiplexer using if statement
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--ifEx.vhd

library ieee;
use ieee.std_logic_1164.all;

entity ifEx is
    port( s: in std_logic_vector(1 downto 0);
            i0, i1, i2, i3: in std_logic;
            y: out std_logic
        ); 
end ifEx;

architecture behave of ifEx is
begin
    process(s)
    begin
        if s = "00" then
            y <= i0;
        elsif s = "01" then
            y <= i1;
        elsif s = "10" then
            y <= i2;
        elsif s = "11" then 
            y <= i3;
        else 
            null; 
        end if;
    end process;
end behave;
../_images/ifEx.jpg

Fig. 5.2 Multiplexer using if statement, Listing 5.2

../_images/ifExWave.jpg

Fig. 5.3 Waveforms of Listing 5.2 and Listing 5.3

5.4. Case statement

Case statement is shown in lines 17-28 of Listing 5.3. ‘s’ is defined in case statement at line 17; whose value is checked using ‘when’ keyword at lines 18 and 20 etc. The value of ‘y’ depends on the value of ‘s’ e.g. if ‘s’ is ‘‘01’‘, then line 20 will be true, hence value of ‘i1’ will be assigned to ‘y’.

Note

Note that the ‘multiplexer design’ in Fig. 5.4 (generated by case in Listing 5.3) is exactly same as the design in Fig. 4.7 (generated by with-select in Listing 4.4).

Listing 5.3 Multiplexer using case statement
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--caseEx.vhd

library ieee;
use ieee.std_logic_1164.all;

entity caseEx is
    port( s: in std_logic_vector(1 downto 0);
            i0, i1, i2, i3: in std_logic;
            y: out std_logic
        ); 
end caseEx;

architecture behave of caseEx is
begin
    process(s)
    begin
        case s is
            when "00" =>
                y <= i0;
            when "01" =>
                y <= i1;
            when "10" =>
                y <= i2;
            when "11" =>
                y <= i3;
            when others =>
                null ;
        end case;
    end process;
end behave;
../_images/caseEx.jpg

Fig. 5.4 Multiplexer using case statement, Listing 5.3

5.5. Wait statement

‘Wait statement’ is used to hold the system for certain time duration. It can be used in three different ways as shown below,

  • wait until : It is synthesizable statement and holds the system until the defined condition is met e.g. ‘’wait until clk = ‘1’‘’ will hold the system until clk is ‘1’.
  • wait on : It is synthesizable statement and holds the system until the defined signal is changed e.g. ‘wait on clk’ will hold the system until clk changes it’s value i.e. ‘0’ to ‘1’ or vice-versa.
  • wait for : It is not synthesizable and holds the system for the defined timed e.g. ‘wait for 20ns’ will hold the system for 20 ns. This is used with testbenches as shown in Chapter 10.

5.6. Problem with Loops

VHDL provides two loop statements i.e. ‘for’ loop and ‘while’ loop’. These loops are very different from software loops. Suppose ‘for i = 1 to N’ is a loop, then, in software ‘i’ will be assigned one value at a time i.e. first i=1, then next cycle i=2 and so on. Whereas in VHDL, N logic will be implement for this loop, which will execute in parallel. Also, in software, ‘N’ cycles are required to complete the loop, whereas in VHDL the loop will execute in one cycle.

Note

As loops implement the design-units multiple times, therefore design may become large and sometimes can not be synthesized as well. If we do not want to execute everything in one cycle (which is almost always the case), then loops can be replaced by ‘case’ statements and ‘conditional’ statements as shown in Section 5.7. Further, due to these reasons, we do not use loops for the design. Lastly, the loops can be extremely useful in testbences, when we want to iterate through all the test-data which is shown in Listing 10.4.

5.7. Loop using ‘if’ statement

In Listing 5.4, a loop is created using ‘if’ statement, which counts the number upto input ‘x’.

Explanation Listing 5.4

In the listing, two ‘process’ blocks are used i.e. at lines 20 and 31. The process at line 20 checks whether the signal ‘count’ value is ‘less or equal’ to input x (line 22), and sets the currentState to ‘continueState’; otherwise if count is greater than the input x, then currentState is set to ‘stopState’.

Then next process statement (line 31), increase the ‘count’ by 1, if currentState is ‘continueState’; otherwise count is set to 0 for stopState. Finally count is displayed at the output through line 39. In this way, we can implement the loops using process statements.

Fig. 5.5 shows the loop generated by the listing with generic value N=1. Further, Fig. 5.6 shows the count-waveforms generated by the listing with generic value N = 3.

Sensitivity list of the process block should be implemented carefully. For example, if we add ‘count’ in the sensitivity list at line 31 of Listing Listing 5.4, then the process block will execute infinite times. This will occur because the process block execute whenever there is any event in the signals in the sensitivity list; therefore any change in ‘count’ will execute the block, and then this block will change the ‘count’ value through line 34. Since ‘count’ value is changed, therefore process block will execute again, and the loop will never exit.

Listing 5.4 Loop using ‘if’ statement
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
--ifLoop.vhd (-- This code is for simulation purpose only)
-- ideally positive or negative clock edge must be used; which will be discussed later.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ifLoop is
  generic ( N: integer := 3);
    port(  clk : in std_logic; -- clock: increase count on each clk
           x: in unsigned(N downto 0); -- count upto x
               z: out unsigned(N downto 0) -- display count
          ); 
end ifLoop;

architecture behave of ifLoop is
    signal count : unsigned(N downto 0):= (others => '0'); -- count signal
    type stateType is (continueState, stopState); --states to continue or stop the count
    signal currentState : stateType;
begin
    process(clk, currentstate, count)
    begin
        if (count <= x) then -- check whether count is completed till x
            currentState <= continueState; -- if not continue the count
        else
            currentState <= stopState; -- else stop further count
        end if;
    end process;
    
    --if we put `count' in below sensitivity list, 
    -- then below process block will work as infinite loop
    process(clk, currentstate) 
    begin
        if (currentState = continueState) then 
            count <= count + 1; -- increase count by 1 if continueState
        else 
          count <=(others => '0');  -- else set count to zero i.e. for stopState      
        end if;
    end process;    
    z <= count; -- show the count on output
end behave;
../_images/ifLoop.jpg

Fig. 5.5 Loop using ‘if’ statement, Listing 5.4 with N = 1

../_images/ifLoopWave.jpg

Fig. 5.6 Loop using ‘if’ statement, Listing 5.4 with N = 3

Note

Note that, the design in Listing 5.4 is not good because of following reasons,

  • First, the clock is used in the sensitive list, therefore it may arise race-around condition in the system. To avoid it, the system should be sensitive to only edge of the clock (i.e. positive and negative) , which is discussed in Listing 8.1 using ‘event’ keyword.
  • Secondly, the process statement in Line 31, does not required the ‘clk’ in the sensitive list. In Fig. 4.1, we saw that the sequential designs contain both ‘combinational logic’ and ‘sequential logic’; also the figure shows that the ‘clock’ is required only for ‘sequential logics’. But in the current design, we used clock in both ‘combinational logic’ and ‘sequential logic’. Detailed discussion about FSM designs are in Chapter 9, where such issues are raised for careful designs.

5.8. Unintentional memories in combinational designs

In previous sections, we saw various statements which can be used within the process-block. Also, in Section 4.2, we discussed that the combinational designs do not have memories. Note that, processes are very easy to implement, but careful design is required, otherwise unintended memories (latches) or internal states (via feedback paths) may be created in combination designs, which is not desirable. This section shows one example of ‘unintentional latches’ along with the precautions to avoid such errors.

Listing 5.5 assign the values to ‘large’ and ‘small’ based on the values of ‘a’ and ‘b’. The design generated by the listing is shown in Fig. 5.7. Note that two latches are created in the design. Latches are introduced in the circuit, because we did not define the complete set of outputs in the conditions, e.g. we did no specify the value of ‘small’ when ‘a > b’ at Line 17. Therefore, a latch is created to store the previous value of ‘small’ for this condition.

Listing 5.5 Unintentional latch
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- latchEx.vhd

library ieee; 
use ieee.std_logic_1164.all;

entity latchEx is 
port(
    a, b : in std_logic;
    large, small : out std_logic
);
end entity;

architecture arch of latchEx is
begin 
    process(a, b)
    begin 
        if (a > b) then 
            large <= a;
        elsif (a < b) then 
            small <= a;
        end if;
    end process;
end arch; 
../_images/latchEx.jpg

Fig. 5.7 Latch generated by Listing 5.5

Note

The latches can be removed by following two simple rules which are applied in Listing 5.6 and corresponding design in shown in Fig. 5.8,

  • Include all the input signals related to combinational designs in the sensitivity list of process statement.
  • Always define the ‘else’ block in ‘if statement’ and ‘others’ block in ‘case’ statement.
  • Assign all outputs inside every block of statements e.g define all outputs inside every ‘elsif’ block of ‘if-else’ statement and inside every ‘when’ block of ‘case’ statement etc.
Listing 5.6 Remove unintentional latch
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- latchRemoveEx.vhd

library ieee; 
use ieee.std_logic_1164.all;

entity latchRemoveEx is 
port(
    a, b : in std_logic;
    large, small : out std_logic
);
end entity;

architecture arch of latchRemoveEx is
begin 
    process(a, b)
    begin 
        -- define all outputs in each if-elsif statement
        -- also include the `else' statement
        if (a > b) then 
            large <= a;
            small <= b;
        elsif (a < b) then 
            large <= b;
            small <= a;     
        else 
            large <= '0';
            small <= '0';
        end if;
    end process;
end arch; 
../_images/latchRemoveEx.jpg

Fig. 5.8 Latch removed by Listing 5.6

Another method remove the unintended memories is the use of ‘default values’ as shown in Listing 5.7. Here we can defined, all the outputs at the beginning of the process statement (Lines 18-19). Then, we can overwrite the values in different statements (i.e. using multiple assignments) e.g. value of ‘large’ is modified in Line 21. The design generated by the listing is shown in Fig. 5.9.

Note

Note that, ‘multiple assignments’ are error-prone as discussed in Listing 5.1. It’s use should be limited to ‘default-value-assignments’ only. Do not use it for any other purposes.

../_images/latchRemoveEx2.jpg

Fig. 5.9 Latch removed by default-value-assignments in Listing 5.7

Listing 5.7 Remove unintentional latch using default values
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- latchRemoveEx2.vhd

library ieee; 
use ieee.std_logic_1164.all;

entity latchRemoveEx2 is 
port(
    a, b : in std_logic;
    large, small : out std_logic
);
end entity;

architecture arch of latchRemoveEx2 is
begin 
    process(a, b)
    begin 
        -- use default values : no need to define output in each if-elsif-else part
        large <= '0';
        small <= '0';
        if (a > b) then 
            large <= a;
        elsif (a < b) then 
            small <= a;
        end if;
    end process;
end arch; 

5.9. Code-size vs design-size

Note that, in VHDL/Verilog designs the code-size has nothing to do with the design size. Remember, the well defined design (code may be very lengthy) can be easily understood by the synthesizer and will be implemented using less number of components.

../_images/squareWave.jpg

Fig. 5.10 Generated square wave by Listing 5.8 and Listing 5.8 : On-time = 2, Off-time = 4

Let’s understand this with an example. In Listing 5.8 and Listing 5.9, an square wave generator is implemented whose on/off duration is programmable as shown in Fig. 5.10. In Listing 5.8, the whole code is written in one process block, therefore the code is smaller as compare to Listing 5.9 where FSM approach is used to design the same circuit. The designs generated by these methods are shown in Fig. 5.11 and Fig. 5.12 respectively. In these figures, we can observe that, the code is larger for the FSM approach (Listing 5.9) but the resultant design is smaller than the first approach (Listing 5.8).

../_images/square_wave.jpg

Fig. 5.11 Design generated by Listing 5.8

../_images/square_wave2.jpg

Fig. 5.12 Design generated by Listing 5.9

Note

Remember, code-size and design-size are independent of each other. The well defined designs are implemented with less number of components.

Listing 5.8 Code is small but design is large (see Fig. 5.11)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-- square_wave.vhd

-- High : on_duration * time_scale
-- Low  : off_duration * time_scale

library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; 

entity square_wave is
port(
    clk, reset : in std_logic; 
    on_duration, off_duration : in unsigned(2 downto 0);
    out_wave : out std_logic
);
end entity; 

architecture arch of square_wave is
    signal count_on, count_off : unsigned(2 downto 0);
    constant time_scale : unsigned(7 downto 0) := unsigned(to_signed(2, 8));
begin
    process(clk, reset)
    begin
        if reset = '1' then 
            out_wave <= '0';
            count_on <= (others => '0');
            count_off <= (others => '0');
        elsif rising_edge(clk) then
            if count_on < on_duration * time_scale then
                out_wave <= '1';
                count_on <= count_on + 1;
            elsif count_off < off_duration * time_scale - 1 then
                out_wave <= '0';
                count_off <= count_off + 1;
            else
                count_on <= (others => '0');
                count_off <= (others => '0');
            end if;
        end if;
    end process; 
end arch; 

                
Listing 5.9 code is lengthy but design is smaller (see Fig. 5.12
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
-- square_wave2.vhd

-- High : on_duration * time_scale
-- Low  : off_duration * time_scale

library ieee; 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; 

entity square_wave2 is
port(
    clk, reset : in std_logic; 
    on_duration, off_duration : in unsigned(2 downto 0);
    out_wave : out std_logic
);
end entity; 

architecture arch of square_wave2 is
    signal count_on_reg, count_on_next, count_off_reg, count_off_next : unsigned(2 downto 0);
    constant time_scale : unsigned(7 downto 0) := unsigned(to_signed(2, 8));
begin
    process(clk, reset)
    begin
        if reset = '1' then 
            count_on_reg <= (others => '0');
            count_off_reg <= (others => '0');
        elsif rising_edge(clk) then
            count_on_reg <= count_on_next;
            count_off_reg <= count_off_next;
        end if;
    end process; 
    
    process(count_on_reg, count_off_reg)
    begin
        count_on_next <= (others => '0');
        count_off_next <= (others => '0');
        out_wave <= '0';
        if count_on_reg < on_duration * time_scale then
            out_wave <= '1';
            count_on_next <= count_on_reg + 1;
            count_off_next <= count_off_reg;
        elsif count_off_reg < off_duration * time_scale - 1 then
            out_wave <= '0';
            count_on_next <= count_on_reg;
            count_off_next <= count_off_reg + 1;
        end if;
    end process; 
end arch; 

                

5.10. Conclusion

In this chapter, various statements of behavioral modeling styles are discussed. Also, we saw the relationship between the designs generated by behavior modeling and dataflow modeling. Further, problem with loops are discussed and finally loop is implemented using ‘if’ statement.