VHDL functions, procedures, packages and libraries

prepared by P. Bakowski


This chapter  introduces the programming constructs called subprograms and packages.
VHDL provides basic functional blocks in the form of programming modules (sub-programs) called   procedures and functions [syntax]. The modules called packages are used to collect  declarations of types, subtypes, functions and procedures into modular units that can be used in several designs.
In general, the declaration a sub-program can be done in a package, an entity, an architecture, a process, a procedure or a function. For functions, the declaration also specifies the type of the result returned when the function is called.
In this chapter we consider only constant and variable parameters of subprograms, although signals can also be used .

Contents: procedure declaration , function declaration, subprogram call, subprogram bodies, overloading, packages, standard packages, libraries, exercises

Procedures and Functions

Procedures and functions are essential functional modules provided to describe the algorithms. The algorithms having multiple results are described as procedures; the algorithms having only one result are usually described with functions.

Procedure declaration [syntax]

Below a simple example of a procedure with no parameters:

procedure alarm;

This simply defines alarm as a procedure with no parameters, whose statement body will be given subsequently in the VHDL program.

A procedure call to alarm would be:


The following is a declaration of a procedure with two parameters:

procedure count_256( variable init: in integer; variable compteur:out word_32 )

When the procedure is called, the actual parameter associated with compteur must itself be a variable. The operation mode of init is in , which means that init can be read but not assigned..

Other possible modes for procedure parameters are:

The default mode is specified of a  parameter is in. If no class is specified (variable, constant,..) , parameters of mode in are interpreted as being of class constant and parameters of mode out and inout are interpreted as being of class variable.

Function declaration [syntax]

A function is a subprogram which returns one value.  Formal parameters mode must be in , and this is assumed if not explicitly specified. Default class is assumed to be constant .

function increment(val: bit_vector(7 downto 0)) return bit_vector(7 downto 0);

Note that the value returned by the body of this function must be a bit_vector(7 downto 0).

Procedure and function body [syntax]

As in any programming language the declared  items presented after the subprogram header are to be used locally within the subprogram body. The names of these items are visible only  inside locally declared subprograms. When the subprogram is called, the statements in the body are executed until either the last statement in the program order is encountered, or a return statement is executed.

The  return statement used within a function  carries a return value. This value may be ready in a variable or it can be evaluated by an expression related to return instruction.

return [ expression ] ;

Note taht the  return statement used within a procedure has no return value.

Few examples

subtype word is bit_vector(7 downto 0);
function increment(val: word) return word is
variable tmp: word;
for i in val'low to val'high loop
if tmp(i)='0' then
else a(i):='0';
end if;
end loop;
return tmp;
end increment;
procedure message is
assert false
report "bonjour"
severity note;
end procedure message;
procedure color_mix (signal c1,c2: in prime_color; signal mix: out secondary_color) is
if (c1=rouge and c2=jaune) then mix:= orange;
(c1=jeune and c2=blue) then mix:= vert;
(c1=rouge and c2=blue) then mix:= violet;
end if;
end procedure color_mix;

Subprogram call

A call to a subprogram includes a list of actual parameters to be mapped onto the formal parameters. Two kinds of mapping are possible:

positional association

This association list can be position based; it lists the actual parameters in the same order as the formal parameters.

count_32(init_value, registre);

 named association : formal parameter  => actual parameter

A subprogram call with named association explicitly gives the formal parameter name to be associated with each actual parameter ; the parameters can be in any order.

count_32(init => init_value,compteur => registre);
count_32(compteur => registre,init => init_value);


A given subprogram designator (name) can be used in several subprogram specifications, provided the number or base types of parameters differs. The subprogram name is then said to be overloaded. When a subprogram call is made using an overloaded name, the choice of one of the overloaded specifications depends on the number of actual parameters their base types , their order and the corresponding formal parameter names. This means that the selection of the overloaded subprogram is related not only to the name of the subprogram but also to its parameters.
The following are two specifications of a function called compare; depending on the parameters used with the name compare we can select the first or the second version of this function.

subtype word is bit_vector(7 downto 0);
-- specifications
(1) function compare( val1 : integer; val2 : integer ) return boolean;
(2) function compare( val1 : word ;val2 : word) return boolean;
-- calls
res = compare( "01010111"; "01110111"); -- second function compare called
res = compare( 76473; 874837);-- first function compare called

It is important that the operator symbols (+, -, *, & , ...)  may be used as names of the functions. It allows a variety of functions to be defined for these operators. For example, the addition operator (+) is often overloaded in order to perform the addition of  bit_vector  operands.

subtype word is bit_vector(7 downto 0);
function "+" ( a, b : word) return word is  -- creating new + operator operating on bit_vector type
return int_to_word( word_to_int(a) + word_to_int(b));  -- the use of "standard" + operator
end "+";

Note: The "standard" addition operator (+) is defined originally to be used only with  integers.

Packages [syntax]

