Home » Programming » 6. Data types

6. Data types

Each data object has a type associated with it. The type defines the set of values that the object can have and the set of operations that are allowed on it. The notion of type is key to VHDL since it is a strongly typed language that requires each object to be of a certain type. In general one is not allowed to assign a value of one type to an object of another data type (e.g. assigning an integer to a bit type is not allowed). There are four classes of data types: scalar, composite, access and file types. The scalar types represent a single value and are ordered so that relational operations can be performed on them. The scalar type includes integer, real, and enumerated types of Boolean and Character. Examples of these will be given further on.

 

  1. Data Types defined in the Standard Package

 

VHDL has several predefined types in the standard package as shown in the table below. To use this package one has to include the following clause:

 

library std, work;

use std.standard.all;

 

 

Types defined in the Package Standard of the std Library
Type Range of values Example
bit ‘0’, ‘1’ signal A: bit :=1;
bit_vector an array with each element of type bit signal INBUS: bit_vector(7 downto 0);
boolean FALSE, TRUE variable TEST: Boolean :=FALSE’
character any legal VHDL character (see package standard); printable characters must be placed between single quotes (e.g. ‘#’) variable VAL: character :=’$’;
file_open_kind* read_mode, write_mode, append_mode  
file_open_status* open_ok, status_error, name_error, mode_error  
integer range is implementation dependent but includes at least –(231 – 1) to +(231 – 1) constant CONST1: integer :=129;
natural integer starting with 0 up to the max specified in the implementation variable VAR1: natural :=2;
positive integer starting from 1 up the max specified in the implementation variable VAR2: positive :=2;
real* floating point number in the range of –1.0 x 1038 to +1.0x 1038 (can be implementation dependent. Not supported by the Foundation synthesis program. variable VAR3: real :=+64.2E12;
severity_level note, warning, error, failure  
string array of which each element is of the type character variable VAR4: string(1 to 12):= “@$#ABC*()_%Z”;
time* an integer number of which the range is implementation defined; units can be expressed in sec, ms, us, ns, ps, fs, min and hr. . Not supported by the Foundation synthesis program variable DELAY: time :=5 ns;

* Not supported by the Foundation synthesis program

 

  1. User-defined Types

 

One can introduce new types by using the type declaration, which names the type and specifies its value range. The syntax is

 

type identifier is type_definition;

 

Here are a few examples of type definitions,

 

Integer types

type small_int is range 0 to 1024;

            type my_word_length is range 31 downto 0;

            subtype data_word is my_word_length range 7 downto 0;

 

A subtype is a subset of a previously defined type. The last example above illustrates the use of subtypes. It defines a type called data_word that is a sybtype of my_word_length of which the range is restricted from 7 to 0. Another example of a subtype is,

 

            subtype int_small is integer range -1024 to +1024;

 

Floating-point types

 

type cmos_level is range 0.0 to 3.3;

type pmos_level is range -5.0 to 0.0;

type probability is range 0.0 to 1.0;

subtype cmos_low_V is cmos_level range 0.0 to +1.8;

 

Note that floating point data types are not supported by the Xilinx Foundation synthesis program.

 

Physical types

 

The physical type definition includes a units identifier as follows,

 

type conductance is range 0 to 2E-9

units

mho;

mmho = 1E-3 mho;

umho = 1E-6 mho;

nmho = 1E-9 mho;

pmho = 1E-12 mho;

end units conductance;

 

Here are some object declarations that use the above types,

 

variable BUS_WIDTH: small_int :=24;

signal DATA_BUS: my_word_length;

variable VAR1: cmos_level range 0.0 to 2.5;

constant LINE_COND: conductance:= 125 umho;

 

Notice that a space must be left before the unit name.

 

The physical data types are not supported by the Xilinx Foundation Express synthesis program.

 

In order to use our own types, we need either to include the type definition inside an architecture body or to declare the type in a package. The latter can be done as follows for a package called “my_types”.

 

package my_types is

type small_int is range 0 to 1024;

     type my_word_length is range 31 downto 0;

     subtype data_word is my_word_length is range 7 downto 0;

type cmos_level is range 0.0 to 3.3;

type conductance is range 0 to 2E-9

units

mho;

mmho = 1E-3 mho;

umho = 1E-6 mho;

nmho = 1E-9 mho;

pmho = 1E-12 mho;

end units conductance;

end package my_types;

 

  1. Enumerated Types

 

An enumerated type consists of lists of character literals or identifiers. The enumerated type can be very handy when writing models at an abstract level. The syntax for an enumerated type is,

 

type type_name is (identifier list or character literal);

 

Here are some examples,

 

type my_3values is (‘0’, ‘1’, ‘Z’);

type PC_OPER is (load, store, add, sub, div, mult, shiftl, shiftr);

type hex_digit is (‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, 8’, ‘9’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’);

type state_type is (S0, S1, S2, S3);

 

Examples of objects that use the above types:

 

signal SIG1: my_3values;

variable ALU_OP: pc_oper;

variable first_digit: hex_digit :=’0’;

signal STATE: state_type :=S2;

 

If one does not initialize the signal, the default initialization is the leftmost element of the list.

 

Enumerated types have to be defined in the architecture body or inside a package as shown in the section above.

 

An example of an enumerated type that has been defined in the std_logic_1164 package is the std_ulogic type, defined as follows

 

type STD_ULOGIC is (

‘U’,                  — uninitialized

‘X’,                  — forcing unknown

‘0’,                   — forcing 0

‘1’,                   — forcing 1

‘Z’,                  — high impedance

‘W’,                 — weak unknown

‘L’,                  — weak 0

‘H’.                  — weak 1

‘-‘);                  — don’t care

 

In order to use this type one has to include the clause before each entity declaration.

 

library ieee; use ieee.std_logic_1164.all;

 

It is possible that multiple drivers are driving a signal. In that case there could be a conflict and the output signal would be undetermined. For instance, the outputs of an AND gate and NOT gate are connected together into the output net OUT1. In order to resolve the value of the output, one can call up a resolution function. These are usually a user-written function that will resolve the signal. If the signal is of the type std_ulogic and has multiple drivers, one needs to use a resolution function. The std_logic_1164 package has such a resolution function, called RESOLVED predefined. One can then use the following declaration for signal OUT1

 

signal OUT1: resolved: std_ulogic;

 

If there is contention, the RESOLVED function will be used to intermediate the conflict and determine the value of the signal. Alternatively, one can declare the signal directly as a std_logic type since the subtype std_logic has been defined in the std_logic_1164 package.

 

signal OUT1: std_logic;

 

  1. Composite Types: Array and Record

 

Composite data objects consist of a collection of related data elements in the form of an array or record. Before we can use such objects one has to declare the composite type first.

 

Array Type

 

An array type is declared as follows:

 

type array_name is array (indexing scheme) of element_type;

 

type MY_WORD is array (15 downto 0) of std_logic;

type YOUR_WORD is array (0 to 15) of std_logic;

type VAR is array (0 to 7) of integer;

type STD_LOGIC_1D is array (std_ulogic) of std_logic;

 

In the first two examples above we have defined a one-dimensional array of elements of the type std_logic indexed from 15 down to 0, and 0 up to 15, respectively. The last example defines a one-dimensional array of the type std_logic elements that uses the type std_ulogic to define the index constraint. Thus this array looks as follows:

 

Index:               ‘U’ ‘X’ ‘0’ ‘1’ ‘Z’ ‘W’ ‘L’ ‘H’ ‘-‘

Element:

 

We can now declare objects of these data types. Some examples are given

 

signal MEM_ADDR: MY_WORD;

signal DATA_WORD: YOUR_WORD := B“1101100101010110”;

constant SETTING: VAR := (2,4,6,8,10,12,14,16);

 

In the first example, the signal MEM_ADDR is an array of 16 bits, initialized to all ‘0’s. To access individual elements of an array we specify the index. For example, MEM_ACCR(15) accesses the left most bit of the array, while DATA_WORD(15) accesses the right most bit of the array with value ‘0’. To access a subrange, one specifies the index range, MEM_ADDR(15 downto 8) or DATA_WORD(0 to 7).

 

Multidimensional arrays can be declared as well by using a similar syntax as above,

 

type MY_MATRIX3X2 is array (1 to 3, 1 to 2) of natural;

type YOUR_MATRIX4X2 is array (1 to 4, 1 to 2) of integer;

type STD_LOGIC_2D is array (std_ulogic, std_ulogic) of std_logic;

 

variable DATA_ARR: MY_MATRIX :=((0,2), (1,3), (4,6), (5,7));

 

The variable array DATA_ARR will then be initialized to,

 

0 2

1 3

4 6

5 7

 

To access an element one specifies the index, e.g. DATA_ARR(3,1) returns the value 4.

The last example defines a 9×9 array or table with an index the elements of the std_ulogic type.

 

Sometimes it is more convenient not to specify the dimension of the array when the array type is declared. This is called an unconstrained array type. The syntax for the array declaration is,

 

type array_name is array (type range <>) of element_type;

 

Some examples are

 

type MATRIX is array (integer range <>) of integer;

type VECTOR_INT is array (natural range <>) of integer;

type VECTOR2 is array (natural range <>, natural range <>) of std_logic;

 

The range is now specified when one declares the array object,

 

variable MATRIX8: MATRIX (2 downto -8) := (3, 5, 1, 4, 7, 9, 12, 14, 20, 18);

variable ARRAY3x2: VECTOR2 (1 to 4, 1 to 3)) := ((‘1’,’0’), (‘0’,’-‘), (1, ‘Z’));

 

Record Type

A second composite type is the records type. A record consists of multiple elements that may be of different types. The syntax for a record type is the following:

 

type name is

record

identifier :subtype_indication;

:

identifier :subtype_indication;

end record;

 

As an example,

 

type MY_MODULE is

record

RISE_TIME     :time;

FALL_TIME   : time;

SIZE                : integer range 0 to 200;

DATA              : bit_vector (15 downto 0);

end record;

 

signal A, B: MY_MODULE;

 

To access values or assign values to records, one can use one of the following methods:

 

A.RISE_TIME <= 5ns;

A.SIZE <= 120;

 

B <= A;

 

  1. Type Conversions

 

Since VHDL is a strongly typed language one cannot assign a value of one data type to a signal of a different data type. In general, it is preferred to the same data types for the signals in a design, such as std_logic (instead of a mix of std_logic and bit types). Sometimes one cannot avoid using different types. To allow assigning data between objects of different types, one needs to convert one type to the other. Fortunately there are functions available in several packages in the ieee library, such as the std_logic_1164 and the std_logic_arith packages. As an example, the std_logic_1164 package allows the following conversions:

 

 

Conversions supported by std_logic_1164 package
Conversion
Function
std_ulogic   to bit to_bit(expression)
std_logic_vector to bit_vector to_bitvector(expression)
std_ulogic_vector to bit_vector to_bitvector(expression)
bit to std_ulogic To_StdULogic(expression)
bit_vector   to     std_logic_vector To_StdLogicVector(expression)
bit_vector to   std_ulogic_vector To_StdUlogicVector(expression)
std_ulogic to std_logic_vector To_StdLogicVector(expression)
std_logic to std_ulogic_vector To_StdUlogicVector(expression)

 

The IEEE std_logic_unsigned and the IEEE std_logic_arith packages allow additional conversions such as from an integer to std_logic_vector and vice versa.

 

An example follows.

 

entity QUAD_NAND2 is

port (A, B: in bit_vector(3 downto 0);

out4: out std_logic_vector (3 downto 0));

end QUAD_NAND2;

 

architecture behavioral_2 of QUAD_NAND2 is

begin

out4 <= to_StdLogicVector(A and B);

end behavioral_2;

 

 

The expression “A and B” which is of the type bit_vector has to be converted to the type std_logic_vector to be of the same type as the output signal out4.

 

The syntax of a type conversion is as follows:

 

type_name (expression);

 

In order for the conversion to be legal, the expression must return a type that can be converted into the type type_name. Here are the conditions that must be fulfilled for the conversion to be possible.

 

  • Type conversions between integer types or between similar array types are possible
  • Conversion between array types is possible if they have the same length and if they have identical element types or convertible element types.
  • Enumerated types cannot be converted.

 

 

  1. Attributes

 

VHDL supports 5 types of attributes. Predefined attributes are always applied to a prefix such as a signal name, variable name or a type. Attributes are used to return various types of information about a signal, variable or type. Attributes consist of a quote mark (‘) followed by the name of the attribute.

 

Signal attributes

 

The following table gives several signal attributes.

 

Attribute Function
signal_name’event returns the Boolean value True if an event on the signal occurred, otherwise gives a False
signal_name’active returns the Boolean value True there has been a transaction (assignment) on the signal, otherwise gives a False
signal_name’transaction returns a signal of the type “bit” that toggles (0 to 1 or 1 to 0) every time there is a transaction on the signal.
signal_name’last_event returns the time interval since the last event on the signal
signal_name’last_active returns the time interval since the last transaction on the signal
signal_name’last_value gives the value of the signal before the last event occurred on the signal
signal_name’delayed(T) gives a signal that is the delayed version (by time T) of the original one. [T is optional, default T=0]
signal_name’stable(T) returns a Boolean value, True, if no event has occurred on the signal during the interval T, otherwise returns a False. [T is optional, default T=0]
signal_name’quiet(T) returns a Boolean value, True, if no transaction has occurred on the signal during the interval T, otherwise returns a False. [T is optional, default T=0]

 

An example of an attribute is

 

if (CLOCK’event and CLOCK=’1’) then

 

This expression checks for the arrival of a positive clock edge. To find out how much time has passed since the last clock edge, one can use the following attribute:

 

CLOCK’last_event

 

Scalar attributes

 

Several attributes of a scalar type, scalar-type, are supported. The following table shows some of these attributes.

 

Attribute Value
scalar_type’left returns the first or leftmost value of scalar-type in its defined range
scalar_type’right returns the last or rightmost value of scalar-type in its defined range
scalar_type’low returns the lowest value of scalar-type in its defined range
scalar_type’high returns the greatest value of scalar-type in its defined range
scalar_type’ascending True if T is an ascending range, otherwise False
scalar_type’value(s) returns the value in T that is represented by s (s stands for string value).

 

Here are a few examples.

 

type conductance is range 1E-6 to 1E3

units mho;

end units conductance;

type my_index is range 3 to 15;

type my_levels is (low, high, dontcare, highZ);

 

conductance’right                     returns: 1E3

conductance’high                                              1E3

conductance’low                                              1E-6

my_index’left                                                    3

my_index’value(5)                                            “5”

my_levels’left                                                    low

my_levels’low                                                   low

my_levels’high                                      highZ

my_levels’value(dontcare)                                 “dontcare”

 

Array attributes

 

By using array attributes one can return an index value corresponding to the array range.

 

The following attributes are supported.

 

Attribute Returns
MATRIX‘left(N)

MATRIX’right(N)

MATRIX’high(N)

MATRIX’low(N)

MATRIX’length(N)

MATRIX’range(N)

MATRIX’reverse_range(N)

MATRIX’ascending(N)

left-most element index

right-most index

upper bound

lower bound

the number of elements

range

reverse range

a Boolean value TRUE if index is an ascending range, otherwise FALSE

 

The number N between parentheses refers to the dimension. For a one-dimensional array, one can omit the number N as shown in the examples below. Lets assume the following arrays, declared as follows:

 

type MYARR8x4 is array (8 downto 1, 0 to 3) of boolean;

type MYARR1 is array (-2 to 4) of integer;

 

MYARR1’left              returns:             -2

MYARR1’right                                                4

MYARR1’high                                     4

MYARR1’reverse_range                                  4 downto to -2

 

MYARR8x4’left(1)                                          8

MYARR8x4’left(2)                                          0

MYARR8x4’right(2)                                        3

MYARR8x4’high(1)                                         8

MYARR8x4’low(1)                                         1

MYARR8x4’ascending(1)                                False

Leave a Reply

Your email address will not be published. Required fields are marked *

Name *
Email *
Website

March 2024
M T W T F S S
 123
45678910
11121314151617
18192021222324
25262728293031

Pages

Recent Comments

    Archives