We've seen examples of how sequential statements are written in a process statement. The process statement is relatively easy to understand if you think of it as a small software program that executes independent of other processes and concurrent statements during simulation.

 

Functions and procedures (which are collectively called subprograms) are very similar to processes in that they contain sequential statements executed as independent 'programs' during simulation. The parameters you pass into a subprogram are analogous to the sensitivity list of a process; whenever there is an event on any object (signal or variable) being passed as an argument to a subprogram, that subprogram is executed and its outputs (whether they are output parameters, in the case of a procedure, or the return value, in the case of a function) are recalculated.

 

The following example includes a procedure declared within the architecture. The procedure counts the number of ones and zeroes there are in a std_logic_vector input (of arbitrary width) and returns the count values as output parameters. The procedure is used to build two result strings containing the appropriate number of ones and zeroes, left justified and padded with 'X' values. (For example, an input with the values "1010001001" would result in the values "1111XXXXXX" and "000000XXXX".)

 

entity proc is

    port (Clk: in std_logic;

             Rst: in std_logic;

             InVector: in std_logic_vector(0 to 9);

             OutOnes: out std_logic_vector(0 to 9);

             OutZeroes: out std_logic_vector(0 to 9));

end proc;

 

architecture behavior of proc is

 

    procedure CountBits(InVector: in std_logic_vector;

                        ones,zeroes: out natural) is

        variable cnt1: natural := 0;

        variable cnt0: natural := 0;

    begin

        for I in 0 to InVector'right loop

            case InVector(I) is

                when '1' => cnt1 := cnt1 + 1;

                when '0' => cnt0 := cnt0 + 1;

                when others => null;

            end case;

        end loop;

        ones := cnt1;

        zeroes := cnt0;

    end CountBits;

 

    signal Tmp1, Tmp0: std_logic_vector(0 to 9);

begin

 

    process(Rst, Clk)

    begin

        if Rst = '1' then

            OutOnes <= (others => '0');

            OutZeroes <= (others => '0');

        elsif rising_edge(Clk) then

            OutOnes <= Tmp1;

            OutZeroes <= Tmp0;

        end if;

    end process;

 

    process(InVector)

        variable ones, zeroes: natural;

    begin

        Countbits(InVector,ones,zeroes);

        Tmp0 <= (others => 'X');

        Tmp1 <= (others => 'X');

        for I in 0 to ones - 1 loop

            Tmp1(I) <= '1';

        end loop;

        for I in 0 to zeroes - 1 loop

            Tmp0(I) <= '0';

        end loop;

    end process;

 

end behavior;

 

This example shows that a procedure containing sequential statements can be invoked from within a process—or even from within another procedure. The calling process simply suspends until the procedure has completed execution.

 

Note: This example is theoretically synthesizable, but the fact that the procedure has been written without regard to the width of the inputs will probably make it impossible to process by synthesis tools. If this design were to be synthesized, the variables cnt1 and cnt0 would have to be given range constraints.