2
\$\begingroup\$

The question is pretty simple: is it possible to declare an empty array in VHDL?

Here is my problem: I am trying to write a module(for an exercise in Peter Ashenden's book), that will find the biggest integer in an unconstrained array of integers and detect empty arrays:

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

My module looks like this:

entity maximum is
    port ( vector_in : in int_set;
           data_out : out integer;
           valid_in : out std_ulogic ); -- will be false, when vector_in is empty
end entity maximum;

-- behavioral body
architecture behav of maximum is
begin
    maximum_behavioral : process ( vector_in ) is
        variable temp_max : integer := integer'low;
    begin
        -- reset temp variable
        temp_max := integer'low;
        -- test for empty array
        if vector_in'length = 0 then
            valid_in <= '0';
        else
            for i in vector_in'range loop
                if vector_in(i) > temp_max then
                    temp_max := vector_in(i);
                end if;
            end loop;
            data_out <= temp_max;
            valid_in <= '1';
        end if;
    end process maximum_behavioral;
end architecture behav;

Now in my test bench I have a number of arrays/sets to test my module:

entity int_max_tb is
end entity int_max_tb;

architecture test_int_max of int_max_tb is
    signal data_in : int_set(0 to 4);
    signal data_out : integer;
    signal valid_in : std_ulogic;
begin
    max_dut : entity work.maximum(behav)
        port map ( data_in, data_out, valid_in );

    stimulus : process is
        -- test arrays/sets
        constant set1 : int_set := (4, 5, 6, 5, 3);
        constant set2 : int_set := (7, 6, 5, 10, 4);
        constant set3 : int_set := (-7, -6, -5, -10, -4);
    begin
        data_in <= set1; wait for 10 ns;
        data_in <= set2; wait for 10 ns;
        data_in <= set3; wait for 10 ns;
        wait;
    end process stimulus;
end architecture test_int_max;

So far, this works fine and yields the results I expect. But now I want to test it against an empty array. Adding something along the lines of

constant set4 : int_set(0 to 4);
-- and later on
data_in <= set4;

won't work obviously, since a constant needs to be initialized with some values. Changing constant to variable will work, but the result will be an array with five elements each of the value integer'low - again, this is expected behavior.

Specifically, the exercise asks: How should the module behave if the actual array associated with the input port is empty (i.e., of zero length)?

From this statement, I assume that it is possible to declare(or have some code produce) an empty array. But the answer to this could also simply be, that a module does not need to detect an empty array since once an object of an unconstrained array is declared, I need to provide constraints specifying the index bounds of that object(and will assign default values to the elements if unspecified).

From this question I infer that it is possible to have zero-length arrays if one uses generics(which I am not, so far).

So in conclusion: Can I declare an empty/zero-length array in VHDL(without generics) or is it simply not possible(which I am thinking right now, taken the arguments from the two paragraphs right above this one)?

\$\endgroup\$
5
\$\begingroup\$

You don't need to declare int_set, use integer_vector instead. You can use 'length to test for an empty array.

An empty array is called a null array, because it has a null range. A null range and thus a null array can be constructed by using e.g. 0 downto 1. Alternatively, an ascending direction can be used (1 to 0) can be used.

5.2.1 General
[...]
A range specifies a subset of values of a scalar type. A range is said to be a null range if the specified subset is empty.

The range L to R is called an ascending range; if L > R, then the range is a null range. The range L downto R is called a descending range; if L < R, then the range is a null range. L is called the left bound of the range, and R is called the right bound of the range. The lower bound of a range is the left bound if the range is ascending or the right bound if the range is descending. The upper bound of a range is the right bound if the range is ascending or the left bound if the range is descending. The value V is said to belong to the range if the relations (lower bound <= V) and (V <= upper bound) are both true. The operators >, <, and <= in the preceding definitions are the predefined operators of the applicable scalar type.
[...]

5.3.2.2 Index constraints and discrete ranges
[...]
An array constraint of the first form is compatible with the type if, and only if, the constraint defined by each discrete range is compatible with the corresponding index subtype and the array element constraint, if present, is compatible with the element subtype of the type. If any of the discrete ranges defines a null range, any array thus constrained is a null array, having no elements. An array value satisfies an index constraint if at each index position the array value and the index constraint have the same index range. (Note, however, that assignment and certain other operations on arrays involve an implicit subtype conversion.)
[...]

8.5 Slice names
[...]
The bounds of the discrete range define those of the slice and shall be of the type of the index of the array. The slice is a null slice if the discrete range is a null range. It is an error if the direction of the discrete range is not the same as that of the index range of the array denoted by the prefix of the slice name.

For the evaluation of a name that is a slice, the prefix and the discrete range are evaluated. It is an error if either of the bounds of the discrete range does not belong to the index range of the prefixing array, unless the slice is a null slice. (The bounds of a null slice need not belong to the subtype of the index.)
[...]

Such a null array can be initialized/assigned with (others => ...).

Example for your integer vector:

constant Empty : integer_vector(0 downto 1) := (others => 0);

Btw: VHDL already defines a function 'maximum' that calculates the biggest integer in an array.

\$\endgroup\$
  • \$\begingroup\$ Very thorough, thank you! The exercise is for learning purposes, that's why I am writing my own module. That's also why I am using my own array declaration instead of "built-ins". But using constant empty : int_set(1 to 0) := (others => 0); did the trick. Thank you again! \$\endgroup\$ – georgjz Feb 21 '18 at 21:19
  • \$\begingroup\$ Writing your own entity as a training is a good thing, but you should not reinvent existing types, because you create incompatible types to existing types. VHDL matches types based on names and not based on structure like C. \$\endgroup\$ – Paebbels Feb 21 '18 at 23:04
  • \$\begingroup\$ Also see 5.3.2.1 para 6 An unbounded array definition...For each object that has the array type, the number of indices, the type and position of each index, and the subtype of the elements are as in the type definition. The index subtype for a given index position is, by definition, the subtype denoted by the type mark of the corresponding index subtype definition. The values of the left and right bounds of each index range are not defined, but shall belong to the corresponding index subtype;... (natural for integer_vector (16.3)), shall has mandatory weight. Some tools might not have a bug. \$\endgroup\$ – user8352 Feb 21 '18 at 23:20
  • \$\begingroup\$ @user8352 Oh you are right. The bounds are only allowed to not match the bounds in a null slice but not in a null array. \$\endgroup\$ – Paebbels Feb 22 '18 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.