A procedure is a type of subprogram in VHDL which can help us avoid repeating code. Sometimes the need arises to perform identical operations several places throughout the design. While creating a module might be overkill for minor operations, a procedure is often what you want.

Procedures can be declared within any declarative region. The scope of the procedure will be limited to wherever it’s declared, architecture, package, or process. Whenever you call the procedure, it will behave like the code of the procedure was inserted where it was called from.

A procedure doesn’t return a value like a function does, but you can return values by declaring out or inout signals in the parameter list.

This blog post is part of the Basic VHDL Tutorials series.

The basic syntax for creating a procedure is:
procedure <procedure_name> (signal|variable|constant <name1> : in|out|inout <type>;
                            signal|variable|constant <name2> : in|out|inout <type>;
                            ... ) is
    <signal_constant_or_variable_declarations_for_use_within_the_procedure>
begin
    <code_performed_by_the_procedure_here>
end procedure;

A procedure’s parameter list defines its inputs and outputs, kind of like a mini-module. It can be a signal or a constant, but unlike a module, it can also be a variable. The procedure can also declare its own signals, constants, or variables. These are only valid inside of the procedure.

Unlike functions, procedures may contain wait-statements. Therefore, they are often used in testbenches like simple BFM’s for simulating interfaces, or for checking output from the device under test (DUT).

Exercise

In the previous tutorial we created a timer module using nested If-Then-Else statements. Each level of If-Then-Else inside of another If-Then-Else adds complexity to the design, and it becomes less readable. On each level of logic we are basically doing the same operation on a different set of signals. Isn’t there a better way to do this?

In this video tutorial we will learn how to create a procedure in VHDL:

The final code for the procedure testbench:

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

entity T19_ProcedureTb is
end entity;

architecture sim of T19_ProcedureTb is

    -- We're slowing down the clock to speed up simulation time
    constant ClockFrequencyHz : integer := 10; -- 10 Hz
    constant ClockPeriod : time := 1000 ms / ClockFrequencyHz;

    signal Clk     : std_logic := '1';
    signal nRst    : std_logic := '0';
    signal Seconds : integer;
    signal Minutes : integer;
    signal Hours   : integer;

begin

    -- The Device Under Test (DUT)
    i_Timer : entity work.T19_Timer(rtl)
    generic map(ClockFrequencyHz => ClockFrequencyHz)
    port map (
        Clk     => Clk,
        nRst    => nRst,
        Seconds => Seconds,
        Minutes => Minutes,
        Hours   => Hours);

    -- Process for generating clock
    Clk <= not Clk after ClockPeriod / 2;

    -- Testbench sequence
    process is
    begin
        wait until rising_edge(Clk);
        wait until rising_edge(Clk);

        -- Take the DUT out of reset
        nRst <= '1';

        wait;
    end process;

end architecture;

The final code for the timer module using a procedure:

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

entity T19_Timer is
generic(ClockFrequencyHz : integer);
port(
    Clk     : in std_logic;
    nRst    : in std_logic; -- Negative reset
    Seconds : inout integer;
    Minutes : inout integer;
    Hours   : inout integer);
end entity;

architecture rtl of T19_Timer is

    -- Signal for counting clock periods
    signal Ticks : integer;

    procedure IncrementWrap(signal   Counter   : inout integer;
                            constant WrapValue : in    integer;
                            constant Enable    : in    boolean;
                            variable Wrapped   : out   boolean) is
    begin
        if Enable then
            if Counter = WrapValue - 1 then
                Wrapped := true;
                Counter <= 0;
            else
                Wrapped := false;
                Counter <= Counter + 1;
            end if;
        end if;
    end procedure;

