![]() |
![]() |
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 the Combinatorial Versus Sequential Processes section of 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. A 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 declarations and bodies. A subprogram declaration specifies its name, parameters, and return value (for functions). The 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 Procedures and Functions as Design Components section of this chapter). Foundation Express does not allow you to infer sequential devices, such as latches or flip-flops, in subprograms.
The following example 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 INVERT (A: BIT) return BIT;
-- Declaration of function INVERT
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 INVERT (A: BIT) return BIT is
-- Body of function INVERT
begin
return (not A);
end;
end EXAMPLE;
For more information about subprograms, see the Subprograms section of the Design Descriptions 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 receives 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 the following.
Actual parameters that use mode out and mode inout must be variables or signals and include indexed names (A(1)) and slices (A(1 to 3)). They 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).
Use functions 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 } ) ] ;
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. A notation can mix named and positional notation, but positional parameters must precede named parameters.
A procedure call occurs in three steps.
In the synthesized circuit, 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. The figure following the example illustrates the corresponding design.
library IEEE;
use IEEE.std_logic_1164.all;
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;
library IEEE;
use IEEEE.std_logic_1164.all;
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 1st and 2nd
SWAP(MY_ARRAY, 2, 3); -- Swap 2nd and 3rd
SWAP(MY_ARRAY, 1, 2); -- Swap 1st and 2nd -- again
OUT_ARRAY <= MY_ARRAY; -- Write result to -- output
end process;
end EXAMPLE;
A function call executes a named function with the given parameter values. The value returned to an operator is the function's return value. The syntax follows.
function_name ( [parameter_name =>] expression
{, [parameter_name =>] expression }) ;
You can specify parameter values in positional or named notation, as you can aggregate values.
In positional notation, the parameter_name -> construct is omitted. The first expression provides a value for the function's first parameter, the second expression is for the second parameter, and so on.
In named notation, parameter_name -> is specified before an expression; the named parameter gets the value of that expression.
You can mix positional and named expressions in the same function call if you put all positional expressions before named parameter expressions.
The example below shows a simple function definition and two calls to that function.
function INVERT (A : BIT) return BIT is
begin
return (not A);
end;
...
process
variable V1, V2, V3: BIT;
begin
V1 := '1';
V2 := INVERT (V1) xor 1;
V3 := INVERT ('0');
end process;