A package is a collection of logically related declarations. They typically are used to contain sets  of   type  subtypes, constants declarations, global signal declarations, and global variables (VHDL'93). The packages may contain component declarations, attribute declarations and specifications and subprograms. The subprograms declared in packages provide information hiding; they can be called from outside the package but the body of the subprograms remains hidden and protected from users.

Normally a package is defined in two parts:

The body part may be omitted if there are no subprogram  implementations or deferred constants.

package simple is
constant cdiff: integer;  -- deferred constant
subtype word is bit_vector(15 downto 0);
subtype address is bit_vector (24 downto 0);
type mem is array (0 to 31) of word;
function address2int (val : address) return integer;
function increment_word(val : word) return word;
end simple;

In the example above, the bodies of the two functions address2int and increment_word are not specified , so a package body is needed.
Note the deferred constant cdiff  is initialized in the package body; its value may be changed by re-analyzing only the package body.

The package body can be written as:

package body simple is
constant cdiff: integer:= 500;  -- deferred constant initialized
address_to_int(value : address) return integer is
-- the definition of the function
end address2int;
function increment_word( val : word) return word is
-- the definition of the function
end increment_word;
end simple;

Note that the subtype declarations are not repeated, they are visible from the package declarations block.

use clause

Items declared within a package become accessible by direct selection:

variable pc: simple.address; -- simple is the package name
or by the use clause:
use work.simple.address;  -- work is the name of working library where the package is compiled
variable pc: address;     -- direct visibility

If all of the declared names in a package are to be used in this way, you can indicate it through special suffix all , for example:

use work.simple.all ;
variable pc: address;
variable ram: mem;

Standard packages

There are two predefined packages provided with VHDL:

package standard is
type severity_level is ( note, warning, error, failure);
type boolean is ( false, true);
type bit is ( `0', `1')
type character is (
NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF, VT, FF, CR, SO, SI, DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FSP, GSP, RSP, USP, ` `, `!', `"', `#', `$', `%', `&', `'', `\ `, `*', `+', `,', `-', `.', `/', `0', `1', `2', `3', `4', `5', `6', `7', `8', `9', `:', `;', `<`, `=', `>', `?', `@', `A', `B', `C', `D', `E', `F', `G', `H', `I', `J', `K', `L', `M', `N', `O', `P', `Q', `R', `S', `T', `U', `V', `W', `X', `Y', `Z', `[`, `\\', `]', `^', `_', ``', `a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k', `l', `m', `n', `o', `p', `q', `r', `s', `t', `u', `v', `w', `x', `y', `z', `{`, `|', `}', `~', DEL);
type time is range implementation_defined units fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units ;
function now return time; -- returns the current simulation time
subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;
type bit_vector is array (natural range <>) of bit;
type string is array (positive range <>) of character;
end standard;
package textio is
type line is access string; -- a line is a pointer to a string value
type text is file of string; -- a file of variable-length ASCII records
type side is (right, left); -- for justifying output data within fields
subtype width is natural; -- for specifying widths of output fields
-- standard text files
file input: text is in "STD_INPUT";
file utput: text is out "STD_OUTPUT";
-- input procedures for standard types
procedure readline(f:in text; l:out line);
procedure read(:inout line; value:out le_type;good: out boolean);
procedure read(:inout line; value:out le_type);
-- for le_type in: bit, bit_vector, boolean, character, integer, real, string, time
procedure writeline(f:out text; l:in line);
procedure write(l:inout line; value:in le_type;justified:in side:=right; field:in width:=0);
-- for le_type in: bit, bit_vector, boolean, character, integer, string
procedure write(l:inout line; value:in real;justified:in side:=right; field:in width:=0; digits: in natural:=0);
procedure write(l:inout line; value:in time;justified:in side:=right; field:in width:=0; unit: in time:=ns);
-- file position predicates
function endline (l:in line) return boolean;
function endfile (f:in text) return boolean;
end textio;


All VHDL compilers store the compiled designs in design libraries. The standard package is already compiled into std library and used implicitly by VHDL compiler. Other VHDL  files must be explicitly compiled into design libraries before the use by simulator.

Library named work is used when no explicit user designed library is provided.

use work.all;
entity using_work is

For comprehensive designs the compiled vhdl modules should be placed in design libraries other than work. The library name should reflect the nature of the compiled components (e.g. CMOS_logic).

library CMOS_logic;
use work.CMOS_logic.package_gates.all;
entity CMOS_gates is

One of the well known libraries is IEEE standard logic library. If the compiled vhdl module requires standard logic , IEEE library must be included into the compilation process.

library IEEE;
use IEEE.std_logic_1164.all;
entity using_std_logic is

The synthesis tools use standard numeric packages. These numeric packages contain the functions which allow to apply the numeric operators to bit vectors. Depending on the use of simple bit vectors and standard logic vectors two numeric packages are provided:

  • numeric_bit package based on VHDL type bit and
  • numeric_std package based on the subtype std_ulogic
  • library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;

    Signals declared in packages are not synthetizable. They are useful for applications where information is exchanged among the components in an abstract manner (handshake protocols). Global signals may be used to model Bus Functional architectures and to provide the test benches for such architectures.