Skip to content

VHDL · Chapter 11.3 · Functions and Procedures

Procedures

Where a function returns one value inside an expression, a procedure is called as a statement and can return several results through its parameters. Its parameters carry modes, in for inputs, out for results, and inout for read-modify-write, so one call can produce a sum and a carry, or an encoded value and a valid flag. In synthesizable code a procedure simply packages multi-output logic that inlines at the call site, combinational when called in a combinational context and registered when called inside a clocked process. Procedures also shine in testbenches, where a non-synthesizable procedure with a wait can wrap a whole bus-write or reset sequence into one reusable call. This lesson covers writing procedures, how they synthesize, and their testbench role.

Foundation14 min readVHDLProceduresSubprogramsTestbenchReuseSynthesis

1. Engineering intuition — one call, several results (or a sequence)

A function fits when exactly one value comes out. Many useful operations are not like that: an adder produces a sum and a carry; a priority encoder produces an index and a valid bit; a transaction performs a sequence of steps. A procedure is built for these — it is called as a statement and hands results back through its out parameters, as many as you need, and it can perform an ordered series of actions. In synthesizable RTL that means packaging a multi-output computation under one name; in a testbench it means packaging a whole protocol sequence (drive address, assert valid, wait, deassert) into a single readable call.

2. Formal explanation — anatomy of a procedure

procedure_anatomy.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Called as a STATEMENT. Parameters carry MODES: in (input), out (result), inout (read+write).
-- Several outputs are normal. (Synthesizable procedures contain no wait.)
procedure add_carry (a, b   : in  unsigned(7 downto 0);   -- inputs
                     sum     : out unsigned(7 downto 0);   -- result 1
                     carry   : out std_logic) is           -- result 2
  variable t : unsigned(8 downto 0);
begin
  t     := ('0' & a) + ('0' & b);
  sum   := t(7 downto 0);     -- writing variable 'out' params
  carry := t(8);
end procedure;
 
-- TESTBENCH procedure (NOT synthesizable) — packages a sequence using wait.
procedure bus_write (signal clk  : in  std_logic;
                     signal addr : out std_logic_vector;
                     signal wr   : out std_logic;
                     constant a  : in  std_logic_vector) is
begin
  wait until rising_edge(clk);
  addr <= a;  wr <= '1';
  wait until rising_edge(clk);
  wr   <= '0';                 -- a whole write transaction in one call
end procedure;

A procedure has parameters with modes in/out/inout, a body of sequential statements, and no return value (results flow through out/inout). Synthesizable procedures avoid wait and inline as logic; testbench procedures may use wait to package timed sequences.

3. Production usage — multi-output logic and testbench sequences

procedures_in_use.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- SYNTHESIZABLE: a multi-output operation, called inside a clocked process → registered results.
process (clk) begin
  if rising_edge(clk) then
    add_carry(x, y, sum_r, carry_r);   -- one call, two registered outputs
  end if;
end process;
 
-- TESTBENCH: a reusable transaction sequence, called from a stimulus process.
stim : process begin
  bus_write(clk, addr, wr, x"10");     -- a full write, readable as one line
  bus_write(clk, addr, wr, x"14");
  wait;
end process;

What hardware does this become? The synthesizable add_carry inlines at its call site as an adder whose two outputs (sum, carry) drive the two targets; because the call sits in a clocked process, those targets are registered. Called in a combinational context instead, the same procedure is combinational. The testbench bus_write is not hardware — it is simulation control, its wait statements sequencing stimulus over clock edges. Same construct, two roles: package logic, or package a timed sequence.

4. Structural interpretation — several outputs from one call

procedure with two inputs producing two outputs through out parameters at a call siteoutoutin parametersa, b (inputs)procedure bodysequential statementsout: sumresult 1out: carryresult 212
A procedure is called as a statement and produces several outputs through its parameters. add_carry takes inputs a and b and writes two results — sum and carry — through its out parameters; in synthesizable code it inlines at the call site as an adder, registered if the call sits in a clocked process and combinational otherwise. Parameter modes (in, out, inout) define the direction of each connection. A non-synthesizable testbench procedure instead packages a timed sequence using wait. Because the synthesizable form is just inlined logic, a structural diagram, not a waveform, captures it.

5. Why this is structural, not timing

A synthesizable procedure inlines into the logic at its call site — an adder, an encoder, whatever its body computes — so the right picture is the multi-output structure above; its run-time behaviour is simply that of the logic it expands to (combinational, or registered when the call is in a clocked process), already seen in earlier modules. The genuinely timed form is the testbench procedure with wait, but there the waveform of interest is the stimulus it produces (a bus transaction), which belongs to the verification context rather than to the procedure construct itself. The construct's essence is structural reuse of multi-output behaviour.

6. Debugging example — reading an out parameter (or wait in synthesizable code)

Expected: a procedure returns results through its out parameters. Observed: a compile error that an out parameter cannot be read, or a synthesizable procedure rejected for containing wait. Root cause: either the body tried to read an out parameter (it is write-only; to both read and write a parameter it must be inout), or a wait was placed in a procedure meant for synthesis (legal only in simulation/testbench procedures). Fix: use inout for a parameter that is read and written; remove wait from synthesizable procedures (use a clocked process for sequencing). Engineering takeaway: out is write-only and inout is read-write; wait belongs only in non-synthesizable testbench procedures.

out_vs_inout.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: reading an 'out' parameter.
-- procedure p (x : out integer) is begin x := x + 1; end procedure;  -- can't read 'out' x
-- FIX: use 'inout' to read and write the same parameter.
procedure p (x : inout integer) is begin x := x + 1; end procedure;

7. Common mistakes & what to watch for

  • Reading an out parameter. out is write-only; use inout to both read and write a parameter.
  • wait in synthesizable procedures. Legal only in testbench procedures; for hardware sequencing use a clocked process.
  • Expecting a return value. Procedures return through parameters, not return with a value; return (no value) only exits early.
  • Forgetting the call context sets timing. A synthesizable procedure is combinational or registered depending on where it is called (combinational vs clocked process).
  • Over-stuffing one procedure. Keep a procedure to one coherent operation; split unrelated work, just as you would partition logic.

8. Engineering insight & continuity

A procedure is the multi-output, statement-form subprogram: in/out/inout parameters let one call return several results or, in a testbench, package a whole timed sequence behind wait. In synthesizable code it inlines as logic (combinational or registered by context); in verification it makes stimulus read like a protocol. With functions and procedures both covered, the natural next question is the parameters themselves — the modes, classes, and how arguments associate — which is exactly the next lesson, Subprogram Parameters and Modes.