Consider the following example, compiling with VHDL-2008 support.
library ieee;
use ieee.numeric_std.all;
...
if (to_unsigned(0, 3) <= 8) then
StatementsA
end if;
if (to_unsigned(1, 3) <= 8) then
StatementsB
end if;
When I see
if (some cond) then
I suspect someone who learned C first. Those parentheses are unnecessary
(but harmless).
My theory is that 8 is interpreted as a 3 bit
number, and the top bit is being lost, creating the value of 0.
Yes.
Questions
1. Is this the intended behavior of VHDL?
The VHDL language is not responsible for this; the library you are using
(numeric_std) is. There is nothing hidden there, you can answer the
question yourself by looking at the source for the <= function with
(unsigned, int) arguments.
2. What is the logic/reasoning
behind this? Why wouldn't the size of the comparison automatically be
resized to the required size of the integer?
Automatically changing word sizes or automatic type conversions increase
the scope for unexpected behaviour. That certainly shouldn't be a
property of the language! It should do what you write according to well
defined rules.
In this case, the LHS is an Unsigned with a well defined size. Maybe you
had good reasons for wanting a 3-bit comparator. Presumably you did,
since you made the Unsigned 3 bits wide. It shouldn't create hardware you
didn't ask for!
Instead there should be scope for adding such behaviour where you want it
intentionally, perhaps through a library of functions, like numeric_std,
but with different behaviour. I believe that library would turn out to be
much less generally useful, but YMMV.
3. What is a logical & safe practice to encourage in order to avoid
situations like this?
Completely avoid? I don't know of one.
Good practice? A few things can help.
Sanitize inputs - Check assumptions. Presuming the above example was an
unfortunate meeting of quantities from different sources (e.g. a=3 in
your design, b=8 was supplied as an input)
assert b >= 0 and b < 2**a report "Input data b out of range"
severity failure;
Now you can be sure that the input is valid.
Use the type system, don't fight it...
-- in a package somewhere...
constant Word_width : integer := 3;
subtype Word_type is unsigned (Word_width - 1 downto 0);
subtype Num_type is natural range 0 to 2**Word_width - 1;
-- in your design
signal a : Word_type := to_unsigned(0,Word_width);
signal b : Num_type := 8; -- compiler catches the error here
--before you even start simulating!
if a <= b then ...
Writing like this, with everything relevant derived from Word_width, is a
bit more work in a small design. But probably less work, by the time you
have eliminated 100 occurrences of "std_logic_vector(31 downto 0)"
And when you realise you really needed that 4th bit, just change
Word_width and everything else is still correct.
Use procedures and functions. They can be local to a specific process or
even to another procedure. Or hidden in a package. You wrote the same
comparison twice, giving 2 identical pieces of complex behaviour to
modify.
function OK(natural a; natural b) return boolean is
begin
return to_unsigned(a, 3) <= b;
end function OK;
if OK(a,b) then ...
Now when you have to change the behaviour (perhaps testing b for valid
range with an assert) you only have to change it once.
And others, no doubt.
- Brian