|
VHDL Tutorial |
Array types in VHDL and array operations | ||
|
Introduction Fundamental concepts Modelling concepts Elements of behaviour Elements of structure Analysis elaboration Lexical elements Identifiers Numbers Characters and strings Syntax descriptions Constants and variables Scalar type Integer types Floating point types Time type Enumeration types Character types Boolean type Bits type Standard logic Sequential statements Case statements Loop and exit statements Assertion statements Array types & array operations Architecture bodies Entity declarations Behavioral descriptions Wait statements Delta delays Process statements Conditional signal assignment Selected signal assigment Structural descriptions Library and library clauses Procedures Procedure parameters Signal parameters Default values Unconstrained array parameter Functions Package declarations and bodies Subprograms in package Use clauses Resolved signals and subtypes Resolved signals and ports Parameterizing behavior Parameterizing structure |
Array Types and Operations
An array consists of a collection of values, all of which are of the same type as each other. The position of each element in an array is given by a scalar value called its index. To create an array object in a model, we first define an array type in a type declaration. The syntax rule for an array type definition is
array_type_definition ⇐ array ( discrete_range ) of element_subtype_indication
This defines an array type by specifying the index range and the element type or sub- type. A discrete range is a subset of values from a discrete type (an integer or enu- meration type). It can be specified as shown by the simplified syntax rule
Want To have highly paid VLSI jobs ?? then you may contact at
Contact : webmaster@freehost7com
discrete_range ⇐ type_mark I simple_expression ( to I downto ) simple_expression We illustrate these rules for defining arrays with a series of examples. Here is a simple example to start off with, showing the declaration of an array type to represent words of data:
type word is array (0 to 31) of bit;
Each element is a bit, and the elements are indexed from 0 up to 31. An alterna- tive declaration of a word type, more appropriate for little-endian systems, is
type word is array (31 downto 0) of bit;
The difference here is that index values start at 31 for the leftmost element in val- ues of this type and continue down to 0 for the rightmost. The index values of an array do not have to be numeric. For example, given this declaration of an enumer- ation type:
type controller_state is (initial, idle, active, error);
we could then declare an array as follows:
type state_counts is array (idle to error) of natural;
If we need an array element for every value in an index type, we need only name the index type in the array declaration without specifying the range. For example:
subtype coeff_ram_address is integer range 0 to 63; type coeff_array is array (coeff_ram_address) of real;
Once we have declared an array type, we can define objects of that type, includ- ing constants, variables and signals. For example, using the types declared above, we can declare variables as follows:
variable buffer_register, data_register : word; variable counters : state_counts; variable coeff : coeff_array;
Each of these objects consists of the collection of elements described by the corre- sponding type declaration. An individual element can be used in an expression or as the target of an assignment by referring to the array object and supplying an index value, for example:
coeff(0) := 0.0;
If active is a variable of type controller_state, we can write
counters(active) := counters(active) + 1;
An array object can also be used as a single composite object. For example, the assignment
data_register := buffer_register; copies all of the elements of the array buffer_register into the corresponding elements of the array data_register.
Array Aggregates
Often we also need to write literal array values, for example, to initialize a variable or constant of an array type. We can do this using a VHDL construct called an array aggregate, according to the syntax rule
aggregate ⇐ ( ( [ choices => ] expression ) { , } )
Let us look first at the form of aggregate without the choices part. It simply con- sists of a list of the elements enclosed in parentheses, for example:
type point is array (1 to 3) of real; constant origin : point := (0.0, 0.0, 0.0); variable view_point : point := (10.0, 20.0, 0.0);
This form of array aggregate uses positional association to determine which value in the list corresponds to which element of the array. The first value is the element with the leftmost index, the second is the next index to the right, and so on, up to the last value, which is the element with the rightmost index. There must be a one-to-one correspondence between values in the aggregate and elements in the array. An alternative form of aggregate uses named association, in which the index val- ue for each element is written explicitly using the choices part shown in the syntax rule. The choices may be specified in exactly the same way as those in alternatives of a case statement. For example, the variable declaration and initialization could be rewritten as
variable view_point : point := (1 => 10.0, 2 => 20.0, 3 => 0.0);
Figure 3-9 is a model for a memory that stores 64 real-number coefficients, initialized to 0.0. We assume the type coeff_ram_address is previously declared as above. The architecture body contains a process with an array variable repre- senting the coefficient storage. The array is initialized using an aggregate in which all elements are 0.0. The process is sensitive to all of the input ports. When rd is 1, the array is indexed using the address value to read a coefficient. When wr is 1, the address value is used to select which coefficient to change.
entity coeff_ram is port ( rd, wr : in bit; addr : in coeff_ram_address; d_in : in real; d_out : out real ); end entity coeff_ram; architecture abstract of coeff_ram is begin memory : process (rd, wr, addr, d_in) is type coeff_array is array (coeff_ram_address) of real; variable coeff : coeff_array := (others => 0.0); begin if rd = '1' then d_out <= coeff(addr); end if; if wr = '1' then coeff(addr) := d_in; end if; end process memory;
An entity and architecture body for a memory module that stores real-number coefficients. The mem-
Array Attributes
VHDL provides a number of attributes to refer to information about the index ranges of array types and objects. Attributes are written by following the array type or object name with the symbol ' and the attribute name. Given some array type or object A, and an integer N between 1 and the number of dimensions of A, VHDL defines the following attributes:
A'left Left bound of index range of A A'right Right bound of index range of A A'range Index range of A A'reverse_range Reverse of index range of A
A'length Length of index range of A
For example, given the array declaration
type A is array (1 to 4) of boolean;
some attribute values are
A'left = 1 A'right = 4
A'range is 1 to 4 A'reverse_range is 4 downto 1
A'length = 4
The attributes can be used when writing for loops to iterate over elements of an array. For example, given an array variable free_map that is an array of bits, we can write a for loop to count the number of 1 bits without knowing the actual size of the array:
count := 0; for index in free_map'range loop if free_map(index) = '1' then
count := count + 1; end if; end loop;
The 'range and 'reverse_range attributes can be used in any place in a VHDL model where a range specification is required, as an alternative to specifying the left and right bounds and the range direction. Thus, we may use the attributes in type and subtype definitions, in subtype constraints, in for loop parameter specifications, in case state- ment choices and so on. The advantage of taking this approach is that we can specify the size of the array in one place in the model and in all other places use array at- tributes. If we need to change the array size later for some reason, we need only change the model in one place.
Unconstrained Array Types
The array types we have seen so far in this chapter are called constrained arrays, since the type definition constrains index values to be within a specific range. VHDL also allows us to define unconstrained array types, in which we just indicate the type of the index values, without specifying bounds. An unconstrained array type definition is described by the alternate syntax rule
array_type_definition ⇐ array ( type_mark range <> ) of element_subtype_indication
The symbol <>, often called box, can be thought of as a placeholder for the index range, to be filled in later when the type is used. An example of an unconstrained array type declaration is
type sample is array (natural range <>) of integer;
An important point to understand about unconstrained array types is that when we declare an object of such a type, we need to provide a constraint that specifies the index bounds. We can do this in several ways. One way is to provide the constraint when an object is created, for example:
variable short_sample_buf : sample(0 to 63);
This indicates that index values for the variable short_sample_buf are natural num- bers in the ascending range 0 to 63. Another way to specify the constraint is to declare a subtype of the unconstrained array type. Objects can then be created using this sub- type, for example:
subtype long_sample is sample(0 to 255); variable new_sample_buf, old_sample_buf : long_sample;
These are both examples of a new form of subtype indication that we have not yet seen. The syntax rule is
subtype_indication ⇐ type_mark [ ( discrete_range ) ] The type mark is the name of the unconstrained array type, and the discrete range specifications constrain the index type to a subset of values used to index array ele- ments.
Strings
VHDL provides a predefined unconstrained array type called string, declared as
type string is array (positive range <>) of character;
For example:
constant LCD_display_len : positive := 20; subtype LCD_display_string is string(1 to LCD_display_len); variable LCD_display : LCD_display_string := (others => ' ');
Bit Vectors
VHDL also provides a predefined unconstrained array type called bit_vector, declared as
type bit_vector is array (natural range <>) of bit;
For example, subtypes for representing bytes of data in a little-endian processor might be declared as
subtype byte is bit_vector(7 downto 0);
Alternatively, we can supply the constraint when an object is declared, for example:
variable channel_busy_register : bit_vector(1 to 4);
Standard-Logic Arrays
The standard-logic package std_logic_1164 provides an unconstrained array type for vectors of standard-logic values. It is declared as
type std_ulogic_vector is array ( natural range <> ) of std_ulogic;
We can define subtypes of the standard-logic vector type, for example:
subtype std_ulogic_word is std_ulogic_vector(0 to 31);
Or we can directly create an object of the standard-logic vector type:
signal csr_offset : std_ulogic_vector(2 downto 1);
Unconstrained Array Ports
An important use of an unconstrained array type is to specify the type of an array port. This use allows us to write an entity interface in a general way, so that it can connect to array signals of any size or with any range of index values. When we instantiate the entity, the index bounds of the array signal connected to the port are used as the bounds of the port.
Suppose we wish to model a family of and gates, each with a different num- ber of inputs. We declare the entity interface as shown in Figure 3-10. The input port is of the unconstrained type bit_vector. The process in the architecture body performs a logical and operation across the input array. It uses the 'range attribute to determine the index range of the array, since the index range is not known until the entity is instantiated.
entity and_multiple is port ( i : in bit_vector; y : out bit ); end entity and_multiple; architecture behavioral of and_multiple is begin and_reducer : process ( i ) is variable result : bit; begin result := '1'; for index in i'range loop result := result and i(index); end loop; y <= result; end process and_reducer;
An entity and architecture body for an and gate with an unconstrained array input port.
To illustrate the use of the multiple-input gate entity, suppose we have the following signals:
signal count_value : bit_vector(7 downto 0); signal terminal_count : bit;
We instantiate the entity, connecting its input port to the bit-vector signal:
tc_gate : entity work.and_multiple(behavioral) port map ( i => count_value, y => terminal_count);
For this instance, the input port is constrained by the index range of the sig-
|