Search code examples
arraysconcatenationvhdlarray-indexing

Initializing arrays in VHDL: How exactly does it work?


Consider the following VHDL code:

constant c : std_logic_vector(7 downto 0) := (7 downto 6 => '1', others => '0');

Here, the indices 7 and 6 are important: they indicate which elements should be 1, and from that, the compiler can derive which ones should be 0.

Question 1

If I understand correctly, this can also be written as

constant c : std_logic_vector(7 downto 0) := (7 downto 6 => '1') & (5 downto 0 => '0');

Now I am wondering how exactly this works, and whether the indices still matter or only the difference between them. The way I understand it, the expression (x downto y => z) creates an array with indices x downto y, whose elements are all equal to z (and have the same type). But do these indices matter when arrays are concatenated? I.e., could I also write any of the following:

constant c : std_logic_vector(7 downto 0) := (1 downto 0 => '1') & (5 downto 0 => '0');
constant c : std_logic_vector(7 downto 0) := (99 downto 98 => '1') & (4 downto -1 => '0');

Of course one could say that those statements, esp. the last one, aren't really clear, but my question is: Would they have the same result? Or would they cause some kind or error?

The reason I am wondering about this is that you could also write

constant c : std_logic_vector(7 downto 0) := "11" & "000000";

and the literal "11" doesn't have any range specified, so I'm not sure what exactly the type of this literal would be, but certainly not std_logic_vector(7 downto 6).

Question 2

Finally, I am wondering what is the difference between

constant c : std_logic_vector(7 downto 0) := (7 downto 6 => '1') & (5 downto 0 => '0');

and

constant c : std_logic_vector(7 downto 0) := (7 downto 6 => '1', 5 downto 0 => '0');

By this I mean: Is there any reason you would want/need to write the former (which seems more error-prone because it will give unexpected results if you e.g. swap the 2 aggregates) instead of the latter? Or is it just a matter of taste?


Solution

    1. Lets start with literals. When you specify a literal, the default range is (type_index'low to type_index'low + len-1). The easiest way to see this is when specifying a constant, where the range is not required in the subtype:
    constant SOME_CONSTANT : std_logic_vector := x"00";
    

    Here, if you check the range of SOME_CONSTANT, you'll see its (0 to 7). This is because std_logic_vector is declared as:

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

    The 'low value of natural is 0, which is the start point.

    Lets say you went a bit crazy, and this:

    type my_crazy_array_t is array(std_logic range <>) of integer;
    

    and you declared a constant:

    constant CRAZY : my_crazy_array_t := (0, 22);
    

    The results are:

    CRAZY('U') = 0
    CRAZY('X') = 22
    CRAZY'left = 'U'
    CRAZY'right = 'X'
    CRAZY'low = 'U'
    CRAZY'high = 'X'
    

    Because 'U' is the low value of std_logic, followed by 'X'.

    On the other questions, it all works because VHDL is always context driven, and for arrays as long as the lengths match, the resulting arrays can always be mapped from 'left to 'right. In your examples the length is correct, and the subtype is know from the declaration, so mapping can occur as you specified.

    NOTE: (4 downto -1 => '0'); is illegal, because -1 is not a valid index value for a std_logic_vector, so you would get an error (probably from the & function because it cannot work out what type you meant, because it cannot be std_logic_vector).

    1. The difference is that the first one is a concatenation of a 2 bit and 6 bit bit string, and the 2nd is a complete 8 bit bit string. Which you use will probably be a matter of style. But usually, for this, I would use literals eg: constant c : std_logic_vector(7 downto 0) := x"C0"; what you use and when may depend on what is clearest to the user(s).