diff --git a/README.txt b/README.txt index da1e70b4f..f47d03f5f 100644 --- a/README.txt +++ b/README.txt @@ -8,7 +8,7 @@ Icarus Verilog is intended to compile ALL of the Verilog HDL as described in the IEEE-1364 standard. Of course, it's not quite there yet. It does currently handle a mix of structural and behavioral constructs. For a view of the current state of Icarus Verilog, see its -home page at . +home page at . Icarus Verilog is not aimed at being a simulator in the traditional sense, but a compiler that generates code employed by back-end diff --git a/compiler.h b/compiler.h index 6e4bf1cdf..63cc509b5 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef __compiler_H #define __compiler_H /* - * 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 @@ -36,6 +36,11 @@ */ extern unsigned integer_width; +/* + * The width_cap is the width limit for unsized expressions. + */ +extern unsigned width_cap; + /* * This is the maximum number of recursive module loops allowed within * a generate block. diff --git a/driver/cflexor.lex b/driver/cflexor.lex index 08bb455c3..9d50c4c4f 100644 --- a/driver/cflexor.lex +++ b/driver/cflexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -92,6 +92,8 @@ int cmdfile_stack_ptr = 0; "+vhdl-libdir+" { BEGIN(PLUS_ARGS); return TOK_VHDL_LIBDIR; } +"+width-cap+" { BEGIN(PLUS_ARGS); return TOK_WIDTH_CAP; } + /* If it is not any known plus-flag, return the generic form. */ "+"[^\n \t\b\f\r+]* { cflval.text = strdup(yytext); diff --git a/driver/cfparse.y b/driver/cfparse.y index 22f127d26..13fcb85c9 100644 --- a/driver/cfparse.y +++ b/driver/cfparse.y @@ -1,6 +1,6 @@ %{ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -61,6 +61,7 @@ static void translate_file_name(char*text) %token TOK_Da TOK_Dc TOK_Dv TOK_Dy %token TOK_DEFINE TOK_INCDIR TOK_INTEGER_WIDTH TOK_LIBDIR TOK_LIBDIR_NOCASE %token TOK_LIBEXT TOK_PARAMETER TOK_TIMESCALE TOK_VHDL_WORK TOK_VHDL_LIBDIR +%token TOK_WIDTH_CAP %token TOK_PLUSARG TOK_PLUSWORD TOK_STRING %% @@ -191,6 +192,13 @@ item free(tmp); } + | TOK_WIDTH_CAP TOK_PLUSARG + { char*tmp = substitutions($2); + free($2); + width_cap = strtoul(tmp,0,10); + free(tmp); + } + /* The + tokens that are not otherwise matched, are ignored. The skip_args rule arranges for all the argument words to be consumed. */ diff --git a/driver/globals.h b/driver/globals.h index 1ef723315..23c72a380 100644 --- a/driver/globals.h +++ b/driver/globals.h @@ -1,7 +1,7 @@ #ifndef __globals_H #define __globals_H /* - * Copyright (c) 2000-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-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,9 @@ /* This is the integer-width argument that will be passed to ivl. */ extern unsigned integer_width; + /* This is the width-cap argument that will be passed to ivl. */ +extern unsigned width_cap; + extern const char*vhdlpp_work; extern const char**vhdlpp_libdir; extern unsigned vhdlpp_libdir_cnt; diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index f6c33fa68..21d504728 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,4 +1,4 @@ -.TH iverilog 1 "April 2nd, 2013" "" "Version %M.%m.%n %E" +.TH iverilog 1 "February 26th, 2014" "" "Version %M.%m.%n %E" .SH NAME iverilog - Icarus Verilog compiler @@ -462,6 +462,12 @@ This allows the programmer to select the width for integer variables in the Verilog source. The default is 32, the value can be any desired integer value. +.TP 8 +.B +width-cap+\fIvalue\fP +This allows the programmer to select the width cap for unsized expressions. +If the calculated width for an unsized expression exceeds this value, the +compiler will issue a warning and limit the expression width to this value. + .SH "VARIABLES IN COMMAND FILES" In certain cases, iverilog supports variables in command files. These @@ -515,7 +521,7 @@ Tips on using, debugging, and developing the compiler can be found at .SH COPYRIGHT .nf -Copyright \(co 2002\-2011 Stephen Williams +Copyright \(co 2002\-2014 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff --git a/driver/main.c b/driver/main.c index b86bacba7..d0b286c47 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-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 @@ -141,6 +141,8 @@ char warning_flags[16] = "n"; unsigned integer_width = 32; +unsigned width_cap = 65536; + char*mod_list = 0; char*command_filename = 0; @@ -1192,6 +1194,8 @@ int main(int argc, char **argv) fprintf(iconfig_file, "iwidth:%u\n", integer_width); + fprintf(iconfig_file, "widthcap:%u\n", width_cap); + /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ diff --git a/elab_expr.cc b/elab_expr.cc index 57ef9ecc0..854d6e1fb 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 @@ -538,7 +538,7 @@ NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, return tmp; } - if (rp_val.is_zero()) { + if (rp_val.is_zero() && (lp->expr_type() != IVL_VT_LOGIC)) { NetEConst*tmp = make_const_0(expr_wid); tmp->cast_signed(signed_flag_); tmp->set_line(*this); @@ -756,12 +756,11 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) r_val = rc->value().as_long(); // Clip to a sensible range to avoid underflow/overflow - // in the following calculations. 1024 bits should be - // enough for anyone... + // in the following calculations. if (r_val < 0) r_val = 0; - if (r_val > 1024) - r_val = 1024; + if ((unsigned long)r_val > width_cap) + r_val = width_cap; // If the left operand is a simple unsized number, we // can calculate the actual width required for the power @@ -5373,13 +5372,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; @@ -5492,7 +5486,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..ff224d909 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); @@ -1088,16 +1089,16 @@ NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const verinum val; if (rv.is_defined()) { - unsigned shift = rv.as_ulong(); + unsigned shift = rv.as_unsigned(); 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/main.cc b/main.cc index 7b7e36001..5c7e040a1 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "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 @@ -191,6 +191,11 @@ bool verbose_flag = false; unsigned integer_width = 32; +/* + * Width limit for unsized expressions. + */ +unsigned width_cap = 65536; + int def_ts_units = 0; int def_ts_prec = 0; @@ -647,6 +652,9 @@ static void read_iconfig_file(const char*ipath) } else if (strcmp(buf, "iwidth") == 0) { integer_width = strtoul(cp,0,10); + } else if (strcmp(buf, "widthcap") == 0) { + width_cap = strtoul(cp,0,10); + } else if (strcmp(buf, "library_file") == 0) { perm_string path = filename_strings.make(cp); library_file_map[path] = true; diff --git a/netmisc.cc b/netmisc.cc index a7184696e..e08f3a9f1 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -761,9 +761,9 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, // If context_width is positive, this is the RHS of an assignment, // so the LHS width must also be included in the width calculation. - if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) - && (expr_width < (unsigned)context_width)) - expr_width = context_width; + unsigned pos_context_width = context_width > 0 ? context_width : 0; + if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width)) + expr_width = pos_context_width; if (debug_elaborate) { cerr << pe->get_fileline() << ": elab_and_eval: test_width of " @@ -783,8 +783,8 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, // width, do so. if ((context_width > 0) && (!force_expand) && (pe->expr_type() != IVL_VT_REAL) - && (expr_width > (unsigned)context_width)) { - expr_width = max(pe->min_width(), (unsigned)context_width); + && (expr_width > pos_context_width)) { + expr_width = max(pe->min_width(), pos_context_width); if (debug_elaborate) { cerr << pe->get_fileline() << ": " @@ -792,6 +792,15 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, } } + if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap) + && (expr_width > pos_context_width)) { + cerr << pe->get_fileline() << ": warning: excessive unsized " + << "expression width detected." << endl; + cerr << pe->get_fileline() << ": : The expression width " + << "is capped at " << width_cap << " bits." << endl; + expr_width = width_cap; + } + unsigned flags = PExpr::NO_FLAGS; if (need_const) flags |= PExpr::NEED_CONST; @@ -812,10 +821,10 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe, tmp = cast_to_real(tmp); break; case IVL_VT_BOOL: - tmp = cast_to_int2(tmp, context_width > 0 ? context_width : 0); + tmp = cast_to_int2(tmp, pos_context_width); break; case IVL_VT_LOGIC: - tmp = cast_to_int4(tmp, context_width > 0 ? context_width : 0); + tmp = cast_to_int4(tmp, pos_context_width); break; default: 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/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 2fc9ed612..8c641d138 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -1080,6 +1080,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, return lv; } + static struct vector_info draw_binary_expr(ivl_expr_t expr, unsigned wid, int stuff_ok_flag) diff --git a/verinum.cc b/verinum.cc index 12c234964..250df8293 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. */ @@ -378,6 +378,25 @@ void verinum::set(unsigned off, const verinum&val) bits_[off+idx] = val[idx]; } +unsigned verinum::as_unsigned() const +{ + if (nbits_ == 0) + return 0; + + if (!is_defined()) + return 0; + + unsigned val = 0; + unsigned mask = 1; + for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) + if (bits_[idx] == V1) { + if (mask == 0) return ~mask; + val |= mask; + } + + return val; +} + unsigned long verinum::as_ulong() const { if (nbits_ == 0) @@ -386,15 +405,13 @@ unsigned long verinum::as_ulong() const if (!is_defined()) return 0; - unsigned top = nbits_; - if (top >= (8 * sizeof(unsigned long))) - top = 8 * sizeof(unsigned long); - unsigned long val = 0; unsigned long mask = 1; - for (unsigned idx = 0 ; idx < top ; idx += 1, mask <<= 1) - if (bits_[idx] == V1) + for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) + if (bits_[idx] == V1) { + if (mask == 0) return ~mask; val |= mask; + } return val; } @@ -407,15 +424,13 @@ uint64_t verinum::as_ulong64() const if (!is_defined()) return 0; - unsigned top = nbits_; - if (top >= (8 * sizeof(uint64_t))) - top = 8 * sizeof(uint64_t); - uint64_t val = 0; uint64_t mask = 1; - for (unsigned idx = 0 ; idx < top ; idx += 1, mask <<= 1) - if (bits_[idx] == V1) + for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1) + if (bits_[idx] == V1) { + if (mask == 0) return ~mask; val |= mask; + } return val; } @@ -968,7 +983,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 +1003,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 +1218,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 +1242,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 +1288,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 +1368,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 +1400,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 +1435,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 +1472,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 +1508,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 +1540,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..f6b3626c5 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 @@ -97,9 +97,14 @@ class verinum { V operator[] (unsigned idx) const { return get(idx); } - + // Return the value as a native unsigned integer. If the value is + // larger than can be represented by the returned type, return + // the maximum value of that type. If the value has any x or z + // bits or has zero width, return the value 0. uint64_t as_ulong64() const; + unsigned as_unsigned() const; unsigned long as_ulong() const; + signed long as_long() const; double as_double() const; string as_string() const; @@ -171,11 +176,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 +197,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 diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 2f85a89f5..475de9695 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -1768,7 +1768,7 @@ bool of_BREAKPOINT(vthread_t, vvp_code_t) } /* - * the %cassign/link instruction connects a source node to a + * The %cassign/link instruction connects a source node to a * destination node. The destination node must be a signal, as it is * marked with the source of the cassign so that it may later be * unlinked without specifically knowing the source that this @@ -1783,17 +1783,8 @@ bool of_CASSIGN_LINK(vthread_t, vvp_code_t cp) = dynamic_cast(dst->fun); assert(sig); - /* Detect the special case that we are already continuous - assigning the source onto the destination. */ - if (sig->cassign_link == src) - return true; - - /* If there is an existing cassign driving this node, then - unlink it. We can have only 1 cassign at a time. */ - if (sig->cassign_link != 0) { - vvp_net_ptr_t tmp (dst, 1); - sig->cassign_link->unlink(tmp); - } + /* Any previous continuous assign should have been removed already. */ + assert(sig->cassign_link == 0); sig->cassign_link = src; @@ -1806,7 +1797,27 @@ bool of_CASSIGN_LINK(vthread_t, vvp_code_t cp) } /* - * the %cassign/vec4 instruction invokes a continuous assign of a + * If there is an existing continuous assign linked to the destination + * node, unlink it. This must be done before applying a new continuous + * assign, otherwise the initial assigned value will be propagated to + * any other nodes driven by the old continuous assign source. + */ +static void cassign_unlink(vvp_net_t*dst) +{ + vvp_fun_signal_base*sig + = dynamic_cast(dst->fun); + assert(sig); + + if (sig->cassign_link == 0) + return; + + vvp_net_ptr_t tmp (dst, 1); + sig->cassign_link->unlink(tmp); + sig->cassign_link = 0; +} + +/* + * The %cassign/v instruction invokes a continuous assign of a * constant value to a signal. The instruction arguments are: * * %cassign/vec4 ; @@ -1822,7 +1833,10 @@ bool of_CASSIGN_VEC4(vthread_t thr, vvp_code_t cp) vvp_net_t*net = cp->net; vvp_vector4_t value = thr->pop_vec4(); - /* set the value into port 1 of the destination. */ + /* Remove any previous continuous assign to this net. */ + cassign_unlink(net); + + /* Set the value into port 1 of the destination. */ vvp_net_ptr_t ptr (net, 1); vvp_send_vec4(ptr, value, 0); @@ -1843,6 +1857,9 @@ bool of_CASSIGN_VEC4_OFF(vthread_t thr, vvp_code_t cp) if (thr->flags[4] == BIT4_1) return true; + /* Remove any previous continuous assign to this net. */ + cassign_unlink(net); + vvp_signal_value*sig = dynamic_cast (net->fil); assert(sig); @@ -1873,6 +1890,9 @@ bool of_CASSIGN_WR(vthread_t thr, vvp_code_t cp) vvp_net_t*net = cp->net; double value = thr->pop_real(); + /* Remove any previous continuous assign to this net. */ + cassign_unlink(net); + /* Set the value into port 1 of the destination. */ vvp_net_ptr_t ptr (net, 1); vvp_send_real(ptr, value, 0); diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index c87e26ef9..a9df069a2 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-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 @@ -2364,7 +2364,7 @@ vvp_vector2_t pow(const vvp_vector2_t&x, vvp_vector2_t&y) { /* If we have a zero exponent just return 1. */ if (y == vvp_vector2_t(0L, 1)) { - return vvp_vector2_t(1L, x.size()); + return vvp_vector2_t(1L, x.size()); } /* Is the value odd? */