begin

    process(Clk) is
        variable Wrap : boolean;
    begin
        if rising_edge(Clk) then

            -- If the negative reset signal is active
            if nRst = '0' then
                Ticks   <= 0;
                Seconds <= 0;
                Minutes <= 0;
                Hours   <= 0;
            else

                -- Cascade counters
                IncrementWrap(Ticks, ClockFrequencyHz, true, Wrap);
                IncrementWrap(Seconds,             60, Wrap, Wrap);
                IncrementWrap(Minutes,             60, Wrap, Wrap);
                IncrementWrap(Hours,               24, Wrap, Wrap);

            end if;
        end if;
    end process;

end architecture;

The waveform window in ModelSim, zoomed in on the timeline where the Minutes signal is wrapping:
procedure_counter

Need the ModelSim project files?

Let me send you a Zip with everything you need to get started in 30 seconds

How does it work?

Tested on Windows and Linux How it works

    Unsubscribe at any time

    Analysis

    We can see from the waveform that the wrapping of signals still work as it did in the previous tutorial. That’s because we haven’t actually changed the function on the module, only the way it’s implemented.

    The first item on the parameter list for the IncrementWrap procedure is the Counter signal. It’s declared using direction inout for the procedure to be able to both read and set its value.

    The second and third items on the parameter list are constants. This means that the values you put in here will appear as constants inside of the procedure. The WrapValue input together with the Enable input determines if the Counter signal is incremented or wrapped.

    The last item on the parameter list is a variable with direction out. The purpose of this output is to inform the caller of the procedure that the counter wrapped. We use it here kind of like a return value.

    In the main process we have four calls to the IncrementWrap procedure. Each of the subsequent calls use the Wrap variable to enable the counting. It wouldn’t have worked if we had used a signal instead of a variable, because signal values are only updated when a process goes to sleep. We need the output value from one procedure call to be be used as an input to a call on the very next line. Thus, it has to be a variable.

    Get exclusive access to exercises and answers!

    Takeaway

    • Procedures can be used as mini-modules to avoid copy-pasting code
    • Paramenters (inputs/outputs) to a procedure can be signals, variables, or constants
    • Unlike functions, procedures can contain wait-statements

    Go to the next tutorial »

    Leave a Reply

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

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    5 thoughts on “How to use a Procedure in VHDL

    1. @Jonas
      Thank you for the tutorial.
      Why do you need to double the `wait until rising_edge(Clk);` line in the test bench file?

      Posted on October 6, 2018 at 7:42 am
      1. Hi Kana,

        Thank you for your curiosity.

        I added a double rising_edge(Clk) in the testbench sequence to make the reset active for two full clock periods.

        Here, I have zoomed in on the beginning of the waveform using the double rising_edge(Clk):

        First, the signals have default integer values. Then, at the first rising edge of the clock, the reset logic changes the values to 0. Finally, at the second rising edge of the clock, the reset signal is released.

        Now consider the waveform where I have changed it to a single rising_edge(Clk):

        It still works. The rising edge of the clock triggers two things within the same timestep (delta cycle). The reset is released and at the same time the reset logic kicks in. The reset logic samples nRst which is still ‘0’ at the time. Therefore, the integer signals will appear to change at the same time as the reset is released.

        I thought this might cause a bit of confusion, so I added another clock period to avoid the issue. Thereby adding another element of confusion 🙂

        I should add that the reset signal is normally held for a number of clock period, not only one or two. This is known as a reset strobe.

        Posted on October 6, 2018 at 2:25 pm
        1. Thanks Jonas, that’s very clear now, and I’ll check what strobes are used for.

          Posted on October 7, 2018 at 4:12 pm
    2. Loved the tutorial series.. Do you have any plans to make advanced vhdl tutorial series? It will be helpful if you make a video on modelsim… Thanks again..

      Posted on October 18, 2018 at 6:19 am
      1. Glad you enjoyed the tutorials!

        I am planning an intermediate VHDL course using FPGA development boards. I have also been thinking about teaching advanced testbench strategies. But a video only about ModelSim sounds like a good idea too. Thank you for the tip.

        Jonas

        Posted on October 18, 2018 at 11:43 am