The high-level constructs discussed earlier in this chapter involve the following.
An entity specification defines the entity's characteristics that must be known before that entity can be connected to other entities and components.
For example, before you can connect a counter to other entities, you must specify the number and types of its inputs and outputs. The entity specification defines the ports (inputs and outputs) of an entity.
The syntax of an entity specification follows.
entity entity_name is
[ generic( generic_declarations) ; ]
[ port( port_declarations) ; ]
end [ entity_name ] ;
entity_name is the name of the entity, generic_declarations determine local constants used for sizing or timing the entity, and port_declarations determine the number and type of inputs and outputs. Other declarations are not supported in the entity specification.
Generic specifications are entity parameters. Generics can specify the bit widths of components (such as adders) or provide internal timing values.
A generic can have a default value. A generic is assigned a nondefault value only when the entity is instantiated (see the Component Instantiation Statement section of this chapter) or configured (see the Entity Configurations section of this chapter). Inside an entity, a generic is a constant value.
The syntax of generic_declarations follows.
generic(
[ constant_name : type [ := value ]
{ ; constant_name : type [ := value ] }
);
constant_name is the name of a generic constant, type is a previously defined data type, and the optional value is the default value of constant_name.
Foundation Express supports only INTEGER type generics.
The syntax of port_declarations follows.
port(
[ port_name : mode port_type
{ ; port_name : mode port_type}]
);
port_name is the name of a port; mode is either in, out, inout, or buffer; and port_type is a previously defined data type.
The four port modes follow.
The value read is the assigned value. The buffer can have only one driver. For more information on drivers, see the Driving Signals section of the Concurrent Statements chapter.
The following example shows an entity specification for a 2-input N-bit comparator with a default bit width of 8.
-- Define an entity (design) called COMP
-- that has 2 N-bit inputs and one output.
entity COMP is
generic(N: INTEGER := 8); -- default is 8 bits
port(X, Y: in BIT_VECTOR(0 to N-1);
EQUAL: out BOOLEAN);
end COMP;
Each entity architecture defines one implementation of the entity's function. An architecture can range in abstraction from an algorithm (a set of sequential statements within a process) to a structural netlist (a set of component instantiations).
The syntax of an architecture follows.
architecture architecture_name of entity_name is
{ block_declarative_item }
begin
{ concurrent_statement }
end [ architecture_name ] ;
architecture_name is the name of the architecture, and entity_name is the name of the entity you want to implement.
A block_declarative_item is any of these.
The following example shows a complete circuit description for a three-bit counter, entity specification (COUNTER3), and an architecture (MY_ARCH). Following this example is a schematic of the resulting synthesized circuit.
entity COUNTER3 is
port ( CLK : in bit;
RESET: in bit;
COUNT: out integer range 0 to 7);
end COUNTER3;
architecture MY_ARCH of COUNTER3 is
signal COUNT_tmp : integer range 0 to 7;
begin
process
begin
wait until (clock'event and clock = '1');
-- wait for the clock
if RESET = '1' or COUNT_tmp = 7 then
-- Ck. for RESET or max. count
COUNT_tmp <= 0;
else COUNT_tmp <= COUNT_tmp + 1;
-- Keep counting
end if;
end process;
COUNT <= COUNT_tmp;
end MY_ARCH;
Figure 3.5 Three-Bit Counter Schematic |
In a VHDL architecture, you must not declare constants or signals with the same name as any of the entity's ports. If you declare a constant or signal with a port's name, the new declaration hides that port name. If the new declaration is included in the architecture declaration, as shown in the following example, and not in an inner block, Foundation Express reports an error.
entity X is
port(SIG CONST: in BIT;
OUT1, OUT2: out BIT);
end X;
architecture EXAMPLE of X is
signal SIG: BIT;
constant CONST: BIT := '1';
begin
...
end EXAMPLE;
The error messages generated for Example 3-8 are:
signal SIG : BIT;
^
Error: (VHDL-1872) line 13
Illegal redeclaration of SIG.
constant CONST: BIT := '1';
^
Error: (VHDL-1872) line 14
Illegal redeclaration of CONST.
An entity configuration defines one combination of an entity and architecture for a design.
Foundation Express supports only configurations that associate one top-level entity with an architecture.
The supported syntax for a configuration follows.
configuration configuration_name of entity_name is
for architecture_name
end for;
end [ configuration_name ] ;
configuration_name is the name of this configuration, entity_name is the name of a top-level entity, and architecture_name is the name of the architecture to use for entity_name.
The following example associates the counter's entity specification (COUNTER3) with an architecture (MY_ARCH).
configuration MY_CONFIG of COUNTER3 is
for MY_ARCH
end for;
end MY_CONFIG;
If you do not specify a configuration for an entity with multiple architectures, IEEE VHDL specifies that the last architecture read is used. This is determined from the .mra (most recently analyzed) file.
Subprograms describe algorithms that are meant to be used more than once in a design. Unlike component instantiation statements, when a subprogram is used by an entity or another subprogram, a new level of design hierarchy is not automatically created. However, you can manually define a subprogram as a new level of design hierarchy in the Foundation Express Implementation graphical user interface (GUI).
Two types of subprograms, procedures and functions, can contain zero or more parameters.
A function has a single value that it returns to the caller, but it cannot change the value of its parameters.
Like an entity, a subprogram has two parts - its declaration and its body.
Defines an algorithm that gives the subprogram's expected results
When you declare a subprogram in a package, the subprogram declaration must be in the package declaration, and the subprogram body must be in the package body. A subprogram defined inside an architecture has a body but does not have a corresponding subprogram declaration.
A subprogram declaration lists the names and types of its parameters and, for functions, the type of its return value.
The syntax of a procedure declaration follows.
procedure proc_name [(parameter_declarations)];
proc_name is the name of the procedure.
The syntax of a function declaration follows.
function func_name [ ( parameter_declarations ) ]
return type_name ;
func_name is the name of the function, and type_name is the type of the function's returned value.
The syntax of parameter_declarations is the same as the syntax of port_declarations.
[ parameter_name : mode parameter_type
{ ; parameter_name : mode parameter_type}]
parameter_name is the name of a parameter; mode is either in, out, inout, or buffer; and parameter_type is a previously defined data type.
Procedure parameters can use any mode. Function parameters must use only mode in. Signal parameters of type range cannot be passed to a subprogram.
The following example shows sample subprogram declarations for a function and a procedure.
type BYTE is array (7 downto 0) of BIT;
type NIBBLE is array (3 downto 0) of BIT;
function IS_EVEN(NUM: in INTEGER) return BOOLEAN;
-- Returns TRUE if NUM is even.
procedure BYTE_TO_NIBBLES(B: in BYTE;
UPPER, LOWER: out NIBBLE);
-- Splits a BYTE into UPPER and LOWER halves.
When you call a subprogram, actual parameters are substituted for the declared formal parameters. Actual parameters are either constant values or signal, variable, constant, or port names. An actual parameter must support the formal parameter's type and mode. For example, an input port cannot be used as an out actual parameter, and a constant can be used only as an in actual parameter.
The following example shows some calls to the subprogram declarations from the example above.
signal INT : INTEGER;
variable EVEN : BOOLEAN;
. . .
INT <= 7;
EVEN := IS_EVEN(INT);
. . .
variable TOP, BOT: NIBBLE;
. . .
BYTE_TO_NIBBLES("00101101", TOP, BOT);
A subprogram body defines an implementation of a subprogram's algorithm.
The syntax of a procedure body follows.
procedure procedure_name [ (parameter_declarations) ] is
{ subprogram_declarative_item }
begin
{ sequential_statement }
end [ procedure_name ] ;
The syntax of a function body follows.
function function_name [ (parameter_declarations) ]
return type_name is
{ subprogram_declarative_item }
begin
{ sequential_statement }
end [ function_name ] ;
A subprogram_declarative_item is any of these.
The following example shows subprogram bodies for the sample subprogram declarations for a function and a procedure.
function IS_EVEN(NUM: in INTEGER)
return BOOLEAN is
begin
return ((NUM rem 2) = 0);
end IS_EVEN;
procedure BYTE_TO_NIBBLES(B: in BYTE;
UPPER, LOWER: out NIBBLE) is
begin
UPPER := NIBBLE(B(7 downto 4));
LOWER := NIBBLE(B(3 downto 0));
end BYTE_TO_NIBBLES;
You can overload subprograms; more than one subprogram can have the same name. Each subprogram that uses a given name must have a different parameter profile.
A parameter profile specifies a subprogram's number and type of parameters. This information determines which subprogram is called when more than one subprogram has the same name. Overloaded functions are also distinguished by the type of their return values.
The following example shows two subprograms with the same name, but different parameter profiles.
type SMALL is range 0 to 100;
type LARGE is range 0 to 10000;
function IS_ODD(NUM: SMALL) return BOOLEAN;
function IS_ODD(NUM: LARGE) return BOOLEAN;
signal A_NUMBER: SMALL;
signal B: BOOLEAN;
. . .
B <= IS_ODD(A_NUMBER); -- Will call the first
-- function above
Predefined operators such as +, and, and mod can also be overloaded. By using overloading, you can adapt predefined operators to work with your own data types.
For example, you can declare new logic types, rather than use the predefined types BIT and INTEGER. However, you cannot use predefined operators with these new types unless you declare overloaded operators for the new logic type.
The following example shows how some predefined operators are overloaded for a new logic type.
-- New logic type
function "and"(I1, I2: in NEW_BIT) return NEW_BIT;
function "or" (I1, I2: in NEW_BIT) return NEW_BIT;
-- Declare overloaded operators for new logic type
. . .
signal A, B, C: NEW_BIT;
. . .
C <= (A and B) or C;
VHDL requires overloaded operator declarations to enclose the operator name or symbol in double quotation marks, because they are infix operators (they are used between operands). If you declared the overloaded operators without quotation marks, a VHDL tool considers them functions rather than operators.
Type declarations define the name and characteristics of a type. Types and type declarations are fully described in the Data Types chapter. A type is a named set of values, such as the set of integers or the set (red, green, blue). An object of a given type, such as a signal, can have any value of that type.
The following example shows a type declaration for type NEW_BIT and some functions and variables of that type.
Type declarations are allowed in architectures, packages, entities, blocks, processes, and subprograms.
Use subtype declarations to define the name and characteristics of a constrained subset of another type or subtype. A subtype is fully compatible with its parent type, but only over the subtype's range. Subtype declarations are described in the Data Types chapter.
The following subtype declaration (NEW_LOGIC) is a subrange of the type declaration in the Operator Overloading section.
subtype NEW_LOGIC is NEW_BIT range '0' to '1';
Subtype declarations are allowed wherever type declarations are allowed: in architectures, packages, entities, blocks, processes, and subprograms.
Constant declarations create named values of a given type. The value of a constant can be read but not changed.
Constant declarations are allowed in architectures, packages, entities, blocks, processes, and subprograms. An example of constant declarations follows.
constant WIDTH: INTEGER := 8;
constant X : NEW_BIT := 'X';
You can use constants in expressions, as described in the Expressions chapter and as source values in assignment statements, as described in the Sequential Statements chapter.
Signal declarations create new named signals (wires) of a given type. Signals can be given default (initial) values. However, these initial values are not used for synthesis.
Signals with multiple drivers (signals driven by wired logic) can have associated resolution functions, as described in the Resolution Functions section. An example of signal declarations follows.
signal A, B: BIT;
signal INIT: INTEGER := -1;
Ports are also signals, with the restriction that out ports cannot be read, and in ports cannot be assigned a value. You create signals either by port declarations or by signal declarations. You create ports only by port declarations.
You can declare signals in architectures, entities, and blocks, and use them in processes and subprograms. Processes and subprograms cannot declare signals for internal use.
You can use signals in expressions, as described in the Expressions chapter. Signals are assigned values by signal assignment statements, as described in the Sequential Statements chapter.
Resolution functions are used with signals that can be connected (wired together). For example, if two drivers are directly connected to a signal, the resolution function determines whether the signal value is the AND, OR, or three-state function of the driving values.
Use resolution functions to assign the driving value when there are multiple drivers. For simulation, you can write an arbitrary function to resolve bus conflicts.
A resolution function might change the value of a resolved signal, even if all drivers have the same value.
The resolution function for a signal is part of that signal's subtype declaration. You create a resolved signal in four steps.
The following example shows how to create a resolved signal.
-- Step 1
type SIGNAL_TYPE is ...
-- signal's base type is SIGNAL_TYPE
-- Step 2
subtype res_type is res_function SIGNAL_TYPE;
-- name of the subtype is res_type
-- name of function is res_function
-- signal type is res_type (a subtype of SIGNAL_TYPE)
...
-- Step 3
function res_function (DATA: ARRAY_TYPE)
return SIGNAL_TYPE is
-- declaration of the resolution function
-- ARRAY_TYPE must be an unconstrained array of SIGNAL_TYPE
...
-- Step 4
signal resolved_signal_name:res_type;
-- resolved_signal_name is a resolved signal
...
Foundation Express does not support arbitrary resolution functions. Only wired AND, wired OR, and three-state functions are allowed. Foundation Express requires that you mark all resolution functions with a special directive indicating the kind of resolution you want to perform.
Foundation Express considers the directive only when creating hardware. The body of the resolution function is parsed but ignored. Using unsupported VHDL constructs generates errors.
Do not connect signals that use different resolution functions. Foundation Express supports only one resolution function per network.
The three resolution function directives follow.
-- synopsys resolution_method wired_and
-- synopsys resolution_method wired_or
-- synopsys resolution_method three_state
Pre-synthesis and post-synthesis simulation results might not match if the body of the resolution function used by the simulator does not match the directive used by the synthesizer.
The following example shows how to create and use resolved signals and how to use compiler directives for resolution functions. The signal's base type is the predefined type BIT.
package RES_PACK is
function RES_FUNC(DATA: in BIT_VECTOR) return BIT;
subtype RESOLVED_BIT is RES_FUNC BIT;
end;
package body RES_PACK is
function RES_FUNC(DATA: in BIT_VECTOR) return BIT is
-- pragma resolution_method wired_and
begin
-- The code in this function is ignored by
-- Foundation Express
-- but parsed for correct VHDL syntax
for I in DATA'range loop
if DATA(I) = '0' then
return '0';
end if;
end loop;
return '1';
end;
end;
use work.RES_PACK.all;
entity WAND_VHDL is
port(X, Y: in BIT; Z: out RESOLVED_BIT);
end WAND_VHDL;
architecture WAND_VHDL of WAND_VHDL is
begin
Z <= X;
Z <= Y;
end WAND_VHDL;
Figure 3.6 Circuit for Resolved Signal and Its Resolution Function |
Variable declarations define a named value of a given type.
You can use variables in expressions, as described in the Expressions chapter. Variables are assigned values by variable assignment statements, as described in the Sequential Statements chapter.
An example of variable declarations follows.
variable A, B: BIT;
variable INIT: NEW_BIT;
Variables are declared and used only in processes and subprograms, because processes and subprograms cannot declare signals for internal use.