Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Industrial programming >> VHDL

Using Procedures in VHDL: Simplify Your Design with Reusable Code

In VHDL, procedures are subprograms that let designers avoid repetitive code blocks. When the same operation is needed in multiple places, creating a full module can be excessive. A procedure offers a lightweight, reusable solution.

Procedures may be declared in any declarative region—architecture, package, or process—and their scope is limited to that region. When invoked, the procedure’s body is executed just as if its code were written in place, giving you the benefits of modularity without the overhead of a separate component.

Unlike functions, procedures do not return a value directly. However, you can pass values back by declaring out or inout signals in the parameter list, effectively allowing the procedure to act like a small module with inputs and outputs.

This post is part of the Basic VHDL Tutorials series.

The canonical syntax for defining a procedure is illustrated below:

procedure <procedure_name> (signal|variable|constant <name1> : in|out|inout <type>;
    signal|variable|constant <name2> : in|out|inout <type>;
    ... ) is
    <declarations_for_use_within_the_procedure>
begin
    <code_performed_by_the_procedure_here>
end procedure;

The parameter list defines the procedure’s inputs and outputs—much like a mini‑module. Parameters may be signals, variables, or constants. Inside the procedure you can declare additional objects (constants, variables, types, subtypes, aliases) that are valid only within that block; signals cannot be declared locally.

Because procedures can contain wait statements, they are especially useful in testbenches—for instance, as simple BFM drivers or for checking DUT outputs.

Exercise

In the preceding tutorial, a timer module was built using deeply nested if‑then‑else statements. Each nested level added complexity and reduced readability, even though the same operation was performed on different signal sets. A better approach is to encapsulate the repetitive logic within a procedure.

The following video tutorial walks through creating a VHDL procedure:

Example: Procedure‑Based Timer

The testbench code remains unchanged, except for the addition of a procedure call to drive the DUT:

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

    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

    i_Timer : entity work.T19_Timer(rtl)
        generic map(ClockFrequencyHz => ClockFrequencyHz)
        port map (
            Clk     => Clk,
            nRst    => nRst,
            Seconds => Seconds,
            Minutes => Minutes,
            Hours   => Hours);

    Clk <= not Clk after ClockPeriod / 2;

    process is
    begin
        wait until rising_edge(Clk);
        wait until rising_edge(Clk);

        nRst <= '1';

        wait;
    end process;

end architecture;

The timer module itself now leverages a procedure for counter incrementing:

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 Ticks : integer;

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

begin

    process(Clk) is
        variable Wrap : boolean;
    begin
        if rising_edge(Clk) then
            if nRst = '0' then
                Ticks   <= 0;
                Seconds <= 0;
                Minutes <= 0;
                Hours   <= 0;
            else
                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 below confirms that the minute counter wraps correctly when the timer reaches 60 minutes:

Using Procedures in VHDL: Simplify Your Design with Reusable Code

Analysis

From the waveform we see that the wrap functionality behaves identically to the original nested‑if implementation. The change is purely structural—no change to the underlying logic.

The first parameter, Counter, is an inout signal, allowing the procedure to read and update its value. The second and third parameters, WrapValue and Enable, are constants; they are treated as compile‑time constants inside the procedure.

The final parameter, Wrapped, is a variable declared as out. It serves as a pseudo return value, indicating whether the counter wrapped during that call.

In the main process, four successive calls to IncrementWrap cascade the counters. The Wrap variable from the previous call feeds the next call’s Enable input. Using a variable is essential here because signal updates are only visible after the process suspends; a variable provides immediate, procedural data flow.

Using Procedures in VHDL: Simplify Your Design with Reusable Code

Takeaway

Proceed to the next tutorial »

VHDL

  1. Leveraging In‑Process Procedures for Cleaner VHDL FSM Design
  2. Using Impure Functions in VHDL: Enhancing FSM Readability and Maintainability
  3. Mastering VHDL Functions: A Practical Guide to Efficient Design
  4. Mastering VHDL Port Map Instantiation: A Practical Guide
  5. Mastering the Case-When Statement in VHDL: Efficient Multiplexer Design
  6. Mastering Signed and Unsigned Types in VHDL: A Practical Guide
  7. Mastering VHDL Wait Statements: Wait On, Wait Until, and Combined Usage
  8. Mastering While Loops in VHDL: Dynamic Iteration Control
  9. Mastering For‑Loops in VHDL: A Practical Guide
  10. Mastering Loop and Exit Constructs in VHDL: A Practical Guide