diff --git a/elab_expr.cc b/elab_expr.cc index a1e2cf280..836361199 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -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(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); diff --git a/elab_scope.cc b/elab_scope.cc index ffc0a31af..1b78ac894 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -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 { diff --git a/eval.cc b/eval.cc index 996aacd4e..09b2aae89 100644 --- a/eval.cc +++ b/eval.cc @@ -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; } diff --git a/eval_tree.cc b/eval_tree.cc index 8343f8f5b..ac5d24ae4 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -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; diff --git a/parse.y b/parse.y index 73327feb6..d9b8f0166 100644 --- a/parse.y +++ b/parse.y @@ -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; } diff --git a/verinum.cc b/verinum.cc index 12c234964..41efb99a4 100644 --- a/verinum.cc +++ b/verinum.cc @@ -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 # include // Needed to get pow for as_double(). # include // Needed to get snprintf for as_string(). +# include #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); } diff --git a/verinum.h b/verinum.h index a459e03f6..41df9a90a 100644 --- a/verinum.h +++ b/verinum.h @@ -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