flaw in to_signed() for big numbers?

K

Kevin Neilson

Signed types (using numeric_std) can have widths bigger than 32, but the
to_signed() function only takes integers as an argument, so it seems to
be very difficult to assign values to large signed constants. For
example, in my code I have:

constant round_const: signed(c'range) := to_signed(2**x-1,c'length);

This doesn't work if x==32, because 2**32-1 is outside the range of an
integer (which must be <=(2**31-1)). I can't use a real as an argument,
and I can't convert a real to a signed without converting it to an
integer first, and then I have the same problem.

A workaround, for this case, seems to be:

constant round_const: signed(c'range):= (x-1 downto 0=>'1',others=>'0');

But that is weak.
-Kevin
 
M

Mike Treseler

Kevin said:
Signed types (using numeric_std) can have widths bigger than 32, but the
to_signed() function only takes integers as an argument, so it seems to
be very difficult to assign values to large signed constants.

32 bit ints are annoying but vector
hex constants work for any value.
my_vec_v := x"123456789abc" ;

I use python to do the conversions



-- Mike Treseler
 
K

kennheinrich

32 bit ints are annoying but vector
hex constants work for any value.
my_vec_v := x"123456789abc" ;

I use python to do the conversions


-- Mike Treseler

A few other random thoughts... you could extend Mike's idea to express
a decimal number directly using a string, writing a conversion
function yourself. Then you don't need to leave the VHDL world at all.

function str_to_signed ( s : string; width : integer) return signed;
....
constant x := str_to_signed("1000000000", 64);

But you still need to manually evaluate "2**x - 1" which means you
still lose the ability to make your input argument dependent on a
generic. Perhaps just writing an integer exponentiation of signed
would help? Easy and fast when you have the binary representation of
the exponent, using the "signed" multiply.

function "**" ( x : signed; y : integer) return signed; -- compute x
** y
....
constant round_const: signed(c'range) := (to_signed(2,c'range))**x-1;

For your example (2^n) you might be able to use just a simple shift to
begin with. But your point is well taken - bignums in the LRM would be
nice :)

- Kenn
 
R

Rob

Going slightly off at a tangent here,
Is the problem with precision a problem with the VHDL standard or is
it just the simulator? Presumably a simulator could allow an
unconstrained integer to be of arbitrary size (like in Python).
I know that XST will implement an unconstrained integer as a 32bit
vector, but what says that it should be done like that?
 
R

Rob

VHDL standard requires integers to support a range of at least
max = 2**31 - 1, min = -(2**31 - 1). Note that this does NOT
include the most negative possible 2s complement number, -(2**31).
I think this was done to maximise portability across platforms;
in the days when VHDL was first designed, there were still
computers around that used sign-and-magnitude arithmetic.


I believe it could. But if you were to exploit that, you
could end up creating code that would not work correctly
in a different but nevertheless standards-compliant simulator.
If a standard specifies a minimum level of something, it's
usually wise to restrict your own code to stay within that
minimum level.


IEEE Std.1076. What higher authority could there be? :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.

I know it's not very constructive to moan about these things, but
people have been having problems with this 32bit issue for years
(among other things). I do wish the evolution of the VHDL standard
wasn't so painfully slow.
 
K

Kevin Neilson

A few other random thoughts... you could extend Mike's idea to express
a decimal number directly using a string, writing a conversion
function yourself. Then you don't need to leave the VHDL world at all.

function str_to_signed ( s : string; width : integer) return signed;
...
constant x := str_to_signed("1000000000", 64);

- Kenn

I suppose I could make my own real_to_signed function and pass large
integers to it as reals. If the real is >= 2^31, I could split the real
into two parts, convert them to signeds and concatenate them. Not sure
if this would work. I'd probably also still be constrained by the
54-bit mantissa of a double, but that's a lot better than 32, a number
which is regularly exceeded when using the 48-bit Xilinx DSP48 accumulator.
-Kevin
 
K

Kevin Neilson

Jonathan said:
VHDL standard requires integers to support a range of at least
max = 2**31 - 1, min = -(2**31 - 1). Note that this does NOT
include the most negative possible 2s complement number, -(2**31).

