Behavior of VHDL comparison operator with integer argument

J

Josh

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;

In my experience, StatementsA get executed, but not StatementsB.
Logically I would expect that both statements get executed since 0 and
1 are both less than 8. My theory is that 8 is interpreted as a 3 bit
number, and the top bit is being lost, creating the value of 0.

Questions
1. Is this the intended behavior of VHDL?
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?
3. What is a logical & safe practice to encourage in order to avoid
situations like this?
 
B

Brian Drummond

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
 
K

KJ

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;

In my experience, StatementsA get executed, but not StatementsB.

You might want to re-check this experience. The full code that I have posted at the end simply reports out when the condition branches are taken and produces the following result which shows that both branches are taken...which contradicts your statement.

Results obtained using Modelsim 6.4 (which does not support -2008...I guessreally should upgrade to something more recent here at home I suppose).

# vsim tb_unsigned
# Loading std.standard
# Loading ieee.std_logic_1164(body)
# Loading ieee.numeric_std(body)
# Loading work.tb_unsigned(rtl)
# ** Note: 0 <= 8
# Time: 0 ns Iteration: 0 Instance: /tb_unsigned
# ** Note: 1 <= 8
# Time: 0 ns Iteration: 0 Instance: /tb_unsigned
Logically I would expect that both statements get executed since 0 and
1 are both less than 8.

That would be what I would expect.
My theory is that 8 is interpreted as a 3 bit
number, and the top bit is being lost, creating the value of 0.

If you run my code below and get different results than what I posted here it suggests one of the following theories (listed from what I would rank asleast to most probable):

- A new definition of "<=" in the numeric_std library came out with VHDL-2008 and my code, when compiled with VHDL-2008, behaves differently.
- Your simulator has a bug, get a better one.
- You are mistaken. Perhaps your stripped down example didn't correctly represent what you had experienced leading to the incorrect theory. If so, you should go back to your original code that seemed to produce the unexpected result and analyze that (or post it here for review).

Kevin Jennings

---- START OF CODE ----
library ieee;
use ieee.numeric_std.all;
entity tb_unsigned is
end tb_unsigned;
architecture rtl of tb_unsigned is
begin
process
begin
if (to_unsigned(0, 3) <= 8) then
report "0 <= 8";
end if;
if (to_unsigned(1, 3) <= 8) then
report "1 <= 8";
end if;
wait;
end process;
end rtl;
---- END OF CODE ----
 
P

Paul Uiterlinden

KJ said:
If you run my code below and get different results than what I posted here
it suggests one of the following theories (listed from what I would rank
as least to most probable):

- A new definition of "<=" in the numeric_std library came out with
VHDL-2008 and my code, when compiled with VHDL-2008, behaves differently.

I don't have access to the source of the 2008 version of numeric_std. I just
tried your example in ModelSim 10.1, both in 1993 and 2008 mode. No
difference there. I see the same results as you see. Pfew.
 
J

Jim

Too cool. Good test case. This is not a VHDL-2008 change.

The following is from, IEEE Std 1076.3-1997 (Numeric_std) standard, sectionA.3.2:
"When a relation operator compares a SIGNED or UNSIGNED argument value withan INTEGER or NATURAL value, the function has the effect of converting theSIGNED or UNSIGNED argument to its equivalent universal integer value and then doing the corresponding comparison of integer values." ...

I note that std_logic_arith does not do it correctly - or does it the way the OP speculated.

Best Regards,
Jim
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top