Fixes for GitHub issues 13 and 15.

The verinum arithmetic operators now observe the standard Verilog
rules for calculating the result width if all operands are sized.
If any operand is unsized, the result is lossless, as before.
They also now all observe the standard rules for handling partially
undefined operands (if any operand bit is 'x', the entire result is
'x').

I've also added the unary '-' operator, and renamed v_not() to be
the unary '~' operator. This has allowed some simplification in
other parts of the compiler.
This commit is contained in:
Martin Whitaker 2014-02-25 20:39:21 +00:00
parent 320f6d008c
commit a3450bf856
7 changed files with 236 additions and 192 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -5341,13 +5341,8 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope,
case '-':
if (NetEConst*ipc = dynamic_cast<NetEConst*>(ip)) {
verinum val = ipc->value();
/* Calculate unary minus as 0-val */
verinum zero (verinum::V0, expr_wid);
zero.has_sign(val.has_sign());
verinum nval = verinum(zero - val, expr_wid);
tmp = new NetEConst(nval);
verinum val = - ipc->value();
tmp = new NetEConst(val);
tmp->cast_signed(signed_flag_);
tmp->set_line(*this);
delete ip;
@ -5460,7 +5455,7 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const
// The only operand that I know can get here is the
// unary not (~).
ivl_assert(*this, op_ == '~');
value = v_not(value);
value = ~value;
ctmp = new NetEConst(value);
ctmp->set_line(*this);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -193,9 +193,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
verinum min_value (0);
verinum max_value (0);
if (enum_type->signed_flag) {
min_value = v_not((pow(verinum(2),
verinum(use_enum->packed_width()-1)))) +
one_value;
min_value = -pow(verinum(2), verinum(use_enum->packed_width()-1));
max_value = pow(verinum(2), verinum(use_enum->packed_width()-1)) -
one_value;
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -261,7 +261,7 @@ verinum* PEUnary::eval_const(Design*des, NetScope*scope) const
for (unsigned idx = 0 ; idx < val->len() ; idx += 1)
tmp.set(idx, val->get(idx));
*val = v_not(tmp) + verinum(verinum::V1, 1);
*val = -tmp;
val->has_sign(true);
return val;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -175,11 +175,11 @@ NetExpr* NetEBAdd::eval_tree()
if (op_ == se->op_) {
/* (a + lval) + rval --> a + (rval+lval) */
/* (a - lval) - rval --> a - (rval+lval) */
val = verinum(rval + lval, wid);
val = cast_to_width(rval + lval, wid);
} else {
/* (a - lval) + rval --> a + (rval-lval) */
/* (a + lval) - rval --> a - (rval-lval) */
val = verinum(rval - lval, wid);
val = cast_to_width(rval - lval, wid);
}
NetEConst*tmp = new NetEConst(val);
@ -217,10 +217,10 @@ NetExpr* NetEBAdd::eval_arguments_(const NetExpr*l, const NetExpr*r) const
verinum val;
switch (op_) {
case '+':
val = verinum(lval + rval, wid);
val = cast_to_width(lval + rval, wid);
break;
case '-':
val = verinum(lval - rval, wid);
val = cast_to_width(lval - rval, wid);
break;
default:
return 0;
@ -816,14 +816,15 @@ NetExpr* NetEBDiv::eval_arguments_(const NetExpr*l, const NetExpr*r) const
verinum val;
switch (op_) {
case '/':
val = verinum(lval / rval, wid);
val = cast_to_width(lval / rval, wid);
break;
case '%':
val = verinum(lval % rval, wid);
val = cast_to_width(lval % rval, wid);
break;
default:
return 0;
}
NetExpr*tmp = new NetEConst(val);
ivl_assert(*this, tmp);
eval_debug(this, tmp, false);
@ -1027,7 +1028,7 @@ NetExpr* NetEBMult::eval_arguments_(const NetExpr*l, const NetExpr*r) const
ivl_assert(*this, lval.len() == wid);
ivl_assert(*this, rval.len() == wid);
verinum val(lval * rval, wid);
verinum val = cast_to_width(lval * rval, wid);
NetEConst*tmp = new NetEConst(val);
ivl_assert(*this, tmp);
eval_debug(this, tmp, false);
@ -1064,7 +1065,7 @@ NetExpr* NetEBPow::eval_arguments_(const NetExpr*l, const NetExpr*r) const
ivl_assert(*this, wid > 0);
ivl_assert(*this, lval.len() == wid);
verinum val(pow(lval, rval), wid);
verinum val = cast_to_width(pow(lval, rval), wid);
NetEConst*res = new NetEConst(val);
ivl_assert(*this, res);
eval_debug(this, res, false);
@ -1092,12 +1093,12 @@ NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const
switch (op_) {
case 'l':
val = verinum(lv << shift, wid);
val = cast_to_width(lv << shift, wid);
break;
case 'r':
lv.has_sign(false);
case 'R':
val = verinum(lv >> shift, wid);
val = cast_to_width(lv >> shift, wid);
break;
default:
return 0;
@ -1446,14 +1447,7 @@ NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const
break;
case '-':
if (val.is_defined()) {
verinum tmp (verinum::V0, val.len());
tmp.has_sign(val.has_sign());
val = verinum(tmp - val, val.len());
} else {
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
val.set(idx, verinum::Vx);
}
val = -val;
break;
case 'm':
@ -1461,9 +1455,7 @@ NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
val.set(idx, verinum::Vx);
} else if (val.is_negative()) {
verinum tmp (verinum::V0, val.len());
tmp.has_sign(val.has_sign());
val = verinum(tmp - val, val.len());
val = -val;
}
break;

View File

@ -1,7 +1,7 @@
%{
/*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -2259,7 +2259,7 @@ pos_neg_number
{ $$ = $1;
}
| '-' number
{ verinum tmp = v_not(*($2)) + verinum(1);
{ verinum tmp = -(*($2));
*($2) = tmp;
$$ = $2;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -24,6 +24,7 @@
# include <cassert>
# include <cmath> // Needed to get pow for as_double().
# include <cstdio> // Needed to get snprintf for as_string().
# include <algorithm>
#if !defined(HAVE_LROUND)
/*
@ -200,7 +201,6 @@ verinum::verinum(double val, bool)
fraction = frexp(val, &exponent);
nbits_ = exponent+1;
bits_ = new V[nbits_];
const verinum const_one(1);
/* If the value is small enough just use lround(). */
if (nbits_ <= BITS_IN_LONG) {
@ -230,9 +230,9 @@ verinum::verinum(double val, bool)
for (int wd = nwords; wd >= 0; wd -= 1) {
unsigned long bits = (unsigned long) fraction;
fraction = fraction - (double) bits;
unsigned max = (wd+1)*BITS_IN_LONG;
if (max > nbits_) max = nbits_;
for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) {
unsigned max_idx = (wd+1)*BITS_IN_LONG;
if (max_idx > nbits_) max_idx = nbits_;
for (unsigned idx = wd*BITS_IN_LONG; idx < max_idx; idx += 1) {
bits_[idx] = (bits&1) ? V1 : V0;
bits >>= 1;
}
@ -242,7 +242,7 @@ verinum::verinum(double val, bool)
/* Convert a negative number if needed. */
if (is_neg) {
*this = v_not(*this) + const_one;
*this = -(*this);
}
/* Trim the result. */
@ -968,7 +968,7 @@ static verinum::V add_with_carry(verinum::V l, verinum::V r, verinum::V&c)
return verinum::V0;
}
verinum v_not(const verinum&left)
verinum operator ~ (const verinum&left)
{
verinum val = left;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
@ -988,137 +988,190 @@ 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 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.
* Addition and subtraction works a bit at a time, from the least
* significant up to the most significant. The result is signed only
* if both of the operands are signed. If either operand is unsized,
* the result is expanded as needed to prevent overflow.
*/
verinum operator + (const verinum&left, const verinum&right)
{
unsigned min = left.len();
if (right.len() < min) min = right.len();
const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned max = left.len();
if (right.len() > max) max = right.len();
unsigned min_len = min(left.len(), right.len());
unsigned max_len = max(left.len(), right.len());
bool signed_flag = left.has_sign() && right.has_sign();
verinum::V*val_bits = new verinum::V[max+1];
// If either the left or right values are undefined, the
// entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
unsigned len = has_len_flag ? max_len : 1;
verinum result (verinum::Vx, len, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[max_len+1];
verinum::V carry = verinum::V0;
for (unsigned idx = 0 ; idx < min ; idx += 1)
for (unsigned idx = 0 ; idx < min_len ; idx += 1)
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;
verinum::V rpad = sign_bit(right);
verinum::V lpad = sign_bit(left);
if (left.len() > right.len()) {
for (unsigned idx = min ; idx < left.len() ; idx += 1)
for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], rpad, carry);
} else {
for (unsigned idx = min ; idx < right.len() ; idx += 1)
for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(lpad, right[idx], carry);
}
val_bits[max] = add_with_carry(lpad, rpad, carry);
#if 0
if (signed_flag) {
if (val_bits[max] != val_bits[max-1])
max += 1;
unsigned len = max_len;
if (!has_len_flag) {
val_bits[max_len] = add_with_carry(lpad, rpad, carry);
if (signed_flag) {
if (val_bits[max_len] != val_bits[max_len-1]) len += 1;
} else {
if (val_bits[max_len] != verinum::V0) len += 1;
}
}
#endif
verinum val (val_bits, max+1, false);
val.has_sign(signed_flag);
verinum result (val_bits, len, has_len_flag);
result.has_sign(signed_flag);
delete[]val_bits;
return val;
return result;
}
verinum operator - (const verinum&left, const verinum&right)
{
unsigned min = left.len();
if (right.len() < min) min = right.len();
const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned max = left.len();
if (right.len() > max) max = right.len();
unsigned min_len = min(left.len(), right.len());
unsigned max_len = max(left.len(), right.len());
bool signed_flag = left.has_sign() && right.has_sign();
verinum::V*val_bits = new verinum::V[max+1];
// If either the left or right values are undefined, the
// entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
unsigned len = has_len_flag ? max_len : 1;
verinum result (verinum::Vx, len, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[max_len+1];
verinum::V carry = verinum::V1;
for (unsigned idx = 0 ; idx < min ; idx += 1)
for (unsigned idx = 0 ; idx < min_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], ~right[idx], carry);
verinum::V rpad = signed_flag? ~right[right.len()-1] : verinum::V1;
verinum::V lpad = signed_flag? left[left.len()-1] : verinum::V0;
verinum::V rpad = sign_bit(right);
verinum::V lpad = sign_bit(left);
if (left.len() > right.len()) {
for (unsigned idx = min ; idx < left.len() ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], rpad, carry);
for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], ~rpad, carry);
} else {
for (unsigned idx = min ; idx < right.len() ; idx += 1)
for (unsigned idx = min_len ; idx < max_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;
unsigned len = max_len;
if (signed_flag && !has_len_flag) {
val_bits[max_len] = add_with_carry(lpad, ~rpad, carry);
if (val_bits[max_len] != val_bits[max_len-1]) len += 1;
}
verinum val (val_bits, max, false);
val.has_sign(signed_flag);
verinum result (val_bits, len, has_len_flag);
result.has_sign(signed_flag);
delete[]val_bits;
return val;
return result;
}
verinum operator - (const verinum&right)
{
const bool has_len_flag = right.has_len();
const bool signed_flag = right.has_sign();
unsigned len = right.len();
// If either the left or right values are undefined, the
// entire result is undefined.
if (!right.is_defined()) {
verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[len+1];
verinum::V carry = verinum::V1;
for (unsigned idx = 0 ; idx < len ; idx += 1)
val_bits[idx] = add_with_carry(verinum::V0, ~right[idx], carry);
if (signed_flag && !has_len_flag) {
val_bits[len] = add_with_carry(verinum::V0, ~sign_bit(right), carry);
if (val_bits[len] != val_bits[len-1]) len += 1;
}
verinum result (val_bits, len, has_len_flag);
result.has_sign(signed_flag);
delete[]val_bits;
return result;
}
/*
* This multiplies two verinum numbers together into a verinum
* result. The resulting number is as large as the sum of the sizes of
* the operand.
* This operator multiplies the left number by the right number. The
* result is signed only if both of the operands are signed. If either
* operand is unsized, the resulting number is as large as the sum of
* the sizes of the operands.
*
* The algorithm used is successive shift and add operations,
* implemented as the nested loops.
*
* If either value is not completely defined, then the result is not
* defined either.
*/
verinum operator * (const verinum&left, const verinum&right)
{
const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
/* If either operand is not fully defined, then the entire
result is undefined. Create a result that is the right size
and is filled with 'bx bits. */
if (! (left.is_defined() && right.is_defined())) {
verinum result (verinum::Vx, left.len()+right.len(), has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
const unsigned l_len = left.len();
const unsigned r_len = right.len();
unsigned len = has_len_flag ? max(l_len, r_len) : l_len + r_len;
// If either the left or right values are undefined, the
// entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum result(verinum::V0, left.len() + right.len(), has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
verinum result(verinum::V0, len, has_len_flag);
result.has_sign(signed_flag);
verinum::V r_sign = sign_bit(right);
for (unsigned rdx = 0 ; rdx < result.len() ; rdx += 1) {
for (unsigned rdx = 0 ; rdx < len ; rdx += 1) {
verinum::V r_bit = rdx < right.len()? right.get(rdx) : r_sign;
verinum::V r_bit = rdx < r_len ? right.get(rdx) : r_sign;
if (r_bit == verinum::V0)
continue;
verinum::V l_sign = sign_bit(left);
verinum::V carry = verinum::V0;
for (unsigned ldx = 0 ; ldx < result.len()-rdx ; ldx += 1) {
verinum::V l_bit = ldx < left.len()? left[ldx] : l_sign;
for (unsigned ldx = 0 ; ldx < (len - rdx) ; ldx += 1) {
verinum::V l_bit = ldx < l_len ? left[ldx] : l_sign;
result.set(ldx+rdx, add_with_carry(l_bit,
result[rdx+ldx],
carry));
@ -1150,22 +1203,20 @@ static verinum recursive_pow(const verinum&left, verinum&right)
return make_p_one(left.len(), left.has_len(), left.has_sign());
}
verinum res;
verinum result;
if (right.get(0) == 1) {
// The exponent is odd, so subtract 1 from it and recurse
// The exponent is odd, so subtract 1 from it and recurse.
// We know it's odd, so the subtraction is easy.
right.set(0, verinum::V0);
res = pow(left, right);
res = left * res;
result = pow(left, right);
result = left * result;
} else {
// The exponent is even, so divide it by 2 and recurse
right = right >> 1;
res = pow(left, right);
res = res * res;
result = pow(left, right);
result = result * result;
}
if (left.has_len()) {
res = verinum(res, left.len());
}
return res;
return result;
}
verinum pow(const verinum&left, const verinum&right)
@ -1176,8 +1227,9 @@ verinum pow(const verinum&left, const verinum&right)
verinum p_one = make_p_one(left.len(), left.has_len(), left.has_sign());
verinum m_one = make_m_one(left.len(), left.has_len(), left.has_sign());
// If either the right or left values are undefined we return 'bx.
if (!right.is_defined() || !left.is_defined()) {
// If either the left or right values are undefined, the
// entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
result = verinum(verinum::Vx, left.len(), left.has_len());
result.has_sign(left.has_sign());
@ -1221,40 +1273,52 @@ verinum pow(const verinum&left, const verinum&right)
verinum operator << (const verinum&that, unsigned shift)
{
verinum result(verinum::V0, that.len() + shift, that.has_len());
bool has_len_flag = that.has_len();
unsigned len = that.len();
if (!has_len_flag) len += shift;
verinum result(verinum::V0, len, has_len_flag);
result.has_sign(that.has_sign());
for (unsigned idx = 0 ; idx < that.len() ; idx += 1)
result.set(idx+shift, that.get(idx));
for (unsigned idx = shift ; idx < len ; idx += 1)
result.set(idx, that.get(idx - shift));
return result;
return trim_vnum(result);
}
verinum operator >> (const verinum&that, unsigned shift)
{
if (shift >= that.len()) {
if (that.has_sign()) {
verinum result (that.get(that.len()-1), 1);
result.has_sign(true);
return result;
} else {
verinum result(verinum::V0, 1);
return result;
}
bool has_len_flag = that.has_len();
unsigned len = that.len();
verinum::V sign_bit = that.has_sign() ? that.get(len-1) : verinum::V0;
if (shift >= len) {
if (!has_len_flag) len = 1;
verinum result(sign_bit, len, has_len_flag);
result.has_sign(that.has_sign());
return result;
}
verinum result(that.has_sign()? that.get(that.len()-1) : verinum::V0,
that.len() - shift, that.has_len());
if (!has_len_flag) len -= shift;
verinum result(sign_bit, len, has_len_flag);
result.has_sign(that.has_sign());
for (unsigned idx = shift ; idx < that.len() ; idx += 1)
result.set(idx-shift, that.get(idx));
return result;
return trim_vnum(result);
}
static verinum unsigned_divide(verinum num, verinum den, bool signed_result)
{
// We need the following calculations to be lossless. The
// result will be cast to the required width by the caller.
num.has_len(false);
den.has_len(false);
unsigned nwid = num.len();
while (nwid > 0 && (num.get(nwid-1) == verinum::V0))
nwid -= 1;
@ -1289,6 +1353,11 @@ static verinum unsigned_divide(verinum num, verinum den, bool signed_result)
static verinum unsigned_modulus(verinum num, verinum den)
{
// We need the following calculations to be lossless. The
// result will be cast to the required width by the caller.
num.has_len(false);
den.has_len(false);
unsigned nwid = num.len();
while (nwid > 0 && (num.get(nwid-1) == verinum::V0))
nwid -= 1;
@ -1316,39 +1385,29 @@ static verinum unsigned_modulus(verinum num, verinum den)
}
/*
* This operator divides the left number by the right number. If
* either value is signed, the result is signed. If both values have a
* defined length, then the result has a defined length.
* This operator divides the left number by the right number. The result
* is signed only if both of the operands are signed.
*/
verinum operator / (const verinum&left, const verinum&right)
{
const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned use_len = left.len();
/* If either operand is not fully defined, then the entire
result is undefined. Create a result that is the right size
and is filled with 'bx bits. */
if (! (left.is_defined() && right.is_defined())) {
// If either the left or right values are undefined, or the
// right value is zero, the entire result is undefined.
if (!left.is_defined() || !right.is_defined() || right.is_zero()) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
return result;
}
/* If the right expression is a zero value, then the result is
filled with 'bx bits. */
if (right.is_zero()) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
result.has_sign(signed_flag);
return result;
}
verinum result(verinum::Vz, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
/* do the operation differently, depending on whether the
result is signed or not. */
if (result.has_sign()) {
if (signed_flag) {
if (use_len <= (8*sizeof(long) - 1)) {
long l = left.as_long();
@ -1361,23 +1420,23 @@ verinum operator / (const verinum&left, const verinum&right)
} else {
verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false;
if (left < zero) {
use_left = zero - left;
if (left.is_negative()) {
use_left = -left;
negative = !negative;
} else {
use_left = left;
}
if (right < zero) {
use_right = zero - right;
use_left.has_sign(false);
if (right.is_negative()) {
use_right = -right;
negative = !negative;
} else {
use_right = right;
}
use_right.has_sign(false);
result = unsigned_divide(use_left, use_right, true);
if (negative) result = zero - result;
if (negative) result = -result;
}
} else {
@ -1398,36 +1457,31 @@ verinum operator / (const verinum&left, const verinum&right)
}
}
if (has_len_flag)
result = cast_to_width(result, use_len);
result.has_sign(signed_flag);
return trim_vnum(result);
}
verinum operator % (const verinum&left, const verinum&right)
{
const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned use_len = left.len();
/* If either operand is not fully defined, then the entire
result is undefined. Create a result that is the right size
and is filled with 'bx bits. */
if (! (left.is_defined() && right.is_defined())) {
// If either the left or right values are undefined, or the
// right value is zero, the entire result is undefined.
if (!left.is_defined() || !right.is_defined() || right.is_zero()) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
return result;
}
/* If the right expression is a zero value, then the result is
filled with 'bx bits. */
if (right.as_ulong() == 0) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
result.has_sign(signed_flag);
return result;
}
verinum result(verinum::Vz, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
if (result.has_sign()) {
if (signed_flag) {
if (use_len <= 8*sizeof(long)) {
/* Use native signed modulus to do the work. */
long l = left.as_long();
@ -1439,23 +1493,22 @@ verinum operator % (const verinum&left, const verinum&right)
}
} else {
verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false;
if (left < zero) {
use_left = zero - left;
if (left.is_negative()) {
use_left = -left;
negative = true;
} else {
use_left = left;
}
if (right < zero) {
use_right = zero - right;
use_left.has_sign(false);
if (right.is_negative()) {
use_right = -right;
} else {
use_right = right;
}
use_right.has_sign(false);
result = unsigned_modulus(use_left, use_right);
result.has_sign(true);
if (negative) result = zero - result;
if (negative) result = -result;
}
} else {
if (use_len <= 8*sizeof(unsigned long)) {
@ -1472,6 +1525,10 @@ verinum operator % (const verinum&left, const verinum&right)
}
}
if (has_len_flag)
result = cast_to_width(result, use_len);
result.has_sign(signed_flag);
return trim_vnum(result);
}

View File

@ -1,7 +1,7 @@
#ifndef __verinum_H
#define __verinum_H
/*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -171,11 +171,13 @@ 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. */
/* These are arithmetic operators. If any operand is unsized, they
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. If all operands are sized,
the normal Verilog rules for result size are used. */
extern verinum operator - (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);
@ -190,6 +192,6 @@ extern verinum operator>> (const verinum&left, unsigned shift);
extern verinum concat(const verinum&left, const verinum&right);
/* Bitwise not returns the ones complement. */
extern verinum v_not(const verinum&left);
extern verinum operator ~ (const verinum&left);
#endif