← All guides

Procedures vs functions in VHDL

VHDL has two kinds of subprogram, and the language reference explains them in terms of expressions and statements. The hardware view is simpler and more useful: a function is a named piece of combinational logic with exactly one output. A procedure is a named block of statements that can drive as many outputs as you give it. Everything else about them follows from that.

Functions: one result, usable anywhere an expression goes

A function takes inputs (only inputs), returns exactly one value, and can be called inside any expression: an assignment, a condition, a port map. Here’s a three-input majority vote, live. Note what you’re looking at: this is the whole source, no entity, no architecture, and the ports come from the function’s own signature:

function majority (a, b, c : std_logic) return std_logic is
begin
  return (a and b) or (b and c) or (a and c);
end function;

Interactive schematic by RapidRTL

The schematic is the point: a function is that cloud of gates. Calling majority(x, y, z) in three places doesn’t create a “call” in hardware, because hardware has no call stack; each call site gets its own copy of the logic (which the optimizer may then share if the inputs match). Functions are how VHDL names a computation: type conversions, reductions, decoders, address calculations. If you’ve written the same expression twice, it wants to be a function.

Two rules keep functions honest:

Procedures: many outputs, called as a statement

A procedure call is a statement, like an assignment, and its parameters have directions. That makes it the natural home for an operation with more than one result. The classic small example: sort two values, low and high out, in one call.

Interactive schematic by RapidRTL

One comparator, two muxes: again, the procedure dissolved into the logic it described. Nothing about “being a procedure” survives synthesis; it’s an organizational tool for you, not a structure the hardware keeps. A min_max used by an 8-value sorting network would be written once and instantiated as gates seven times.

The parameter-class trap

Procedure parameters have a direction (in, out, inout) and also a class: constant, variable, or signal. The class must match what you pass. The one that bites: to assign a signal (like a port) from inside a procedure, the formal parameter must be declared signal ... : out, as in the example above. Leave the class off an out parameter and it defaults to variable, at which point passing a port into it is an error that reads like the compiler is being obtuse. It isn’t; it’s asking whether you want variable or signal assignment semantics inside the body, and those genuinely differ.

Choosing between them

The Verilog aside

Verilog’s split is the same idea with different names: a Verilog function is likewise a single-return combinational calculation, and a task is the procedure analogue, multiple outputs allowed, timing controls allowed in simulation, the same “no wait reaches hardware” rule in practice. If you’ve internalized one language’s pair, you already understand the other’s.

The pitfalls checklist