Wednesday, July 22, 2015

VHDL DATA TYPES

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.

1 comment: