Say you have eight counters, each giving a status bit telling if it is actively counting or not, and that these status bits are brought together into a bit_vector called isCounting:

signal isCounting: bit_vector(7 downto 0);

Typically, you might want to perform bit operations on such a vector, like OR-ing or AND-ing all the bits, to see if any counter or all counters, respectively, are counting. Assume we want to OR the bits of isCounting into signal atLeastOneIsCounting:

OR into isCounting

This can of course be achieved through a process, e.g.:

-- OR all bits of isCounting:
process(isCounting)
  variable temp: boolean;
begin
  temp := false;
  for i in isCounting'range loop
    temp := temp OR isCounting(i);
  end loop;
  atLeastOneIsCounting <= temp;
end process;

But the code would be more readable if we could do it through one single concurrent statement instead of having to use sequential VHDL. (The resulting logic of a synthesis tool would be the same, of course.)

To do that, some might try this:

atLeastOneIsCounting <= (isCounting /= (others => '0')); -- won't compile

which however won't compile since the compiler won't be able to build a vector out of (others => '0') (see further explanation below). Referencing or assigning parts of an array as we do through (others => '0') is called an aggregate expression.

This could be solved by specifying the range (7 downto 0) but this is considered bad coding practice by me 1 and we should use isCounting'range instead:

atLeastOneIsCounting <= (isCounting /= (isCounting'range => '0'));
-- will compile

This is the preferred method of OR-ing all the bits of the isCounting vector. This is what is happening:

  • (isCounting'range => '0') will translate to "00000000", and
  • atLeastOneIsCounting <= (isCounting /= (isCounting'range => '0'));
    will hence translate to
    atLeastOneIsCounting <= (isCounting /= "00000000");

(where "00000000" is a binary string literal 2). Obviously this is equivalent to OR-ing all the bits of the vector.

If we instead want to AND the bits, we just do:

-- AND the bits of isCounting:
atLeastOneIsCounting <= (isCounting = (isCounting'range => '1'));
-- will compile

 

Why is this?

The reason that

atLeastOneIsCounting <= (isCounting /= (others => '0')); -- won't compile

is illegal is that the compiler won't be able to interpret the (others => '0') part of (isCounting /= (others => '0').

Remember that the unequal sign /= is a an operator which is overloaded for different data types (e.g. integer, signed, unsigned, or in this case bit_vector). An operator is very similar to a function - the main difference is that it is called with infix syntax ("a /= b") as opposed to prefix syntax ("unequal(a,b)").

Since we're using the unequal operator with two bit_vector data types, the compiler will look-up the right definition of the unequal operator and find:

function "/="(l, r : bit_vector) return boolean

Notice that the two operands are of type bit_vector and that bit_vector is defined as an unconstrained array ([...] is array (natural range <>)[...]). This means generally that e.g. functions (or operators) with bit_vector as data type for their arguments (or operands) can be called with arrays of any length - the length is determined by the actual length of the argument passed when the function is called.

It also means that the definition of the /= operand allows it to be called with two operands of different lengths. Hence, our

atLeastOneIsCounting <= (isCounting /= (others => '0')); -- won't compile

won't compile since (others => '0') doesn't represent a vector of defined length - the length of isCounting is known, but the right operand is allowed to have a different length.

I solved it by actually building the right operand to the bit_vector "00000000":

atLeastOneIsCounting <= (isCounting = (isCounting'range => '1'));
-- will compile

but there is an option that works just as well - namely, using a qualified expression to denote the data type and then using the (others => ...) aggregate expression. In that case we need that data type defined however, so it is defined on the first line below, after that comes the actual expression:

subtype TcounterStatus is bit_vector(7 downto 0); -- define data type
[...]
atLeastOneIsCounting <= (isCounting /= TcounterStatus'(others => '0'));
-- the above line will compile

The most common use of qualified expressions is to attach a data type to a string literal. If I make a function call with a string literal as argument,

a = myFun("10"); -- won't compile

and there is one function myFun with a bit_vector as argument, and one with string as argument, the compiler won't know which function to call since it doesn't know if "10" refers to a string or a bit_vector - I must qualify my string literal with the desired data type like this:

a = myFun(string'"10"); -- will compile

We're basically doing the same thing when we use a qualified expression to solve our first problem - TcounterStatus'(others => '0').

OK, is it the same for concurrent assigment (<=)?

Actually, it is not! The <= is a built-in symbol, it's not and operator (and therefore it can't be overloaded either).

The <= symbol demands both sides to be of equal length. Therefore is the expression

isCounting <= (others => '0'); -- will compile

perfectly legal VHDL, and I assume the above is very familiar to most VHDL coders. Although the right hand side is unconstrained, the compiler knows that the length of the right hand side must be equal to that of the left hand side, which is known - and therefore this construct is legal.

The same goes for the variable assignment symbol, :=, and also for constant value assignments, initial value assignment and port assignments, e.g.:

-- all of the following constructs are legal:

constant isCounting: bit_vector(7 downto 0) := (others => '0');

[...]

signal isCounting: bit_vector(7 downto 0) := (others => '0');

[...]

inst_cnt: entity work.counter port map(
  D => (others => '0'),
  [...]
  );

Notes:

  1. Code should be parameterized and constants (like 7, defining the highest index of the isCounting vector) shouldn't be repeated. Code shouldn't rely on constants having some specific value, changing constants should only have to be done in one place.
  2. in code, the string literal "00000000" can't be written just like that since its data type is ambiguous - we must use a qualified expression and write e.g. bit_vector'"00000000". Qualified expressions are briefly described further down.