DATA TYPES
Every data object in VHDL can hold a value that belongs to a set
of values, specified by using a type declaration.
A type is a name that has associated with it a set of
values and a set of operations. Certain types, and operations that can be
performed on objects of these types, are predefined in the language.
Eg., INTEGER is a predefined type with the set of values being
integers in a specific range provided by the VHDL system i.e., from -(231
- 1) to +(231 - 1).
Some of the allowable and frequently used predefined operators are
+, for addition, -, for subtraction, /, for division, and *, for
multiplication.
BOOLEAN is predefined type that has the values FALSE and TRUE, and
some of its predefined operators are and, or, nor, nand, and not.
The declarations for the predefined types of the language are
contained in package STANDARD.
The language also provides the facility to define new types by
using type declarations and also to define a set of operations on these types
by writing functions that return values of this new type.
Four major categories of types exist. They are
1.
Scalar types: Values belonging to these types appear in a sequential
order.
2.
Composite types: These are composed of elements of a single type (an array
type) or elements of different types (a record type).
3.
Access types: These provide access to objects of a given type (via
pointers).
4.
File types: These provide access to objects that contain a sequence of
values of a given type.
It is possible to derive restricted type, called subtype, from
other predefined or user-defined type which is a type with a constraint.
The constraint specifies the subset of values for the type. The type is called
the base type of the subtype.
An object is said to belong to a subtype if it is of the base type
and if it satisfies the constraint. Subtype declarations are used to
declare subtypes. An object can be declared to either belong to a type or to a
subtype.
The set of operations belonging to a subtype is the same as that
associated with its base type. Subtypes are useful for range checking and for
imposing additional constraints on types.
Eg.,
subtype MY_INTEGER is
INTEGER range 48 to 156 ;
type DIGIT is ('0', '1',
'2', '3', '4', '5', '6', '7', '8', '9') ;
subtype MIDDLE is DIGIT
range '3' to '7' ;
MY_INTEGER is a subtype of the INTEGER base type and has a range
constraint with values ranging from 48 through 156.
DIGIT is a user-defined enumeration type. The last subtype
declaration declares a new subtype called MIDDLE whose base type is DIGIT and
has the values '3', '4', '5', '6' and '7'.
A subtype need not impose a constraint, it can have another name
to an already existing type.
Eg.,
subtype NUMBER is DIGIT;
NUMBER is a subtype with no constraint, its set of values are the
same as that for type DIGIT.
SCALAR TYPES
The values belonging to this type are ordered, i.e., relational
operators can be used on these values. Eg., BIT is a scalar type and the
expression '0' < 1' is valid and has the value TRUE.
There are four different kinds of scalar types. They are
1. enumeration,
2. integer,
3. physical,
4. floating point.
Integer types, floating point types, and physical types are
classified as numeric types since the values associated with these types
are numeric.
Enumeration and integer types are called discrete types
since these types have discrete values associated with them.
Every value belonging to an enumeration type, integer type, or a
physical type has a position number associated with it. This number is
the position of the value in the ordered list of values belonging to that type.
ENUMERATION
TYPES
An enumeration type declaration defines a type that has a set of
user-defined values consisting of identifiers and character literals.
Eg.,
Type MVL is ('U','0','1','Z);
type MICRO_OP is
(LOAD, STORE, ADD, SUB, MUL, DIV);
subtype ARITH_OP is MICRO_OP range ADD to DIV;
The objects defined for these types can be
signal CONTROL_A: MVL;
signal CLOCK: MVL range '0' to '1'; -- Implicit subtype
declaration.
variable IC: MICRO_OP := STORE; -- STORE is the initial value for IC.
variable ALU: ARITH_OP;
MVL is an enumeration type that has the set of ordered values,
'U', '0', '1', and 'Z'.
ARITH_OP is a subtype of the base type MICRO_OP and has a range
constraint specified to be from ADD to DIV, i.e., the values ADD, SUB, MUL, and
DIV belong to the subtype ARITH_OP.
A range constraint can also be specified in an object declaration
as shown in the signal declaration for CLOCK; here the value of signal CLOCK is
restricted to '0' or 1'.
The order of values appearing in an enumeration type declaration
defines the lexical order for the values. i.e., when using relational
operators, a value is always less than a value that appears to its right in the
order.
Eg., In the MICRO_OP type declaration, STORE < DIV is true, and
SUB > MUL is false.
Values of an enumeration type also have a position number
associated with them. The position number of the leftmost element is 0. The
position number of any particular element is one more than the position number
of the element to its left.
The values of an enumeration type are called enumeration literals.
Eg.,
type CAR_STATE is (STOP, SLOW,
MEDIUM, FAST);
The enumeration literals specified in this type declaration are
STOP, SLOW, MEDIUM, and FAST; therefore, objects of type CAR_STATE may be
assigned only these values.
If the same literal is used in two different enumeration type
declarations, the literal is said to be overloaded. Then whenever such a literal
is used, the type of the literal is determined from its surrounding context.
Eg.,
type MVL is ('U', '0', '1 ', 'Z);
--line 1
type TWO_STATE is ('0', '1'); --line 2
. . .
variable CLOCK: TWO_STATE; -- line 3
variable LATCH.CTRL: MVL; -- line 4
. . .
CLOCK := '0': -- line 5
LATCH.CTRL := LATCH.CTRL xor '0'; -- line 6
. . .
Here '0' and '1' are two literals that are overloaded since they
appear in both the types, MVL and TWO_STATE. The value '0' being assigned to
CLOCK in line 5 refers to the literal in the TWO_STATE type since variable
CLOCK is of that type. The value '0' in the expression for LATCH.CTRL refers to
the literal in type MVL since the first argument of the xor operator is of type
MVL.
The predefined enumeration types of the language are CHARACTER,
BIT, BOOLEAN, and SEVERITY_LEVEL.
Values belonging to the type CHARACTER constitute the 128
characters of the ASCII character set. These values are called character
literals and are always written between two single quotes (' ').
Eg.,
'A', '_', '" (the single quote character itself),
'3' (the character literal 3)
The predefined type BIT has the literals '0' and 1', while type
BOOLEAN has {he literals FALSE and TRUE. Type SEVERITY_LEVEL has the values
NOTE, WARNING, ERROR, and FAILURE; this type is typically used in assertion
statements.
INTEGER TYPES
An integer type defines a type whose set of values fall within a
specified integer range.
Eg.,
type INDEX is
range 0 to 15;
type
WORD_LENGTH is range 31 downto
0;
subtype DATA_WORD is WORD_LENGTH range 15 downto 0;
type MY_WORD is range 4 to 6;
object declarations are
constant MUX_ADDRESS: INDEX := 5;
signal DATA_BUS: DATA_WORD;
INDEX is an integer type that includes the integer values from 0
through 15. DATA_WORD is a subtype of WORD_LENGTH that includes the integer
values ranging from 15 through 0. The position of each value of an integer type
is the value itself.
Eg., In the type declaration of WORD_LENGTH, value 31 is at
position 31, value 14 is at position 14, and so on.
In the declaration of MY_WORD, the position number of values 4,5,
and 6, is 4,5, and 6, respectively.
Contrast this with the position number of elements of an
enumeration type; the position number in case of integer types does not refer
to the index of the element in the range, but to its numeric value itself.
The bounds of the range for an integer type must be constants or
locally static expressions; a locally static expression is an expression
that computes to a constant value at compile time (a globally static
expression is an expression that computes to a constant value at
elaboration time).
Values belonging to an integer type are called integer literals.
Examples of integer literals are
56349 6E2 0
98_71_28
Literal 6E2 refers to the decimal value 6 * (10^) = 600. The
underscore ( _ ) character can be used freely in writing integer literals and
has no impact on the value of the literal; 98_71_28 is same as 987128.
INTEGER is the only predefined integer type of the language, its
range is implementation dependent but must at least cover the range -(231
- 1) to +(231 - 1).
FLOATING POINT TYPES
A floating point type has a set of values in a given range of real
numbers.
Eg.,
type TTL_VOLTAGE is
range -5.5 to -1.4;
type REAL_DATA is
range 0.0 to 31.9;
object declaration is
variable LENGTH: REAL_DATA range
0.0 to 15.9;
. . .
variable LI, L2, L3:
REAL_DATA range 0.0 to 15.9;
LENGTH is a variable object of type REAL_DATA that has been
constrained to take real values in the range 0.0 through 15.9 only.
ð The range
constraint was specified in the variable declaration itself. Alternately, it is
possible to declare a subtype and then use this subtype in the variable
declarations.
subtype RD16 is REAL_DATA
range 0.0 to 15.9;
. . .
variable LENGTH: RD16;
. . .
variable Li, L2, L3: RD16;
The range bounds specified
in a floating point type declaration must be constants or locally static
expressions.
Floating -point literals are values of a floating point type. Eg.,
16.26 0.0 0.002
3_1.4_2
Floating point literals differ from integer literals by the
presence of the dot ( . ) character. Thus 0 is an integer literal while 0.0 is
a floating point literal.
Floating point literals can also be expressed in an exponential
form. The exponent represents a power of ten and the exponent value must be an
integer.
Eg.,
62.3 E-2 5.0 E+2
Integer and floating point literals can also be written in a base
other than 10 (decimal). The base can be any value between 2 and 16. Such
literals are called based literals. The exponent represents a power of the specified base.
The syntax for a based literal is
base # based-value
# -- form 1
base # based-value
# E exponent – form2
Eg.,
2#101_101_000# represents (101101000)2 = (360) in decimal,
16#FA# represents (FA)16= (11111010)2 = (250) in
decimal,
16#E#E1 represents (E)16* (16^1) = 14* 16= (224) in
decimal,
2#110.01 # represents (110.01)2 = (6.25) in
decimal.
The base and the exponent values in a
based literal must be in decimal notation.
The only predefined floating point type
is REAL. The range of REAL is again implementation dependent but it must at
least cover the range -I.OE38 to +I.OE38 and it must allow for at least six
decimal digits of precision.
Physical Types
A physical type contains values that
represent measurement of some physical quantity, like time, length, voltage,
and current. Values of this type are expressed as integer multiples of a base
unit.
Eg.,
type CURRENT is
range 0 to 1E9
units
nA; -- (base unit) nano-ampere
uA = 1000 nA; -- micro-ampere
mA = 1000 μA; --milli-ampere
Amp = 1000 mA; -- ampere
end units;
subtype FILTER_CURRENT
is CURRENT range 10 μA to 5 mA;
CURRENT is defined to be a physical
type that contains values from 0nA to 1nA.
The base unit is a nA while all others
are derived units. The position number of a value is the number of base units
represented by that value.
Eg., 2 μA has a position of 2000 while
100 nA has a position of 100.
Values of a physical type are called physical
literals. Physical literals are written as an integer literal followed by
the unit name.
Eg., "10 nA" is a physical
literal (note that a space between 10 and nA is essential) while
"Amp" is also a literal that implies I Amp.
Eg.,
100ns 10V 50 sec Kohm (implies 1Kohm)
The only predefined physical type is
TIME and its range of base values, which again is implementation dependent,
must at least be -(231 - 1) to +(231 - 1).
The declaration of type TIME appears in
package STANDARD.
COMPOSITE TYPES
A composite type represents a
collection of values. There are two composite types: an array type and a record
type.
An array type represents a collection
of values all belonging to a single type; on the other hand, a record type
represents a collection of values that may belong to same or different types.
An object belonging to a composite type
represents a collection of subobjects, one for each element of the composite
type. An element of a composite type could have a value belonging to either a
scalar type, a composite type, or an access type.
Eg., a composite type may be defined to
represent an array of an array of records. This provides the capability of
defining arbitrarily complex composite types.
ARRAY TYPES
An object of an array type consists of
elements that have the same type.
Eg.,
type ADDRESS_WORD
is array (0 to 63) of BIT;
type DATA_WORD is
array (7 downto 0) of MVL;
type ROM is
array (0 to 125) of DATA_WORD;
type DECODE_MATRIX
is array (POSITIVE range 15 downto 1,
NATURAL range 3 downto 0) of MVL;
--POSITIVE and NATURAL are predefined subtypes;
these are:
subtype NATURAL is
INTEGER range 0 to INTEGER'HIGH;
subtype POSITIVE is
INTEGER range 1 to INTEGER'HIGH;
-- The HIGH attribute gives the highest value belonging to the type.
object declarations are
variable ROM_ADDR: ROM;
signal ADDRESS.BUS: ADDRESS_WORD;
constant DECODER: DECODE_MATRIX; - A deferred
constant.
variable DECODE_VALUE: DECODE_MATRIX;
ADDRESS_BUS is a one-dimensional array object that consists of 64
elements of type BIT.
ROM_ADDR is a one-dimensional array object that consists of 126
elements, each element being another array object consisting of 8 elements of
type MVL. Hence an array of arrays is created.
Elements of an array can be accessed by specifying the index
values into the array. Eg.,
ADDRESS_BUS(26) refers to the 27th element of ADDRESS_BUS array
object; ROM_ADDR(10)(5) refers to the value (of type MVL) at index 5 of the
ROM_ADDR(10) data object (of type DATA_WORD); DECODER(5,2) refers to the value
of the element at the 2nd column and 5th row of the two-dimensional object.
Notice the difference in addressing for a two-dimensional array
type and an array of a type which is an array of another type.
The language allows for an arbitrary number of dimensions to be
associated with an array. It also allows an array object to be assigned to
another array object of the same type using a single assignment statement.
Assignment can be made to an entire array, or to an element of an
array, or to a slice of an array.
Eg.,
ROM_ADDR(5) := "0100_0100"; - Assign to an element of an
array.
DECODE_VALUE := DECODER; - An entire array is assigned.
ADDRESS_BUS (8 to 15) <= X"FF; -Assign to a slice of an
array.
These examples of array types are constrained array declarations
since the number of elements in the type is explicitly specified. The language
also allows array types to be unconstrained, i.e., the number of elements in
the array is not specified in the type declaration. Instead, the object
declaration for that type declares the number of elements of the array.
A subtype declaration may also specify the index constraint for an
unconstrained array type. A subprogram parameter may be an object specified to
be of an unconstrained type, the constraint is specified by the actual
parameter passed in during the subprogram call.
Eg., of unconstrained array declarations are
-- The "o" symbol is called the "box" symbol
in the language.
type STACK_TYPE is array (INTEGER range
<>)
of ADDRESS_WORD;
subtype STACK is STACK_TYPE(0 to 63);
type OP_TYPE is (ADD, SUB, MUL, DIV);
type TIMING is array (OP_TYPE range
<>, OP_TYPE range <>)
of TIME;
Object declarations are
variable FAST_STK: STACK_TYPE(-127 to 127);
constant ALU_TIMING: TIMING :=
--ADD, SUB, MUL
((10 ns, 20 ns, 45 ns), -- ADD
(20 ns, 15 ns, 40 ns), -- SUB
(45 ns, 40 ns, 30 ns)); -- MUL
STACK_TYPE is defined to be an unconstrained array type that
specifies the index of the array as an integer type and the element types as
type ADDRESS_WORD. STACK is a subtype of the base type STACK_TYPE with the
specified index constraint.
The variable declaration for FAST_STK, defined to be of type
STACK_TYPE, also specifies an index constraint. The constant ALU_TIMING
specifies the timing for a two-operator ALU, where the operators could be ADD,
SUB, or MUL.
Eg., an ALU that performs ADD and SUB operations has a delay of 20
ns.
The declaration for ALU_TIMING is a special case of a constant
declaration where no constraint need be specified for the unconstrained array
type, since the size of the constant object is determined from the number of
values in the constant.
There are two predefined one-dimensional unconstrained array types
in the language, STRING and BIT_VECTOR. STRING is an array of characters while
BIT_VECTOR is an array of bits.
Eg.,
variable MESSAGE: STRING(1 to 17) :=
"Hello, VHDL world";
signal RX_BUS: BIT_VECTOR(0 to 5) :=
O"37";
--O"37" is a bit-string literal representing the octal
value 37.
constant ADD_CODE: BIT_VECTOR := ('0', '1', '1', '1', '0'):
A value representing a
one-dimensional array of characters is called a string literal. String
literals are written by enclosing the sequence of characters within double quotes (".
. .").
Eg.,
“ THIS IS A TEST “
" SPIKE
DETECTED! "
"State
""READY"" entered!" -- 2 double quote characters in a
sequence
-- represents one
double quote character.
A string literal can be assigned to different types of objects, to
a STRING type object or to a BIT_VECTOR type object. The type of a string
literal is determined from the context in which it appears.
Eg.,
-- Eg1:
variable ERROR.MESSAGE: STRING(1 to 19);
ERROR_MESSAGE := "Fatal ERROR: abort!";
-- Eg2:
variable BUS_VALUE: BIT_VECTOR(0 to 3);
BUS_VALUE:= "1101";
In eg1, the string literal is of type STRING while in eg2, the
string literal is of type BIT_VECTOR. The type of a string literal can also be
explicitly stated by using a qualified expression.
A string literal that represents a sequence of bits (values of
type BIT) can also be represented as a bit string literal. This sequence
of bits, called bit strings, can be represented either as a binary
value, or as an octal value, or as a hexadecimal value. The underscore
character can again be freely used in bit string literals for clarity.
Eg.,
X”FFO" -- X for hexadecimal.
B"00_0011_1101° --B for binary.
O"327" -- 0 for octal.
X"FF_FO_AB"
There are many different ways to assign values to an array object.
Eg.,
variable OP_CODES : BIT_VECTOR(1 to 5);
OP_CODES := "01001"; -- A string literal is assigned.
OP_CODES := ('0', '1', '0', '0', '1'); -- Positional association
is implicit;
-- first value is assigned to OP_CODES(0), second value to
--OP_CODES(1), and so on.
OP_CODES := (2=>'1', 5=>'1', others=>'0'); -- Named
association;
-- second and fifth element of OP_CODES get the value '1', and
-- the rest get a value of '0'.
OP_CODES := (others=>'0'); -- All values set to '0'.
The keyword others is used to all unassigned values; it must be used
at the last. The expressions used in the last three assignments to OP_CODES are
examples of array aggregates.
An aggregate is a set of comma separated elements enclosed
within parenthesis.
RECORD TYPES
An object of a record type is composed of elements of same or
different types.
It is analogous to the record data type in Pascal and the struct
declaration in C.
Eg.,
type PIN_TYPE is range 0 to 10;
type MODULE is
record
SIZE: INTEGER range 20 to 200;
CRITICAL_DLY: TIME;
NO_INPUTS: PIN_TYPE:
NO_OUTPUTS: PIN_TYPE;
end record;
Values can be assigned to a record type object using aggregates.
Eg.,
variable NAND.COMP: MODULE;
-- NAND_COMP is an object of record
type MODULE.
NAND_COMP := (50, 20 ns, 3,2); --
Implies 50 is assigned to SIZE, 20 ns is assigned
-- to CRITICAL_DLY, etc.
Values
can be assigned to a record type object from another record type object of the
same type using a single assignment statement.
Eg.,
signal
NAND_GENERIC: MODULE;
NAND_GENERIC
<= NAND_COMP;
Each
element of a record type object can also be assigned individually by using
selected names.
Eg.,
NAND_COMP.NOJNPUTS
:= 2:
Aggregate
values can be assigned to record type objects using both positional and named
associations. Since an aggregate does not specify its type, it can be either an
array or a record aggregate, depending on its usage.
Eg.,
CAB
:= (others=>'0');
If
CAB is an array type object, the aggregate is treated as an array aggregate; on
the other hand, if CAB is a record type object, the aggregate is a record
aggregate where others refers to all the elements in the record.
ACCESS TYPES
Values
belonging to an access type are pointers to a dynamically allocated object of
some other type. They are similar to pointers in Pascal and C languages.
Eg.,
--
MODULE is a record type declared in the previous sub-section.
type
PTR is access MODULE;
type
FIFO is array (0 to 63, 0 to 7) of BIT;
type
FIFO_PTR is access FIFO;
PTR
is an access type whose values are addresses that point to objects of type
MODULE. Every access type may also have the value null, which means that it
does not point to any object.
Objects
of an access type can only belong to the variable class. When an object of an
access type is declared, the default value of this object is null.
Eg.,
variable
MOD1PTR, MOD2PTR: PTR; - Default value is null.
Objects
that access types point to, can be created using allocators.
Allocators
provide a mechanism to dynamically create objects of a specific type.
MOD1PTR
:= new MODULE;
The
new in this assignment causes an object of type MODULE to be created and the
pointer to this object is returned. The values of the elements of the MODULE
record are the default values of each element; these are 20 (the leftmost value
of the implied subtype) for the SIZE element, TIME'LEFT for the CRITICAL_DLY
element, and the value 0 (this is PIN_TYPE'LEFT) for the NO_INPUTS and
NO_OUTPUTS elements.
Initial
values can also be assigned to a newly created object by explicitly specifying
the values.
Eg.,
MOD2PTR
:= new MODULE'(25, 10ns, 4, 9);
Objects
of an access type can be referenced as
1.
scalar-obj-ptr-all,
if scalar-obj-ptr
is a pointer to a scalar type object,
2.
array-obj-ptr
(element-index), if
array-obj-ptr is a pointer to an array object,
3.
record-obj-ptr.element-name,
if record-obj-ptr
is a pointer to a record object.
The
elements of the object that MOD2PTR points to can be accessed as MOD2PTR.SIZE,
MOD2PTR.CRITICAL_DLY, MOD2PTR.NO_INPUTS and MOD2PTR.NO_OUTPUTS, provided the
pointer is not null.
For
every access type, a procedure DEALLOCATE is implicitly declared. This
procedure, when called, returns the storage occupied by the object to the host
environment.
For
the PTR and FIFO_PTR access types declared earlier, the following DEALLOCATE
procedures are implicitly declared:
procedure
DEALLOCATE (P: inout PTR);
procedure
DEALLOCATE (P: inout FIPO_PTR);
The
execution of the following statement:
DEALLOCATE(MOD2PTR);
causes
the storage occupied by the object that MOD2PTR points to, to be deallocated
and MOD2PTR is set to null.
Pointers
can be assigned to other pointer variables of the same access type. Therefore, MOD1PTR
:= MOD2PTR; is a
legal assignment.
Both
MOD1PTR and MOD2PTR now point to the same object.
Eg.,
type
BITVEC_PTR is access BIT_VECTOR;
variable
BITVEC1:BITVEC_PTR:= new BIT_VECTOR'("1001');
BITVECI
points to an object that is constrained to be of four bits. The bits can be
accessed as BITVECI(0) which is 1, BITVECI(1) which is 0, and so on.
Eg.,
For assigning values to a dynamically allocated object using named association.
MOD2PTR
:= new MODULE'(CRITICAL_DLY=>10 ns, NO_INPUTS=>2,
NO_OUTPUTS=>3,
SIZE=>100);
Access
types are useful in modeling high-level behavior, especially that of regular
structures like RAMs and FIFOs, where pointers can be used to access objects
one at a time in a sequence.
Incomplete Types
It
is possible to have an access type that points to an object that has elements
which are also access types. This can lead to mutually dependent or recursive
access types. Since a type must be declared before it is used, an incomplete
type declaration can be used to solve this problem.
An
incomplete type declaration has the form
type
type-name;
Once
an incomplete type has been declared, the type-name can now be used in
any mutually dependent or recursive access type. However, a corresponding full
type declaration must follow later.
Eg.,
a mutually dependent access type is
type
COMP; -- Record contains component name and list of
--
nets its connected to.
type
NET; -- Record contains net name and list of
--
components it is connected to.
type
COMP_PTR is access COMP;
type
NET_PTR is access NET;
constant
MODMAX: INTEGER := 100;
constant
NETMAX: INTEGER := 2500;
type
COMPJJST is array (1 to MODMAX) of COMP_PTR;
type
NETJJST is array (1 to NETMAX) of NET_PTR;
type
COMPLIST_PTR is access COMP_LIST;
type
NETUST_PTR is access NET_LIST;
--
Full type declarations for COMP and NET follow:
type
COMP is
record
COMP_NAME:
STRING(1 to 10);
NETS:
NETLIST_PTR;
end
record;
type
NET is
record
NET_NAME:
STRING(1 to 10);
COMPONENTS:
COMPLIST_PTR:
end
record;
Here,
COMP and NET have elements which access objects of type NET and COMP,
respectively.
Eg.,
a recursive access type is
type
DFG; —A data flow graph node.
type
OP_TYPE is (ADD, SUB, MUL, DIV, SHIFT, ROTATE);
type
PTR is access DFG;
type
DFG is
record
OP_CODE:
OP_TYPE;
SUCC:
PTR; -- Successor node list.
PRED:PTR;
--Predecessor node list.
end
record;
PTR
is an access type of DFG. It is also the type of an element in DFG.
Eg.,
type
MEM_RANGE is range 0 to 16383;
type
HOLE; -- Represents available memory.
type
HOLE_PTR is access HOLE;
type
HOLE is
record
START-LOC,
END_LOC: MEM_RANGE;
PREV_HOLE,
NEXT_HOLE: HOLE_PTR;
end
record;
File Types
Objects
of file types represent files in the host environment, which provide a
mechanism by which a VHDL design communicates with the host environment.
Syntax
of a file type declaration
type
file-type-name Is file of type-name,
The
type-name is the type of values contained in the file.
Eg.,
type
VECTORS is file of BIT_VECTOR;
type
NAMES is file of STRING;
A
file of type VECTORS has a sequence of values of type BIT_VECTOR; a file of
type NAMES has a sequence of strings as values in it.
A
file is declared using a file declaration.
Syntax
of a file declaration
file
file-name: file-type-name is mode string-expression ',
The
string-expression is interpreted by the host environment as the physical
name of the file.
The
mode of a file, in or out, specifies whether it is an input or an output file,
respectively. Input files can only be read while output files can only be
written to.
Eg.,
file
VEC_FILE: VECTORS is in "/usr/home/jb/uart/div.vec";
file
OUTPUT: NAMES is out "stdout";
VEC_FILE
is declared to be a file that contains a sequence of bit vectors and it is an
input file. It is associated with the file
"/usr/home/jb/uart/div.vec" in the host environment.
A
file belongs to the variable class of objects. However, a file cannot be
assigned values using a variable assignment statement. It can be read, written
to, or tested for an end-of-file condition, only by using special procedures
and functions that are implicitly declared for every file type; these are
procedure
READ (F: in file-type-name ; VALUE: out type-name)
;
--
Gets the next value in VALUE from file F.
procedure
WRITE (F: out file-type-name ; VALUE: in type-name)
;
--
Appends a given value in VALUE to file F.
function
ENDFILE (F: in file-type-name) return BOOLEAN;
--
Returns false if a read on an input file F will be successful in getting
--
another value, otherwise it returns true.
If
type-name is an unconstrained array type, a different READ procedure is
implicitly declared, which is of the form
procedure
READ (F: in file-type-name, VALUE: out type-namer,
LENGTH:
out NATURAL);
--
LENGTH returns the number of elements of the array that was read.
A
file cannot be opened or closed explicitly and values within a file can only be
accessed sequentially.
Eg.,
a test bench that reads vectors from a file,. "fadd.vec", applies
these vectors to the test component, a one-bit full adder, and then writes back
the results into another file, "fadd.out".
entity
FA_TEST is end;
architecture
IO_EXAMPLE of FA_TEST is
component
FULL_ADD
port
(CIN, A, B: in BIT; COUT, SUM: out BIT);
end
component;
subtype
STRING3 is BIT_VECTOR(0 to 2);
subtype
STRING2 is BIT_VECTOR(0 to 1);
type
IN_TYPE is file of STRING3;
type
OUT_TYPE is file of STRING2;
file
VEC_FILE: IN_TYPE is in "/usr/home/jb/vhdl_ex/fadd.vec";
file
RESULT_FILE: OUT_TYPE is
out "/usr/home/jb/vhdl_ex/fadd.out";
signal
S: STRINGS;
signal
Q: STRING2:
begin
FA:
FULL_ADD port map (S(0), S(1), S(2), Q(0), Q(1));
process
constant
PROPAGATION_DELAY: TIME := 25ns;
variable
IN_STR: STRING3;
variable
OUT_STR: STRING2;
begin
while
(not ENDFILE (VEC_FILE)) loop
READ
(VEC_FILE, IN_STR); S <= IN_STR;
wait
for PROPAGATION_DELAY;
OUT_STR
:= Q;
WRITE
(RESULT_FILE, OUT_STR);
end
loop;
assert
FALSE
report
"Completed.";
end
process;
end IO_EXAMPLE;
VEC_FILE
is an input file and contains 3-bit strings, and RESULT_FILE is an output file
into which 2-bit strings are written. Input vectors are read one at a time
until end of file is reached. Each vector is applied and then the process waits
for the full-adder circuit to stabilize before sampling the adder output value
which is written to the output file.
The
assertion statement when executed simply stops the simulation run since the
assertion condition is always false. This statement is not necessary if the
VHDL simulator provides a facility to control the simulation run.
One file type, TEXT, is predefined in
the language; this file type represents a file consisting of variable length
ASCII strings.
An access type, LINE, is also provided
to point to such strings. Operations to read and write data from a single line
are provided. Operations to read and write entire lines are also provided. The
definitions for all these types and operations appear in the predefined
package, TEXTIO.
Thanks for posting such useful information. You have done a great job.
ReplyDeleteAngular-9 training institute in hyderabad.