0
\$\begingroup\$

In the architecture of some VHDL entity I have something like this:

type some_array is array(integer range <>, integer range <>) of std_logic;
signal foo : some_array(0 to 3, 0 to 3);

(...)

some_loop: for i in 0 to 3 generate
    something(...) <= foo(i-1, i-1) when (i > 0) else '0';
end generate some_loop;

I expected this to work fine, since foo is only indexed when i > 0. Unfortunately, it seems that VHDL / ghdl evaluates the i-1 even when i > 0, because when I run the testbed for this entity ghdl complains: ghdl:error: overflow detected.

Is this something to be expected? What's the best solution / workaround?

\$\endgroup\$
  • \$\begingroup\$ Is this a Quartus question? Quartus checks ranges and indices independen of the 'control flow'. To cirumway this, use min and max functions to adjust indices in corner cases / not taken nranches. \$\endgroup\$ – Paebbels Oct 8 '15 at 7:26
2
\$\begingroup\$

This is to be expected since i-1 is potentially outside the index range for the array. Unfortunately you have to guard this case separately, the usual way to do it would be with an if ... generate statement.

some_loop: for i in 0 to 3 generate
    if i = 0 generate 
       ... 
    end generate;
    if i > 0 generate 
       ... 
    end generate;
end generate some_loop;

A limitation of the if ... generate construct is that (before VHDL-2008) there is no else part. So you either need the unfortunately clumsy form above, or update to the latest ghdl release (0.33) for its much improved VHDL-2008 support, which allows you to replace the second if ... generate with an else.

If you are assigning every element of some array or record called something simultaneously, you may find this alternative style simpler and cleaner:

something(0) <= '0';
some_loop: for i in 1 to 3 generate
    something(i) <= foo(i-1, i-1);
end generate some_loop;
\$\endgroup\$
1
\$\begingroup\$

There is a requirement in the standard that an indexed name represents an element of array it indexes. It's a matter of where you apply this requirement, and because generate statements are elaborated in the question's case as three block statements containing three processes) that checking can't be applied until after elaboration.

If you were to express the architecture as the fully elaborated equivalent:

architecture fee of foo is

    type some_array is array(integer range <>, integer range <>) of std_logic;
    signal foo : some_array(0 to 3, 0 to 3);
    signal something: some_array (0 to 3, 0 to 3);
begin
    some_loop_0:
        block
            constant i: integer := 0;
        begin
            process
            begin
                if i > 0 then
                    something (0,0) <= foo (i-1,i-1) ;
                else
                    something (0,0) <= '0';
                end if;
                wait on foo;  -- i is an object class constant not signal
            end process;
        end block;

    some_loop_1:
        block
            constant i: integer := 1;
        begin
            process
            begin
                if i > 0 then
                    something (1,1) <= foo (i-1,i-1) ;
                else
                    something (1,1) <= '0';
                end if;
                wait on foo;
            end process;
        end block;

    some_loop_2:
        block
            constant i: integer := 2;
        begin
            process
            begin
                if i > 0 then
                    something (2,2) <= foo (i-1,i-1) ;
                else
                    something (2,2) <= '0';
                end if;
                wait on foo;
            end process;
        end block;
end architecture;

You'd find that you get analysis time errors instead claiming the static constant violates the bounds of foo. (In some_loop_0).

Determining the value of an indexed name is a basic operation.

See IEEE Std 1076-2008, 8.4 Indexed names:

An indexed name denotes an element of an array.

indexed_name ::= prefix ( expression { , expression } )

The prefix of an indexed name shall be appropriate for an array type. The expressions specify the index values for the element; there shall be one such expression for each index position of the array, and each expression shall be of the type of the corresponding index. For the evaluation of an indexed name, the prefix and the expressions are evaluated. It is an error if an index value does not belong to the range of the corresponding index range of the array.

Note the requirement the index value belongs to the corresponding index range (the subtype constraint) of the array is during evaluation while there's a separate requirement that the index expression belong to the index type (which it does in both architectures):

library ieee;
use ieee.std_logic_1164.all;

entity foo is
end entity;

architecture fum of foo is

    type some_array is array(integer range <>, integer range <>) of std_logic;
    signal foo : some_array(0 to 3, 0 to 3);
    signal something: some_array (0 to 3, 0 to 3);
begin
some_loop: for i in 0 to 3 generate
        something(i,i) <= foo(i-1, i-1) when (i > 0) else '0';
    end generate some_loop;
end architecture;

(and this architecture fails when run).

The question becomes when is an expression evaluated?

Well, we know an expression can be evaluated during elaboration - 14.4 Elaboration of a declarative part, 14.4.1 General, para 3:

In certain cases, the elaboration of a declarative item involves the evaluation of expressions that appear within the declarative item. ...

There's also locally static expressions and globally static expressions. See 9.4 Static expressions:

Certain expressions are said to be static. Similarly, certain discrete ranges are said to be static, and the type marks of certain subtypes are said to denote static subtypes. There are two categories of static expression. Certain forms of expression can be evaluated during the analysis of the design unit in which they appear; such an expression is said to be locally static. Certain forms of expression can be evaluated as soon as the design hierarchy in which they appear is elaborated; such an expression is said to be globally static.

In the second architecture with the processes the slice name is locally static (9.4.2 Locally static primaries, j) "A type conversion whose type mark denotes a locally static subtype and whose expression is a locally static expression" and n) "An indexed name whose prefix is a locally static primary and whose index expressions are all locally static expressions").

If you were to explore a bit (9.4.3 Globally static primaries) you'd find that the first architecture use is globally static, depending on the elaboration of the generate statement.

Eh, can be is enabling, it isn't mandatory. It's also implementation dependent. There's reasons why you would do so. More compact code and potentially faster execution with immediate values that aren't calculated.

In this case if you wait until execution to evaluate the condition in that equivalent if statement before evaluating the expression in the indexed name both architectures will simulate successfully:

I expected this to work fine, since foo is only indexed when i > 0.

I happen to know of a VHDL implementation that runs both architectures successfully.

What is the consequence of code that doesn't function the same way on all VHDL implementations?

See Annex D (informative) Potentially nonportable constructs:

This annex lists those VHDL constructs whose use may result in nonportable descriptions.

A description is considered portable if it

    a) Compiles, elaborates, initializes, and simulates to termination of the simulation cycle on all conformant implementations, and

    b) The time-variant state of all signals and variables in the description are the same at all times during the simulation,

under the condition that the same stimuli are applied at the same times to the description. The stimuli applied to a model include the values supplied to generics and ports at the root of the design hierarchy of the model, if any.

The design specification with constants (it's called a generate constant) used to determine an offset which is out of index range isn't portable. (You could also note it's not explicitly listed as a non-portable construct in Annex D).

Both of Brian's answer's solutions are perfectly valid. They prevent an index from being evaluated out of range. His answer tells you what. This one explains why.

Is this something to be expected?

It depends on the VHDL tool implementation, it's a nonportable construct.

What's the best solution / workaround?

(Doctor it hurts when I do this.) Do what ever it takes to avoid generating indexes outside the index constraint of the array.

Brian's examples show two ways. Your code example isn't elaborate enough to need a generate statement.

Both Brian's solutions save you no lines of code and little complexity, all solutions require someone pay attention to either what analyzes, elaborates and simulates for a particular tool implementation, or managing the consequences of the semantics of indexed names.

('Don't do that'.)

\$\endgroup\$

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.