What is an array
In any software programming language, when we need to deal with a collection of elements of the same type we can take advantage of the dedicated data structures provided by the language. In VHDL such kind of structure is defined “array“.
We can collect any data type object in an array type, many of the predefined VHDL data types are defined as an array of a basic data type.
An example is:
type string is array (positive range <>) of character; type bit_vector is array (natural range <>) of bit;
VHDL array declaration
The VHDL Arrays can be may both one-dimensional (with one index) or multidimensional (with two or more indices).
When we declare an array, we can decide if the array is
- Constrained
- Urnconstrained
In the constrained array, he bounds for an index are established when the array type is defined
In the unconstrained array, the bounds are established subsequently during the declaration of the variable or signal.
The BNF for declaring an array type is:
array_type_definition ::= unconstrained_array_definition | constrained_array_definition unconstrained_array_definition ::= array ( index_subtype_definition { , index_subtype_definition } ) of element_subtype_indication constrained_array_definition ::= array index_constraint of element_subtype_indication index_subtype_definition ::= type_mark range <> index_constraint ::= ( discrete_range { , discrete_range } ) discrete_range ::= discrete_subtype_indication | range subtype_declaration ::= subtype identifier is subtype_indication ; subtype_indication ::= [ resolution_function_name ] type_mark [ constraint ] type_mark ::= type_name | subtype_name constraint ::= range_constraint | index_constraint
the subtype allows the values taken on by an object to be restricted or constrained subset of some base type.
Some examples of constrained array type declarations:
type t_vector is array (integer range <>) of real; type t_word is array (31 downto 0) of bit; type t_memory is array (address) of word; type t_transform is array (1 to 4, 1 to 4) of real; type string is array (positive range <>) of character; type bit_vector is array (natural range <>) of bit; signal mystring : string(1 to 255); variable mybitvector : bit_vector(15 downto 0);
Accessing to the array object
An element of an array object can be referred to by indexing the name of the object.
Figure 1 reports an example of the signal vector and matrix addressing, here below the VHDL code for matrix and vector definition and addressing.
type t_matrix is array (0 to 3, 0 to 7) of integer; -- matrix 4 x 8 type t_vector is array (0 to 3) of integer; -- vector 4 element signal matrix : t_matrix; signal vector : t_vector; matrix(0,2) <= 5; vector(1) <= 42;
Implementing a MUX using an array in VHDL
In this post, we describe the VHDL implementation of a MUX using the CASE-WHEN statement. Another compact and elegant way for describing a MUX architecture in VHDL is to use the array approach. In the VHDL code below is reported the VHDL code of the implementation of an 8-way MUX. We defined an array of the same type of data of the MUX then the array is referenced using the MUX selector. In a single VHDL line, we can implement an N-way MUX.
library ieee ; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity array_mux8 is port( d0 : in std_logic_vector(1 downto 0); d1 : in std_logic_vector(1 downto 0); d2 : in std_logic_vector(1 downto 0); d3 : in std_logic_vector(1 downto 0); d4 : in std_logic_vector(1 downto 0); d5 : in std_logic_vector(1 downto 0); d6 : in std_logic_vector(1 downto 0); d7 : in std_logic_vector(1 downto 0); s : in std_logic_vector(2 downto 0); m : out std_logic_vector(1 downto 0)); end array_mux8; architecture rtl of array_mux8 is type t_array_mux is array (0 to 7) of std_logic_vector(1 downto 0); signal array_mux : t_array_mux; begin array_mux(0) <= d0; array_mux(1) <= d1; array_mux(2) <= d2; array_mux(3) <= d3; array_mux(4) <= d4; array_mux(5) <= d5; array_mux(6) <= d6; array_mux(7) <= d7; m <= array_mux(to_integer(unsigned(s))); end rtl;
As example, Figure 2 shows the RTL view of the 8-way MUX implementation on Altera/Intel Cyclone II FPGA
Implementing a LUT using an array in VHDL
A typical application of array in VHDL is the implementation of a LUT aka Look Up Table. In the example below is reported a vector of integer whose range is 0 to 15 i.e. 4 bit unsigned. The LUT is can be initialized in different ways as in the VHDL example below:
library ieee ; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity array_lut8 is port( s1 : in std_logic_vector(2 downto 0); s2 : in std_logic_vector(2 downto 0); m1 : out std_logic_vector(3 downto 0); m2 : out std_logic_vector(3 downto 0)); end array_lut8; architecture rtl of array_lut8 is type t_array_lut is array (0 to 7) of integer range 0 to 15; constant C_LUT1 : t_array_lut := ( 8 , 14 , 13 , 9 , 1 , 3 , 5 , 11); constant C_LUT2 : t_array_lut := ( 5 => 13 , 4 => 9 , 3 => 1 , 7 => 8 , 6 => 14 , 2 => 3 , 1 => 5 , 0 => 11); begin m1 <= std_logic_vector(to_unsigned(C_LUT1(to_integer(unsigned(s1))),4)); m2 <= std_logic_vector(to_unsigned(C_LUT2(to_integer(unsigned(s2))),4)); end rtl;
Figure 3 shows the technology view of the LUT implementation on Altera/Intel Cyclone II FPGA
Implementing a Digital Signal Processing section using an array
Another typical example of array usage is when you need to perform the same operation on a large number of a signal. An example is adding two sets of input number in the code below:
library ieee ; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity array_add8 is port( -- first set op openrand i_op10 : in std_logic_vector(3 downto 0); i_op11 : in std_logic_vector(3 downto 0); i_op12 : in std_logic_vector(3 downto 0); i_op13 : in std_logic_vector(3 downto 0); i_op14 : in std_logic_vector(3 downto 0); i_op15 : in std_logic_vector(3 downto 0); i_op16 : in std_logic_vector(3 downto 0); i_op17 : in std_logic_vector(3 downto 0); -- second set op openrand i_op20 : in std_logic_vector(3 downto 0); i_op21 : in std_logic_vector(3 downto 0); i_op22 : in std_logic_vector(3 downto 0); i_op23 : in std_logic_vector(3 downto 0); i_op24 : in std_logic_vector(3 downto 0); i_op25 : in std_logic_vector(3 downto 0); i_op26 : in std_logic_vector(3 downto 0); i_op27 : in std_logic_vector(3 downto 0); -- output o_add0 : out std_logic_vector(4 downto 0); o_add1 : out std_logic_vector(4 downto 0); o_add2 : out std_logic_vector(4 downto 0); o_add3 : out std_logic_vector(4 downto 0); o_add4 : out std_logic_vector(4 downto 0); o_add5 : out std_logic_vector(4 downto 0); o_add6 : out std_logic_vector(4 downto 0); o_add7 : out std_logic_vector(4 downto 0)); end array_add8; architecture rtl of array_add8 is type t_array_op is array (0 to 7) of signed(4 downto 0); signal w_op1 : t_array_op; signal w_op2 : t_array_op; signal w_add : t_array_op; begin w_op1(0) <= to_signed(to_integer(signed(i_op10)),5); w_op1(1) <= to_signed(to_integer(signed(i_op11)),5); w_op1(2) <= to_signed(to_integer(signed(i_op12)),5); w_op1(3) <= to_signed(to_integer(signed(i_op13)),5); w_op1(4) <= to_signed(to_integer(signed(i_op14)),5); w_op1(5) <= to_signed(to_integer(signed(i_op15)),5); w_op1(6) <= to_signed(to_integer(signed(i_op16)),5); w_op1(7) <= to_signed(to_integer(signed(i_op17)),5); w_op2(0) <= to_signed(to_integer(signed(i_op20)),5); w_op2(1) <= to_signed(to_integer(signed(i_op21)),5); w_op2(2) <= to_signed(to_integer(signed(i_op22)),5); w_op2(3) <= to_signed(to_integer(signed(i_op23)),5); w_op2(4) <= to_signed(to_integer(signed(i_op24)),5); w_op2(5) <= to_signed(to_integer(signed(i_op25)),5); w_op2(6) <= to_signed(to_integer(signed(i_op26)),5); w_op2(7) <= to_signed(to_integer(signed(i_op27)),5); o_add0 <= std_logic_vector(w_add(0)); o_add1 <= std_logic_vector(w_add(1)); o_add2 <= std_logic_vector(w_add(2)); o_add3 <= std_logic_vector(w_add(3)); o_add4 <= std_logic_vector(w_add(4)); o_add5 <= std_logic_vector(w_add(5)); o_add6 <= std_logic_vector(w_add(6)); o_add7 <= std_logic_vector(w_add(7)); p_add : process(w_op1,w_op2) begin for k in 0 to 7 loop w_add(k) <= w_op1(k) + w_op2(k); end loop; end process p_add; end rtl;
Figure 4 shows the RTL view of the 8-adder implementation on Altera/Intel Cyclone II FPGA using the array approach.
Conclusion
As you saw in the previous example, using array allow you writing a very compact and elegant VHDL code. Moreover, using array approach you minimize the coding error since the VHDL code is more compact and simple to read and understand. After all these advantages, you should pay a great attention to your VHDL. Using the array coding style, you can fill a huge FPGA with only just few line of VHDL code! The rule is always the same:
Think hardware!
Every time you write a VHDL code, check the area report if it matches with your design intention and when you are not sure of the VHDL synthesis, check the RTL viewer and technology view.
Reference
[1] RTL HARDWARE DESIGN USING VHDL Coding for Efficiency, Portability, and Scalability
[2] VHDL Programming by Example 4th Ed Douglas – Perry
Hi.
How realize interleave array? Thanks.
Hi Valentin,
can you explain better your needs?
I have a 1D array. I need to get 2 arrays: 1st – indexes 0,2,4,…, 2nd – indexes 1,3,5,…
you can use a single array and use the index (2*i) and (2*i+1)
Hello,
Firstly thanks very much for your post and i found it very helpful.
My questions is my input is an array of 13X13 8 bit vector and i feel wrong to simply type 169 lines for my input.
Is there another way of easier definition in Entity regarding arrays as input or output?
Thanks in advance
Dongfang
You can use a package where define your input type. For instance:
type t_my_input_row is array(0 to 12) of std_logic_vector(7 downto 0);
type t_my_input is array(0 to 12) of t_my_input_row;
your input/output port could be
my_input : in t_my_input;
Any hint on how to develop a square root synthesizable design?
Thanks,
You can use a Cordic
In the first picture of the array example,
vector(2)<=42 is on the 4rd element with index 3.
Shouldn't it be 3rd element and index 2?
you are right, it is a typo, I’ll fix it
thank you
In the Implementation of a LUT using an array in VHDL, you have written
m1 <= std_logic_vector(to_unsigned(C_LUT1(to_integer(unsigned(s1))),4));
This statement I don't understand. Can you explain it more clearly?
the first type casting is “std_logic_vector” is related to m1 signal type
to_integer(unsigned(s1) is related to convert to integer the signal s1