|
10.9 Other Declarations
Chapter start Previous
page Next page
10.9 Other Declarations
A declaration is one
of the following [VHDL LRM4]:
declaration ::=
type_declaration | subtype_declaration | object_declaration
| interface_declaration | alias_declaration | attribute_declaration
| component_declaration | entity_declaration
| configuration_declaration | subprogram_declaration
| package_declaration
| group_template_declaration | group_declaration
I discussed entity, configuration,
component, package, interface, type, and subtype declarations in Sections
10.5-10.8. Next I shall discuss the other types of declarations (except
for groups or group templates [VHDL
93LRM4.6-4.7], new to VHDL-93,
that are not often used in ASIC design).
10.9.1 Object Declarations
There are four object
classes in VHDL: constant, variable, signal, and file [VHDL
LRM 4.3.1.1-4.3.1.3]. You
use a constant declaration, signal declaration, variable declaration, or
file declaration together with a type. Signals can only be declared in the
declarative region (before the first begin ) of an architecture
or block, or in a package (not in a package body). Variables can only be
declared in the declarative region of a process or subprogram (before the
first begin ). You can think of signals as representing real
wires in hardware. You can think of variables as memory locations in the
computer. Variables are more efficient than signals because they require
less overhead.
You may assign an (explicit)
initial value when you declare a type. If you do not provide initial values,
the (implicit) default initial value of a type or subtype T
is T'LEFT (the leftmost item in the range of the type). For
example:
entity Initial_1 is end; architecture
Behave of Initial_1 is
type Fahrenheit is range 32 to 212; -- Default initial value is 32.
type Rainbow is (R, O, Y, G, B, I, V); -- Default initial value is R.
type MVL4 is ('X', '0', '1', 'Z'); -- MVL4'LEFT = 'X'.
-begin end;
The details of initialization
and assignment of initial values are important--it is difficult to implement
the assignment of initial values in hardware--instead it is better to mimic
the hardware and use explicit reset signals.
Here are the formal definitions
of constant and signal declarations:
constant_declaration ::= constant
identifier {, identifier}:subtype_indication [:= expression] ;
signal_declaration ::= signal
identifier {, identifier}:subtype_indication [register|bus] [:=expression];
I shall explain the use of
signals of kind register or bus in Section 10.13.1.
Signal declarations are explicit signal declarations (ports declared in
an interface declaration are implicit signal declarations). Here is an example
that uses a constant and several signal declarations:
entity Constant_2 is end;
library IEEE; use IEEE.STD_LOGIC_1164.all;
architecture Behave of Constant_2 is
constant Pi : REAL := 3.14159; -- A constant declaration.
signal B : BOOLEAN; signal s1, s2: BIT;
signal sum : INTEGER range 0 to 15; -- Not a new type.
signal SmallBus : BIT_VECTOR (15 downto 0); -- 16-bit bus.
signal GBus : STD_LOGIC_VECTOR (31 downto 0); bus; -- A guarded signal.
begin end;
Here is the formal definition
of a variable declaration:
variable_declaration ::= [shared] variable
identifier {, identifier}:subtype_indication [:= expression] ;
A shared variable can be used
to model a varying quantity that is common across several parts of a model,
temperature, for example, but shared variables are rarely used in ASIC design.
The following examples show that variable declarations belong inside a process
statement, after the keyword process and before the first appearance
of the keyword begin inside a process:
library IEEE; use IEEE.STD_LOGIC_1164.all; entity Variables_1 is end;
architecture Behave of Variables_1 is begin process
variable i : INTEGER range 1 to 10 := 10; -- Initial value = 10.
variable v : STD_LOGIC_VECTOR (0 to 31) := (others => '0');
begin wait; end process; -- The wait stops an endless cycle.
end;
10.9.2 Subprogram Declarations
VHDL code that you
use several times can be declared and specified as subprograms (functions
or procedures) [VHDL LRM2.1]. A
function is a form of expression, may only use parameters of mode in
, and may not contain delays or sequence events during simulation (no wait
statements, for example). Functions are useful to model combinational logic.
A procedure is a form of statement and allows you to control the scheduling
of simulation events without incurring the overhead of defining several
separate design entities. There are thus two forms of subprogram declaration:
a function declaration or a procedure declaration.
subprogram_declaration ::= subprogram_specification ; ::=
procedure
identifier|string_literal [(parameter_interface_list)]
| [pure|impure] function
identifier|string_literal [(parameter_interface_list)]
return type_name|subtype_name;
Here are a function and a procedure
declaration that illustrate the difference:
function add(a, b, c : BIT_VECTOR(3 downto 0)) return BIT_VECTOR is
-- A function declaration, a function can't modify a, b, or c.
procedure Is_A_Eq_B (signal A, B : BIT; signal Y : out BIT);
-- A procedure declaration, a procedure can change Y.
Parameter names in subprogram
declarations are called formal parameters (or formals). During a call to
a subprogram, known as subprogram invocation, the passed values are actual
parameters (or actuals). An impure function, such as the function now
or a function that writes to or reads from a file, may return different
values each time it is called (even with the same actuals). A pure function
(the default) returns the same value if it is given the same actuals. You
may call subprograms recursively. Table 10.13 shows the properties
of subprogram parameters.
TABLE 10.13 Properties
of subprogram parameters. |
Example subprogram declarations:
function my_function(Ff) return BIT is -- Formal function parameter, Ff.
procedure my_procedure(Fp); -- Formal procedure parameter, Fp.
Example subprogram calls:
my_result := my_function(Af); -- Calling a function with an actual parameter, Af.
MY_LABEL:my_procedure(Ap); -- Using a procedure with an actual parameter, Ap.
|
Mode of Ff
or Fp (formals) |
in
|
out
|
inout
|
No mode |
Permissible classes for
Af
(function actual parameter) |
constant
(default)
signal
|
Not allowed |
Not allowed |
file
|
Permissible classes for
Ap
(procedure actual parameter)
|
constant
(default)
variable
signal
|
constant
variable
(default)
signal
|
constant
variable
(default)
signal
|
file
|
Can you read attributes
of
Ff or Fp
(formals)?
|
Yes, except:
'STABLE
'QUIET
'DELAYED
'TRANSACTION
of a signal |
Yes, except:
'STABLE 'QUIET
'DELAYED
'TRANSACTION
'EVENT 'ACTIVE
'LAST_EVENT
'LAST_ACTIVE
'LAST_VALUE
of a signal |
Yes, except:
'STABLE
'QUIET
'DELAYED
'TRANSACTION
of a signal |
|
A subprogram declaration is optional,
but a subprogram specification must be included in the subprogram body (and
must be identical in syntax to the subprogram declaration--see BNF [10.19]):
subprogram_body ::=
subprogram_specification is
{subprogram_declaration|subprogram_body
|type_declaration|subtype_declaration
|constant_declaration|variable_declaration|file_declaration
|alias_declaration|attribute_declaration|attribute_specification
|use_clause|group_template_declaration|group_declaration}
begin
{sequential_statement}
end [procedure|function] [identifier|string_literal] ;
You can include a subprogram
declaration or subprogram body in a package or package body (see Section 10.6)
or in the declarative region of an entity or process statement.
The following is an example of a function declaration and its body:
function subset0(sout0 : in BIT) return BIT_VECTOR -- declaration
-- Declaration can be separate from the body.
function subset0(sout0 : in BIT) return BIT_VECTOR is -- body
variable y : BIT_VECTOR(2 downto 0);
begin
if (sout0 = '0') then y := "000"; else y := "100"; end if;
return result;
end;
procedure clockGen (clk : out BIT) -- Declaration
procedure clockGen (clk : out BIT) is -- Specification
begin -- Careful this process runs forever:
process begin wait for 10 ns; clk <= not clk; end process;
end;
One reason for having the
optional (and seemingly redundant) subprogram declaration is to allow companies
to show the subprogram declarations (to document the interface) in a package
declaration, but to hide the subprogram bodies (the actual code) in the
package body. If a separate subprogram declaration is present, it must conform
to the specification in the subprogram body [VHDL
93LRM2.7]. This means the specification and declaration must be almost
identical; the safest method is to copy and paste. If you define common
procedures and functions in packages (instead of in each entity or architecture,
for example), it will be easier to reuse subprograms. In order to make a
subprogram included in a package body visible outside the package, you must
declare the subprogram in the package declaration (otherwise the subprogram
is private).
You may call a function from
any expression, as follows:
entity F_1 is port (s : out BIT_VECTOR(3 downto 0) := "0000"); end;
architecture Behave of F_1 is begin process
function add(a, b, c : BIT_VECTOR(3 downto 0)) return BIT_VECTOR is
begin return a xor b xor c; end;
begin s <= add("0001", "0010", "1000"); wait; end process; end;
package And_Pkg is
procedure V_And(a, b : BIT; signal c : out BIT);
function V_And(a, b : BIT) return BIT;
end;
package body And_Pkg is
procedure V_And(a,b : BIT; signal c : out BIT) is
begin c <= a and b; end;
function V_And(a,b : BIT) return BIT is
begin return a and b; end;
end And_Pkg;
entity F_2 is port (s: out BIT := '0'); end;
use work.And_Pkg.all; -- use package already analyzed
architecture Behave of F_2 is begin process begin
s <= V_And('1', '1'); wait; end process; end;
I shall discuss the two different
ways to call a procedure in Sections 10.10.4 and 10.13.3.
10.9.3 Alias and Attribute Declarations
An alias declaration
[VHDL 87LRM4.3.4, 93LRM4.3.3]
names parts of a type:
alias_declaration ::=
alias
identifier|character_literal|operator_symbol [ :subtype_indication]
is name [signature] ;
(the subtype indication
is required in VHDL-87, but not in VHDL-93).
Here is an example of alias
declarations for parts of a floating-point number:
entity Alias_1 is end; architecture Behave of Alias_1 is
begin process variable Nmbr: BIT_VECTOR (31 downto 0);
-- alias declarations to split Nmbr into 3 pieces :
alias Sign : BIT is Nmbr(31);
alias Mantissa : BIT_VECTOR (23 downto 0) is Nmbr (30 downto 7);
alias Exponent : BIT_VECTOR ( 6 downto 0) is Nmbr ( 6 downto 0);
begin wait; end process; end; -- the wait prevents an endless cycle
An attribute declaration [VHDL LRM4.4] defines attribute properties:
attribute_declaration ::=
attribute identifier:type_name ; | attribute identifier:subtype_name ;
Here is an example:
entity Attribute_1 is end; architecture Behave of Attribute_1 is
begin process type COORD is record X, Y : INTEGER; end record;
attribute LOCATION : COORD; -- the attribute declaration
begin wait ; -- the wait prevents an endless cycle
end process; end;
You define the attribute properties
in an attribute specification (the following example specifies an attribute
of a component label). You probably will not need to use your own attributes
very much in ASIC design.
attribute LOCATION of adder1 : label is (10,15);
You can then refer
to your attribute as follows:
positionOfComponent := adder1'LOCATION;
10.9.4 Predefined Attributes
The predefined attributes
for scalar and array types in VHDL-93 are shown in Table 10.14 [VHDL 93LRM14.1]. There are two attributes,
'STRUCTURE and 'BEHAVIOR , that are present in
VHDL-87, but removed in VHDL-93. Both of these attributes apply to architecture
bodies. The attribute name A'BEHAVIOR is TRUE
if the architecture A does not contain component instantiations.
The attribute name A'STRUCTURE is TRUE if the
architecture A contains only passive processes (those with
no assignments to signals) and component instantiations. These two attributes
were not widely used. The attributes shown in Table 10.14, however,
are used extensively to create packages and functions for type conversion
and overloading operators, but should not be needed by an ASIC designer.
Many of the attributes do not correspond to "real" hardware and
cannot be implemented by a synthesis tool.
TABLE 10.14 Predefined
attributes for scalar and array types. |
Attribute |
Kind |
Prefix
T, A, E |
Parameter
X or N |
Result
type |
Result |
T'BASE
|
T |
any |
|
base(T) |
base(T), use only with other
attribute |
T'LEFT
|
V |
scalar |
|
T |
Left bound of T |
T'RIGHT
|
V |
scalar |
|
T |
Right bound of T |
T'HIGH
|
V |
scalar |
|
T |
Upper bound of T |
T'LOW
|
V |
scalar |
|
T |
Lower bound of T |
T'ASCENDING
|
V |
scalar |
|
BOOLEAN
|
True if range of T is ascending
|
T'IMAGE(X)
|
F |
scalar |
base(T) |
STRING
|
String representation of
X in T |
T'VALUE(X)
|
F |
scalar |
STRING
|
base(T) |
Value in T with representation
X |
T'POS(X)
|
F |
discrete |
base(T) |
UI |
Position number of X in
T (starts at 0) |
T'VAL(X)
|
F |
discrete |
UI |
base(T) |
Value of position X in T |
T'SUCC(X)
|
F |
discrete |
base(T) |
base(T) |
Value of position X in T
plus one |
T'PRED(X)
|
F |
discrete |
base(T) |
base(T) |
Value of position X in T
minus one |
T'LEFTOF(X)
|
F |
discrete |
base(T) |
base(T) |
Value to the left of X in
T |
T'RIGHTOF(X)
|
F |
discrete |
base(T) |
base(T) |
Value to the right of X
in T |
A'LEFT[(N)]
|
F |
array |
UI |
T(Result) |
Left bound of index N of
array A |
A'RIGHT[(N)]
|
F |
array |
UI |
T(Result) |
Right bound of index N of
array A |
A'HIGH[(N)]
|
F |
array |
UI |
T(Result) |
Upper bound of index N of
array A |
A'LOW[(N)]
|
F |
array |
UI |
T(Result) |
Lower bound of index N of
array A |
A'RANGE[(N)]
|
R |
array |
UI |
T(Result) |
Range A'LEFT(N) to A'RIGHT(N)
|
A'REVERSE_RANGE[(N)]
|
R |
array |
UI |
T(Result) |
Opposite range to A'RANGE[(N)] |
A'LENGTH[(N)]
|
V |
array |
UI |
UI |
Number of values in index
N of array A |
A'ASCENDING[(N)]
|
V |
array |
UI |
BOOLEAN
|
True if index N of A is
ascending |
E'SIMPLE_NAME
|
V |
name |
|
STRING
|
Simple name of E |
E'INSTANCE_NAME
|
V |
name |
|
STRING
|
Path includes instantiated
entities |
E'PATH_NAME
|
V |
name |
|
STRING
|
Path excludes instantiated
entities |
The attribute 'LEFT is
important because it determines the default initial value of a type. For
example, the default initial value for type BIT is BIT'LEFT
, which is '0' . The predefined attributes of signals are listed
in Table 10.15. The most important signal attribute is 'EVENT
, which is frequently used to detect a clock edge. Notice that Clock'EVENT
, for example, is a function that returns a value of type BOOLEAN
, whereas the otherwise equivalent not(Clock'STABLE) , is a
signal. The difference is subtle but important when these attributes are
used in the wait statement that treats signals and values differently.
TABLE 10.15 Predefined
attributes for signals. |
Attribute |
Kind |
Parameter
T |
Result
type |
Result/restrictions |
S'DELAYED [(T)]
|
S |
TIME
|
base(S) |
S delayed by time T |
S'STABLE [(T)]
|
S |
TIME
|
BOOLEAN
|
TRUE if no event on S for
time T |
S'QUIET [(T)]
|
S |
TIME
|
BOOLEAN
|
TRUE if S is quiet for time
T |
S'TRANSACTION
|
S |
|
BIT
|
Toggles each cycle if S
becomes active |
S'EVENT
|
F |
|
BOOLEAN
|
TRUE when event occurs on
S |
S'ACTIVE
|
F |
|
BOOLEAN
|
TRUE if S is active |
S'LAST_EVENT
|
F |
|
TIME
|
Elapsed time since the last
event on S |
S'LAST_ACTIVE
|
F |
|
TIME
|
Elapsed time since S was
active |
S'LAST_VALUE
|
F |
|
base(S) |
Previous value of S, before
last event |
S'DRIVING
|
F |
|
BOOLEAN
|
TRUE if every element of
S is driven |
S'DRIVING_VALUE
|
F |
|
base(S) |
Value of the driver for
S in the current process |
Chapter start Previous page Next page
|