This has bitten me before too, causing an error that took a long time to
track until I found this fact in a textbook.

To be fair, I've encountered similar problems in Verilog, particularly,
as I recall, in using the $itor (integer to real) function for integers
larger than 2^32.
-Kevin
 
K

kennheinrich

I suppose I could make my own real_to_signed function and pass large
integers to it as reals. If the real is >= 2^31, I could split the real
into two parts, convert them to signeds and concatenate them. Not sure
if this would work. I'd probably also still be constrained by the
54-bit mantissa of a double, but that's a lot better than 32, a number
which is regularly exceeded when using the 48-bit Xilinx DSP48 accumulator.
-Kevin

As ugly as sin, but it seems to work in simulation. I don't think
there are any fundamental width limitations. Modelsim is nice enough
to show you a decimal int greater than 32 bits (for validation) when
you choose 'decimal' radix in the waveform viewer.

- Kenn

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

package string_signed is
function str_to_signed (
s : string;
width : natural)
return signed ;
end string_signed;

package body string_signed is
function str_to_signed (
s : string;
width : natural)
return signed is
type t is array ('0' to '9') of integer;
constant smap : t := ( '0' => 0, '1' => 1, '2' => 2, '3' => 3,
'4' => 4, '5' => 5, '6' => 6, '7' => 7,
'8' => 8, '9' => 9);

variable v : signed(width-1 downto 0) := to_signed(0, width);
variable z : signed(2*width-1 downto 0) := to_signed(0, 2*width);
begin -- str_to_signed
assert s'length > 0 report "bad string" severity failure;
v := to_signed(smap(s(s'right)), width);
if s'length = 1 then
return v;
else
z := 10 * str_to_signed(s(s'left to s'right-1), width) + v;
assert z(z'left)='0'
and (z(z'left downto width+1) = z(z'left - 1 downto width))
report "ovfl";
return z(width-1 downto 0);
end if;
end str_to_signed;

end string_signed;

library IEEE;
use IEEE.numeric_std.all;
use work.string_signed.all;
entity e is
end e;

architecture a of e is

signal foo : signed(63 downto 0);
begin -- a

foo <= str_to_signed("123456789012345", 64);

end a;
 
K

KJ

Kevin Neilson said:
Signed types (using numeric_std) can have widths bigger than 32, but the
to_signed() function only takes integers as an argument, so it seems to be
very difficult to assign values to large signed constants. For example,
in my code I have:

constant round_const: signed(c'range) := to_signed(2**x-1,c'length);

This doesn't work if x==32, because 2**32-1 is outside the range of an
integer (which must be <=(2**31-1)). I can't use a real as an argument,
and I can't convert a real to a signed without converting it to an integer
first, and then I have the same problem.

A workaround, for this case, seems to be:

constant round_const: signed(c'range):= (x-1 downto 0=>'1',others=>'0');

1. It's not that hard to write a function to compute a power when the
exponent is not a fraction. Something along the lines of the following is a
start

function pow(b, e: unsigned) return unsigned is
begin
if (to_integer(e) = 0) then
return(1);
elsif (to_integer(e) = 1) then
return(b);
else
return(b * pow(b, e-1);
end if'
end function pow;

1a. Perhaps override "**" to do this if you feel like it.

2. Then, using the synthesizable log2 function, give some more thought to
computing the proper number of bits for the return result and modify the
above mentioned starter function. That way 2**4 (when represented as
unsigned) will come out as 5 bits rather than 12.

2a. Hint for #2, a function to compute the ceil(log2(x)) would likely be a
handy function (perhaps ceil_log2(x)) which returns the smallest unsigned
that is greater than or equal to log2(x) (i.e. returns the conventional
'ceiling' math function).

3. Now you can say
constant round_const: signed(c'range) := pow("0010", "0100") - 1;
constant round_const: signed(c'range) := pow(x"2", x"4") - 1;

Remembering that hard coded constants (like 2 and 4 in this example; 2 and
32 in the OP example) can generally also be represented as generics and that
generics can be signed/unsigned of arbitrary width as well as integers one
is well on their way to working strictly with signed/unsigned directly
instead of doing the math in integers which is the source of the problem
here.

Kevin Jennings
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top