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
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
This can of course be achieved through a
variable temp: boolean;
temp := false;
for i in isCounting'range loop
temp := temp OR isCounting(i);
atLeastOneIsCounting <= temp;
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:
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
-- 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
atLeastOneIsCounting <= (isCounting /= (isCounting'range => '0'));
will hence translate to
atLeastOneIsCounting <= (isCounting /= "00000000");
"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:
atLeastOneIsCounting <= (isCounting = (isCounting'range => '1'));
-- will compile
Why is this?
The reason that
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.
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 ("
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:
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
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
-- 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:
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,
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:
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).
<= symbol demands both sides to be of equal length. Therefore is the expression
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.:
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'),
- 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. ↩
- 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. ↩