Make some effort to preserve bits while

operating on constant values.
This commit is contained in:
steve 2003-04-14 03:40:21 +00:00
parent 561a268c9c
commit bab0924d86
5 changed files with 117 additions and 78 deletions

18
eval.cc
View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: eval.cc,v 1.34 2003/03/26 06:16:18 steve Exp $"
#ident "$Id: eval.cc,v 1.35 2003/04/14 03:40:21 steve Exp $"
#endif
# include "config.h"
@ -47,9 +47,7 @@ verinum* PEBinary::eval_const(const Design*des, const NetScope*scope) const
switch (op_) {
case '+': {
if (l->is_defined() && r->is_defined()) {
long lv = l->as_long();
long rv = r->as_long();
res = new verinum(lv+rv, l->len());
res = new verinum(*l + *r);
} else {
res = new verinum(verinum::Vx, l->len());
}
@ -57,9 +55,7 @@ verinum* PEBinary::eval_const(const Design*des, const NetScope*scope) const
}
case '-': {
if (l->is_defined() && r->is_defined()) {
long lv = l->as_long();
long rv = r->as_long();
res = new verinum(lv-rv, l->len());
res = new verinum(*l - *r);
} else {
res = new verinum(verinum::Vx, l->len());
}
@ -67,9 +63,7 @@ verinum* PEBinary::eval_const(const Design*des, const NetScope*scope) const
}
case '*': {
if (l->is_defined() && r->is_defined()) {
long lv = l->as_long();
long rv = r->as_long();
res = new verinum(lv * rv, l->len());
res = new verinum(*l * *r);
} else {
res = new verinum(verinum::Vx, l->len());
}
@ -247,6 +241,10 @@ verinum* PEUnary::eval_const(const Design*des, const NetScope*scope) const
/*
* $Log: eval.cc,v $
* Revision 1.35 2003/04/14 03:40:21 steve
* Make some effort to preserve bits while
* operating on constant values.
*
* Revision 1.34 2003/03/26 06:16:18 steve
* Evaluate > and < in constant expressions.
*

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: eval_tree.cc,v 1.49 2003/03/15 18:07:58 steve Exp $"
#ident "$Id: eval_tree.cc,v 1.50 2003/04/14 03:40:21 steve Exp $"
#endif
# include "config.h"
@ -706,19 +706,26 @@ NetEConst* NetEBShift::eval_tree()
verinum rv = re->value();
verinum lv = le->value();
/* Calculate the width of the result. If it is not fixed, then
get it from the left operand. */
/* Make an early estimate of the expression width. */
unsigned wid = expr_width();
if (wid == 0)
wid = left_->expr_width();
if (rv.is_defined()) {
unsigned shift = rv.as_ulong();
if ((wid == 0) || ! lv.has_len()) {
/* If the caller doesn't care what the width is,
then calcuate a width from the trimmed left
expression, plus the shift. This avoids
data loss. */
lv = trim_vnum(lv);
wid = lv.len();
if (op() == 'l')
wid = lv.len() + shift;
}
assert(wid);
verinum nv (verinum::V0, wid);
verinum nv (verinum::V0, wid, lv.has_len());
if (op() == 'r') {
unsigned cnt = wid;
@ -747,7 +754,9 @@ NetEConst* NetEBShift::eval_tree()
res = new NetEConst(nv);
} else {
assert(wid);
if (wid == 0)
wid = left_->expr_width();
verinum nv (verinum::Vx, wid);
res = new NetEConst(nv);
}
@ -1242,6 +1251,10 @@ NetEConst* NetEUReduce::eval_tree()
/*
* $Log: eval_tree.cc,v $
* Revision 1.50 2003/04/14 03:40:21 steve
* Make some effort to preserve bits while
* operating on constant values.
*
* Revision 1.49 2003/03/15 18:07:58 steve
* More resilient WRT right expression width of GT.
*

View File

@ -203,6 +203,34 @@ interpreted as a loop head, and this is what Icarus Verilog does, as
well as all the other major Verilog tools, but the standard does not
say this.
* UNSIZED NUMERIC CONSTANTS ARE NOT LIMITED TO 32 BITS
The Verilog standard allows Verilog implementations to limit the size
of unsized constants to a bit width of at least 32. That means that a
constant 17179869183 (36'h3_ffff_ffff) may overflow some compilers. In
fact, it is common to limit these values to 32bits. However, a
compiler may just as easily choose another width limit, for example
64bits. That value is equally good.
However, it is not *required* that an implementation truncate at 32
bits, and in fact Icarus Verilog does not truncate at all. It will
make the unsized constant as big as it needs to be to hold the value
accurately. This is especially useful in situations like this;
reg [width-1:0] foo = 17179869183;
The programmer wants the constant to take on the width of the reg,
which in this example is parameterized. Since constant sizes cannot be
parameterized, the programmer ideally gives an unsized constant, which
the compiler then expands/contracts to match the l-value.
Also, by choosing to not ever truncate, Icarus Verilog can handle code
written for a 64bit compiler as easily as for a 32bit compiler. In
particular, any constants that the user does not expect to be
arbitrarily truncated by his compiler will also not be truncated by
Icarus Verilog, no matter what that other compiler chooses as a
truncation point.
* UNSIZED EXPRESSIONS AS PARAMETERS TO CONCATENATION {}
@ -470,8 +498,12 @@ of 4-value behavior in the dead zone, and appears more user friendly
when viewed by reasonable viewers.
$Id: ieee1364-notes.txt,v 1.15 2003/02/16 23:39:08 steve Exp $
$Id: ieee1364-notes.txt,v 1.16 2003/04/14 03:40:21 steve Exp $
$Log: ieee1364-notes.txt,v $
Revision 1.16 2003/04/14 03:40:21 steve
Make some effort to preserve bits while
operating on constant values.
Revision 1.15 2003/02/16 23:39:08 steve
NaN in dead zones of VCD dumps.

View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: verinum.cc,v 1.38 2003/04/03 04:30:00 steve Exp $"
#ident "$Id: verinum.cc,v 1.39 2003/04/14 03:40:21 steve Exp $"
#endif
# include "config.h"
@ -309,7 +309,7 @@ bool verinum::is_zero() const
* If the input value has a definite length, then that value is just
* returned as is.
*/
static verinum trim_vnum(const verinum&that)
verinum trim_vnum(const verinum&that)
{
unsigned tlen;
@ -409,7 +409,7 @@ ostream& operator<< (ostream&o, const verinum&v)
out as a decimal number. */
if (v.is_defined()) {
if (v.has_sign())
o << "'sd" << v.as_ulong();
o << "'sd" << v.as_long();
else
o << "'d" << v.as_ulong();
return o;
@ -571,8 +571,10 @@ verinum v_not(const verinum&left)
/*
* Addition works a bit at a time, from the least significant up to
* the most significant. The result is signed only if either of the
* operands is signed.
* the most significant. The result is signed only if both of the
* operands are signed. The result is also expanded as needed to
* prevent overflow. It is up to the caller to shrink the result back
* down if that is the desire.
*/
verinum operator + (const verinum&left, const verinum&right)
{
@ -582,21 +584,38 @@ verinum operator + (const verinum&left, const verinum&right)
unsigned max = left.len();
if (right.len() > max) max = right.len();
verinum val (verinum::V0, max);
val.has_sign( left.has_sign() || right.has_sign() );
bool signed_flag = left.has_sign() && right.has_sign();
verinum::V*val_bits = new verinum::V[max+1];
verinum::V carry = verinum::V0;
for (unsigned idx = 0 ; idx < min ; idx += 1)
val.set(idx, add_with_carry(left[idx], right[idx], carry));
val_bits[idx] = add_with_carry(left[idx], right[idx], carry);
verinum::V rpad = signed_flag? right[right.len()-1] : verinum::V0;
verinum::V lpad = signed_flag? left[left.len()-1] : verinum::V0;
if (left.len() > right.len()) {
for (unsigned idx = min ; idx < max ; idx += 1)
val.set(idx,add_with_carry(left[idx], verinum::V0, carry));
for (unsigned idx = min ; idx < left.len() ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], rpad, carry);
} else {
for (unsigned idx = min ; idx < max ; idx += 1)
val.set(idx, add_with_carry(verinum::V0, right[idx], carry));
for (unsigned idx = min ; idx < right.len() ; idx += 1)
val_bits[idx] = add_with_carry(lpad, right[idx], carry);
}
if (signed_flag) {
val_bits[max] = add_with_carry(lpad, rpad, carry);
if (val_bits[max] != val_bits[max-1])
max += 1;
}
verinum val (val_bits, max, false);
val.has_sign(signed_flag);
delete[]val_bits;
return val;
}
@ -826,6 +845,10 @@ verinum::V operator & (verinum::V l, verinum::V r)
/*
* $Log: verinum.cc,v $
* Revision 1.39 2003/04/14 03:40:21 steve
* Make some effort to preserve bits while
* operating on constant values.
*
* Revision 1.38 2003/04/03 04:30:00 steve
* Prevent overrun comparing verinums to zero.
*
@ -872,47 +895,5 @@ verinum::V operator & (verinum::V l, verinum::V r)
*
* Revision 1.24 2001/02/07 21:47:13 steve
* Fix expression widths for rvalues and parameters (PR#131,132)
*
* Revision 1.23 2001/02/07 02:46:31 steve
* Support constant evaluation of / and % (PR#124)
*
* Revision 1.22 2001/01/02 03:23:40 steve
* Evaluate constant &, | and unary ~.
*
* Revision 1.21 2000/12/10 22:01:36 steve
* Support decimal constants in behavioral delays.
*
* Revision 1.20 2000/09/28 03:55:55 steve
* handel, by truncation, verinums that are to long for long integers.
*
* Revision 1.19 2000/09/27 18:28:37 steve
* multiply in parameter expressions.
*
* Revision 1.18 2000/09/07 22:37:10 steve
* The + operator now preserves signedness.
*
* Revision 1.17 2000/06/12 03:56:51 steve
* Fix subract of short value form long one.
*
* Revision 1.16 2000/02/23 04:43:43 steve
* Some compilers do not accept the not symbol.
*
* Revision 1.15 2000/02/23 02:56:56 steve
* Macintosh compilers do not support ident.
*
* Revision 1.14 2000/01/07 03:45:49 steve
* Initial support for signed constants.
*
* Revision 1.13 2000/01/06 05:57:06 steve
* Only sign-extend unsized numbers.
*
* Revision 1.12 1999/11/06 16:00:17 steve
* Put number constants into a static table.
*
* Revision 1.11 1999/10/22 23:57:53 steve
* do the <= in bits, not numbers.
*
* Revision 1.10 1999/10/10 23:29:37 steve
* Support evaluating + operator at compile time.
*/

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
#ident "$Id: verinum.h,v 1.23 2003/04/03 04:30:00 steve Exp $"
#ident "$Id: verinum.h,v 1.24 2003/04/14 03:40:21 steve Exp $"
#endif
# include <string>
@ -103,6 +103,9 @@ class verinum {
bool string_flag_;
};
/* Return a verinum that is minimal. That is, it has only the length
needed to accurately represent the contained value, signed or not. */
extern verinum trim_vnum(const verinum&);
extern ostream& operator<< (ostream&, const verinum&);
extern ostream& operator<< (ostream&, verinum::V);
@ -113,11 +116,6 @@ extern verinum::V operator & (verinum::V l, verinum::V r);
extern verinum::V operator == (const verinum&left, const verinum&right);
extern verinum::V operator <= (const verinum&left, const verinum&right);
extern verinum::V operator < (const verinum&left, const verinum&right);
extern verinum operator + (const verinum&left, const verinum&right);
extern verinum operator - (const verinum&left, const verinum&right);
extern verinum operator * (const verinum&left, const verinum&right);
extern verinum operator / (const verinum&left, const verinum&right);
extern verinum operator % (const verinum&left, const verinum&right);
inline verinum::V operator > (const verinum&left, const verinum&right)
{ return right < left; }
@ -128,10 +126,27 @@ inline verinum::V operator >= (const verinum&left, const verinum&right)
inline verinum::V operator != (const verinum&left, const verinum&right)
{ return (left == right)? verinum::V0 : verinum::V1; }
/* These are arithmetic operators. These generally work to produce
results that do not overflow. That means the result may expand or
contract to hold the bits needed to hold the operation results
accurately. It is up to the caller to truncate or pad if a specific
width is expected. */
extern verinum operator + (const verinum&left, const verinum&right);
extern verinum operator - (const verinum&left, const verinum&right);
extern verinum operator * (const verinum&left, const verinum&right);
extern verinum operator / (const verinum&left, const verinum&right);
extern verinum operator % (const verinum&left, const verinum&right);
/* Bitwise not returns the ones complement. */
extern verinum v_not(const verinum&left);
/*
* $Log: verinum.h,v $
* Revision 1.24 2003/04/14 03:40:21 steve
* Make some effort to preserve bits while
* operating on constant values.
*
* Revision 1.23 2003/04/03 04:30:00 steve
* Prevent overrun comparing verinums to zero.
*