Subprograms are independent, named algorithms. A subprogram is either a procedure (zero or more in, inout, or out parameters) or a function (zero or more in parameters and one return value). Subprograms are called by name from anywhere within a VHDL architecture or a package body. Subprograms can be called sequentially (as described later in this chapter) or concurrently (as described in the Concurrent Statements chapter).
In hardware terms, a subprogram call is similar to module instantiation, except that a subprogram call becomes part of the current circuit, whereas module instantiation adds a level of hierarchy to the design. A synthesized subprogram is always a combinatorial circuit (use a process to create a sequential circuit).
Subprograms, like packages, have subprogram declarations and subprogram bodies. A subprogram declaration specifies its name, parameters, and return value (for functions). A subprogram body then implements the operation you want.
Often, a package contains only type and subprogram declarations for other packages to use. The bodies of the declared subprograms are then implemented in the bodies of the declaring packages.
The advantage of the separation between declarations and bodies is that subprogram interfaces can be declared in public packages during system development. One group of developers can use the public subprograms as another group develops the corresponding bodies. You can modify package bodies, including subprogram bodies, without affecting existing users of that package's declarations. You can also define subprograms locally inside an entity, block, or process.
Foundation Express implements procedure and function calls with combinatorial logic, unless you use the map_to_entity compiler directive (see the Mapping Subprograms to Components (Entities) section of this chapter). Foundation Express does not allow you to infer sequential devices, such as latches or flip-flops, in subprograms.
The example below shows a package containing some procedure and function declarations and bodies. The example itself cannot be synthesized; it just creates a template. Designs that instantiate procedure P, however, compile normally.
package EXAMPLE is
procedure P (A: in INTEGER; B: inout INTEGER);
-- Declaration of procedure P
function INV (A: BIT) return BIT;
-- Declaration of function INV
end EXAMPLE;
package body EXAMPLE is
procedure P (A: in INTEGER; B: inout INTEGER) is
-- Body of procedure P
begin
B := A + B;
end;
function INV (A: BIT) return BIT is
-- Body of function INV
begin
return (not A);
end;
end EXAMPLE;
For more information about subprograms, see the Describing Designs chapter.
Subprograms can have zero or more parameters. A subprogram declaration defines each parameter's name, mode, and type. These are a subprogram's formal parameters. When the subprogram is called, each formal parameter is given a value, termed the actual parameter. Each actual parameter's value (of an appropriate type) can come from an expression, a variable, or a signal.
The mode of a parameter specifies whether the actual parameter can be read from (mode in), written to (mode out), or both read from and written to (mode inout). Actual parameters that use modes out and inout must be variables or signals, including indexed names (A(1)) and slices (A(1 to 3)) but cannot be constants or expressions.
Procedures and functions are two kinds of subprograms.
Can have multiple parameters, but only parameters that use mode in. Functions return their own function value. Part of a function definition specifies its return value type (also called the function type).
Functions are used when you do not need to update the parameters, and you want a single return value. For example, the arithmetic function ABS returns the absolute value of its parameter.
A procedure call executes the named procedure with the given parameters. The syntax follows.
procedure_name [ ( [ name => ] expression
{ , [ name => ] expression } ) ] ;
Each expression is called an actual parameter; expression is often just an identifier. If a name is present (positional notation), it is a formal parameter name associated with the actual parameter's expression.
Formal parameters are matched to actual parameters by positional or named notation. Named and positional notation can be mixed, but positional parameters must appear before named parameters.
Conceptually, a procedure call is performed in three steps.
In the synthesized hardware, the procedure's actual inputs and outputs are wired to the procedure's internal logic.
The following example shows a local procedure named SWAP that compares two elements of an array and exchanges these elements if they are out of order. SWAP is repeatedly called to sort an array of three numbers.
package DATA_TYPES is
type DATA_ELEMENT is range 0 to 3;
type DATA_ARRAY is array (1 to 3) of DATA_ELEMENT;
end DATA_TYPES;
use WORK.DATA_TYPES.ALL;
entity SORT is
port(IN_ARRAY: in DATA_ARRAY;
OUT_ARRAY: out DATA_ARRAY);
end SORT;
architecture EXAMPLE of SORT is
begin
process(IN_ARRAY)
procedure SWAP(DATA: inout DATA_ARRAY;
LOW, HIGH: in INTEGER) is
variable TEMP: DATA_ELEMENT;
begin
if(DATA(LOW) > DATA(HIGH)) then -- Check data
TEMP := DATA(LOW);
DATA(LOW) := DATA(HIGH); -- Swap data
DATA(HIGH) := TEMP;
end if;
end SWAP;
variable MY_ARRAY: DATA_ARRAY;
begin
MY_ARRAY := IN_ARRAY; -- Read input to variable
-- Pair-wise sort
SWAP(MY_ARRAY, 1, 2); -- Swap first and second
SWAP(MY_ARRAY, 2, 3); -- Swap second and third
SWAP(MY_ARRAY, 1, 2); -- Swap first and second again
OUT_ARRAY <= MY_ARRAY; -- Write result to output
end process;
end EXAMPLE;
A function call is similar to a procedure call, except that a function call is a type of expression, because it returns a value.
The example below shows a simple function definition and two calls to that function.
function INV (A : BIT) return BIT is
begin
return (not A);
end;
...
process
variable V1, V2, V3: BIT;
begin
V1 := '1';
V2 := INV(V1) xor 1;
V3 := INV('0');
end process;
For more information, see the Function Calls section of the Expressions chapter.