VHDL makes heavy use of types, and has a concept of an unconstrained vector. This is a vector whose dimension isn’t specified in the declaration of a construct, but is determined by the instantiation or use of the construct.
Issues When Used With Functions
The most common place unconstrained vectors are used is in functions. Consider the following function:
function or_reduce (x : std_logic_vector) return std_logic is
variable tmp : std_logic := '0';
begin
for ii in x'range loop
tmp := tmp or x(ii);
end loop;
return tmp;
end function;
This is a common function, and this implementation will work for any length of std_logic_vector.
Issues with ‘low /= 0
One annoyance of unconstrained vectors is that they can’t be used to impose desirable properties, like the direction being “downto” or ‘low being 0. Thus the following function would not have worked correctly in all cases:
-- Bad code:
function or_reduce (x : std_logic_vector) return std_logic is
variable tmp : std_logic := '0';
begin
for ii in x'length-1 downto 0 loop
tmp := tmp or x(ii);
end loop;
return tmp;
end function;
In the above example, the user might call “or_reduce(data(31 downto 24))”. Doing this would result in x being a std_logic_vector(31 downto 24). The for loop would pick up the length as 8, and would then attempt to do “tmp := tmp or x(7)”. x(7) doesn’t exist in the vector x(31 downto 24) and thus this is an error.
Issues with ‘ascending = true
The next issue is the direction. Consider the following:
-- bad code:
function inner(x,y : std_logic_vector) return integer is
variable tmp : integer := 0;
begin
for ii in x'range loop
if x(ii) = y(ii) then
tmp := tmp + 1;
end if;
end loop;
return tmp;
end function;
The above code has the same issue that both x and y must have the same dimensions. But the interesting thing is what happens when it is called as “inner(x(7 downto 0), “11110000”)”. The constant value will have a dimension of (0 to 7). Thus the rightmost bit of x will be compared to the leftmost bit of the constant. This is probably not what the user intended.
Normalization
These same problems can be very annoying for multi-input functions, where it would be nice to assume all vectors have the same dimensions. This can be solved by making “normalized” variables that have the desired range, and giving these the values of the inputs.
... variable x_act : std_logic_vector(x'length-1 downto 0); ... x_act := x;
When the lengths of two vectors with different dimensions are the same, VHDL will map the ‘right and ‘left’s of the two vectors the same, as well as the bits in-between. Thus the vector x, a (0 to 3) = “1100” will map to the vector y, a (3 downto 0) as “1100”. y(y’right) = x(x’right), or:
- y(3) = x(0) = ‘1’
- y(2) = x(1) = ‘1’
- y(1) = x(2) = ‘0’.
- y(0) = x(3) = ‘0’.
Issues When Used With Ports
Unconstrained vectors can also be used for ports and generics of a component. This can be done as follows:
entity example is
generic(
COMP_VAL : std_logic_vector
);
port(
Same : out std_logic;
Different : out std_logic;
Din : in std_logic_vector;
Clk : in std_logic;
Rst : in std_logic
);
end entity;
There are several issues with using unconstrained vectors as ports. The above issues with dimensions is one reason to avoid them.
Further, without documentation, its not clear what sizes the inputs/outputs can be. This can become important if there is an intended size difference between the ports. As unconstrained vectors, this relationship is unclear.
Issues with Inputs
The first additional issue is that unconstrained inputs can’t have a good default value. “(others => ‘0’)” might be a desirable choice, but as an input, “others” has no dimensions. Thus the user must specify the input’s value for each instance. Other default values, like x”55″, can lead to issues because x”55″ will be defined as (0 to 7), which is unlikely to be the desired range in all cases.
Issues with Outputs
The next issue is that outputs must be connected to something other than “open”. Just as above “others” has no dimension. In both cases, it becomes vital, as the input or output vector, even when not intentionally used for data, might still be used in a ‘range, ‘length, or similar statement.
Finally, output ports can be very dangerous if made unconstrained. Consider the following:
-- Dangerous Code: out_pre <= x * y + z; -- infer dsp48 Dout <= std_logic_vector(out_pre(Dout'length-1 downto 0));
This code has the same range issues as explained above, but also has a VERY dangerous issue — the user has to get the correct length for Dout! If the user connects too small of a vector for Dout, the code will still synthesize, and connect the LSB’s of the multiply-add to Dout. This will not flag any errors or warnings, and may even get past some rudimentary sims, depending on if the MSB’s toggle often in the application.
Conclusions
For these reasons, it is important to pay very careful attention to directions and actual ranges (both ‘high and ‘low) of unconstrained vectors. In most cases, it is beneficial to avoid unconstrained vectors in port declarations. Functions should be carefully analyzed to ensure they either work with arbitrary dimensions, or provide useful error messages when dimensions are different that (W downto 0).
Pingback: VHDL Generics | cdstahl.org