diff --git a/PDelays.cc b/PDelays.cc index c547a2c08..e39a57122 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -101,7 +101,7 @@ static NetExpr*calculate_val(Design*des, NetScope*scope, const PExpr*expr) return dex; } -static NetExpr* make_delay_nets(Design*des, NetExpr*expr) +static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr) { if (dynamic_cast (expr)) return expr; @@ -109,7 +109,7 @@ static NetExpr* make_delay_nets(Design*des, NetExpr*expr) if (dynamic_cast (expr)) return expr; - NetNet*sig = expr->synthesize(des); + NetNet*sig = expr->synthesize(des, scope); if (sig == 0) { cerr << expr->get_fileline() << ": error: Expression " << *expr << " is not suitable for delay expression." << endl; @@ -132,17 +132,17 @@ void PDelays::eval_delays(Design*des, NetScope*scope, if (delay_[0]) { rise_time = calculate_val(des, scope, delay_[0]); if (as_nets_flag) - rise_time = make_delay_nets(des, rise_time); + rise_time = make_delay_nets(des, scope, rise_time); if (delay_[1]) { fall_time = calculate_val(des, scope, delay_[1]); if (as_nets_flag) - fall_time = make_delay_nets(des, fall_time); + fall_time = make_delay_nets(des, scope, fall_time); if (delay_[2]) { decay_time = calculate_val(des, scope, delay_[2]); if (as_nets_flag) - decay_time = make_delay_nets(des, decay_time); + decay_time = make_delay_nets(des, scope, decay_time); } else { if (rise_time < fall_time) diff --git a/PExpr.h b/PExpr.h index b2a4f9a9a..3ef8f331f 100644 --- a/PExpr.h +++ b/PExpr.h @@ -53,7 +53,7 @@ class PExpr : public LineInfo { // be. It is used by elaboration of assignments to figure out // the width of the expression. // - // The "min" is the width of the local context, so it the + // The "min" is the width of the local context, so is the // minimum width that this function should return. Initially // this is the same as the lval width. // @@ -492,6 +492,10 @@ class PEUnary : public PExpr { virtual void dump(ostream&out) const; + virtual unsigned test_width(Design*des, NetScope*scope, + unsigned min, unsigned lval, + bool&unsized_flag) const; + virtual bool elaborate_sig(Design*des, NetScope*scope) const; virtual NetNet* elaborate_net(Design*des, NetScope*scope, @@ -575,6 +579,10 @@ class PEBinary : public PExpr { NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; + NetExpr*elaborate_expr_base_lshift_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; + NetExpr*elaborate_expr_base_rshift_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; + NetExpr*elaborate_expr_base_add_(Design*, NetExpr*lp, NetExpr*rp, int use_wid) const; + static void suppress_operand_sign_if_needed_(NetExpr*lp, NetExpr*rp); private: @@ -679,7 +687,7 @@ class PETernary : public PExpr { const NetExpr* decay, Link::strength_t drive0, Link::strength_t drive1) const; - virtual NetETernary*elaborate_expr(Design*des, NetScope*, + virtual NetExpr*elaborate_expr(Design*des, NetScope*, int expr_width, bool sys_task_arg) const; virtual NetETernary*elaborate_pexpr(Design*des, NetScope*sc) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; diff --git a/Statement.h b/Statement.h index 10def60d2..b82d6e456 100644 --- a/Statement.h +++ b/Statement.h @@ -103,6 +103,7 @@ class PAssign_ : public Statement { protected: NetAssign_* elaborate_lval(Design*, NetScope*scope) const; + NetExpr* elaborate_rval_(Design*, NetScope*, unsigned lv_width) const; PExpr* delay_; PEventStatement*event_; diff --git a/design_dump.cc b/design_dump.cc index 25e61d04e..f17ee9605 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -567,8 +567,14 @@ void NetReplicate::dump_node(ostream&o, unsigned ind) const void NetSignExtend::dump_node(ostream&o, unsigned ind) const { - o << setw(ind) << "" << "NetSignExtend: " - << name() << " output width=" << width_ << endl; + o << setw(ind) << "" << "NetSignExtend: " << name(); + if (rise_time()) + o << " #(" << *rise_time() + << "," << *fall_time() + << "," << *decay_time() << ")"; + else + o << " #(.,.,.)"; + o << " output width=" << width_ << endl; dump_node_pins(o, ind+4); dump_obj_attr(o, ind+4); } diff --git a/elab_expr.cc b/elab_expr.cc index ee7cbc444..e345d3ea4 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -148,8 +148,8 @@ NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des, * the correct NetEBinary object and connect the parameters. */ NetExpr* PEBinary::elaborate_expr_base_(Design*des, - NetExpr*lp, NetExpr*rp, - int expr_wid) const + NetExpr*lp, NetExpr*rp, + int expr_wid) const { bool flag; @@ -180,6 +180,13 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, break; case '*': + // Multiply will guess a width that is the sum of the + // widths of the operand. If that sum is too small, then + // pad one of the arguments enough that the sum is the + // desired width. + if (expr_wid > (long)(lp->expr_width() + rp->expr_width())) + lp = pad_to_width(lp, expr_wid - rp->expr_width()); + tmp = new NetEBMult(op_, lp, rp); tmp->set_line(*this); break; @@ -203,60 +210,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, break; case 'l': // << - if (NetEConst*lpc = dynamic_cast (lp)) { - if (NetEConst*rpc = dynamic_cast (rp)) { - // Handle the super-special case that both - // operands are constants. Precalculate the - // entire value here. - verinum lpval = lpc->value(); - unsigned shift = rpc->value().as_ulong(); - verinum result = lpc->value() << shift; - // If the l-value has explicit size, or - // there is a context determined size, use that. - if (lpval.has_len() || expr_wid > 0) { - int use_len = lpval.len(); - if (expr_wid < use_len) - use_len = expr_wid; - result = verinum(result, lpval.len()); - } - - tmp = new NetEConst(result); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Precalculate " << *this - << " to constant " << *tmp << endl; - - } else { - // Handle the special case that the left - // operand is constant. If it is unsized, we - // may have to expand it to an integer width. - verinum lpval = lpc->value(); - if (lpval.len() < integer_width && !lpval.has_len()) { - lpval = verinum(lpval, integer_width); - lpc = new NetEConst(lpval); - lpc->set_line(*lp); - } - - tmp = new NetEBShift(op_, lpc, rp); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Adjust " << *this - << " to this " << *tmp - << " to allow for integer widths." << endl; - } - - } else { - // Left side is not constant, so handle it the - // default way. - tmp = new NetEBShift(op_, lp, rp); - } - tmp->set_line(*this); + tmp = elaborate_expr_base_lshift_(des, lp, rp, expr_wid); break; case 'r': // >> case 'R': // >>> - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); + tmp = elaborate_expr_base_rshift_(des, lp, rp, expr_wid); break; case '^': @@ -271,11 +230,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case '+': case '-': - tmp = new NetEBAdd(op_, lp, rp, expr_wid==-2? true : false); - if (expr_wid > 0 && (tmp->expr_type() == IVL_VT_BOOL - || tmp->expr_type() == IVL_VT_LOGIC)) - tmp->set_width(expr_wid); - tmp->set_line(*this); + tmp = elaborate_expr_base_add_(des, lp, rp, expr_wid); break; case 'E': /* === */ @@ -323,6 +278,224 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, return tmp; } +NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, + NetExpr*lp, NetExpr*rp, + int expr_wid) const +{ + NetExpr*tmp; + + long use_wid = lp->expr_width(); + if (expr_wid > 0) + use_wid = expr_wid; + + if (use_wid == 0) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Oops, left expression width is not known, " + << "so expression width is not known. Punt." << endl; + tmp = new NetEBShift(op_, lp, rp); + tmp->set_line(*this); + return tmp; + } + + // If the left expression is constant, then there are some + // special cases we can work with. If the left expression is + // not constant, but the right expression is constant, then + // there are some other interesting cases. But if neither are + // constant, then there is the general case. + + if (NetEConst*lpc = dynamic_cast (lp)) { + if (NetEConst*rpc = dynamic_cast (rp)) { + // Handle the super-special case that both + // operands are constants. Precalculate the + // entire value here. + verinum lpval = lpc->value(); + unsigned shift = rpc->value().as_ulong(); + verinum result = lpc->value() << shift; + // If the l-value has explicit size, or + // there is a context determined size, use that. + if (lpval.has_len() || expr_wid > 0) { + int use_len = lpval.len(); + if (expr_wid > 0 && expr_wid > use_len) + use_len = expr_wid; + result = verinum(result, use_len); + } + + tmp = new NetEConst(result); + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Precalculate " << *lpc << " << " << shift + << " to constant " << *tmp + << " (expr_wid=" << expr_wid << ")" << endl; + + } else { + // Handle the special case that the left + // operand is constant. If it is unsized, we + // may have to expand it to an integer width. + verinum lpval = lpc->value(); + if (lpval.len() < integer_width && !lpval.has_len()) { + lpval = verinum(lpval, integer_width); + lpc = new NetEConst(lpval); + lpc->set_line(*lp); + } + + tmp = new NetEBShift(op_, lpc, rp); + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Adjust " << *this + << " to this " << *tmp + << " to allow for integer widths." << endl; + } + + } else if (NetEConst*rpc = dynamic_cast (rp)) { + long shift = rpc->value().as_long(); + long use_wid = lp->expr_width(); + if (expr_wid > 0) + use_wid = expr_wid; + + if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Value left-shifted " << shift + << " beyond width of " << use_wid + << ". Elaborate as constant zero." << endl; + + tmp = make_const_0(use_wid); + + } else { + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Left shift expression by constant " + << shift << " bits. (use_wid=" << use_wid << ")" << endl; + lp = pad_to_width(lp, use_wid); + tmp = new NetEBShift(op_, lp, rp); + } + + } else { + // Left side is not constant, so handle it the + // default way. + if (expr_wid >= 0) + lp = pad_to_width(lp, expr_wid); + tmp = new NetEBShift(op_, lp, rp); + } + + tmp->set_line(*this); + return tmp; +} + +NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, + NetExpr*lp, NetExpr*rp, + int expr_wid) const +{ + NetExpr*tmp; + + long use_wid = lp->expr_width(); + if (expr_wid > 0) + use_wid = expr_wid; + + if (use_wid == 0) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Oops, left expression width is not known, " + << "so expression width is not known. Punt." << endl; + tmp = new NetEBShift(op_, lp, rp); + tmp->set_line(*this); + return tmp; + } + + if (NetEConst*rpc = dynamic_cast (rp)) { + long shift = rpc->value().as_long(); + + // Detect the special cases that the shifted + // unsigned expression is completely shifted away to + // zero. + if ((op_=='r' || (lp->has_sign()==false)) + && shift >= (long)lp->expr_width()) { + // Special case that the value is unsigned + // shifted completely away. + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Value right-shifted " << shift + << " beyond width of " << lp->expr_width() + << ". Elaborate as constant zero." << endl; + + tmp = make_const_0(use_wid); + tmp->set_line(*this); + return tmp; + + } + + if (shift >= (long)lp->expr_width()) { + // Signed right shift. + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Value signed-right-shifted " << shift + << " beyond width of " << lp->expr_width() + << ". Elaborate as replicated top bit." << endl; + + tmp = new NetEConst(verinum(lp->expr_width()-1)); + tmp->set_line(*this); + tmp = new NetESelect(lp, tmp, 1); + tmp->cast_signed(true); + tmp->set_line(*this); + tmp = pad_to_width(tmp, use_wid); + tmp->set_line(*this); + return tmp; + + } else if (shift >= 0) { + // Signed right shift. + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Value signed-right-shifted " << shift + << " beyond width of " << lp->expr_width() + << "." << endl; + + tmp = new NetEConst(verinum(shift)); + tmp->set_line(*this); + long tmp_wid = lp->expr_width() - shift; + if (tmp_wid > use_wid) + tmp_wid = use_wid; + tmp = new NetESelect(lp, tmp, tmp_wid); + tmp->set_line(*this); + tmp->cast_signed(lp->has_sign() && op_=='R'); + tmp = pad_to_width(tmp, use_wid); + tmp->set_line(*this); + return tmp; + + } else if ((0-shift) >= use_wid) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Value signed-right-shifted " << shift + << " beyond width of " << use_wid + << "." << endl; + + tmp = make_const_0(use_wid); + tmp->set_line(*this); + return tmp; + } + } + + // Falback, handle the general case. + if (expr_wid > 0) + lp = pad_to_width(lp, expr_wid); + tmp = new NetEBShift(op_, lp, rp); + tmp->set_line(*this); + return tmp; +} + +NetExpr* PEBinary::elaborate_expr_base_add_(Design*des, + NetExpr*lp, NetExpr*rp, + int expr_wid) const +{ + NetExpr*tmp; + tmp = new NetEBAdd(op_, lp, rp, expr_wid==-2? true : false); + if (expr_wid > 0 && (tmp->expr_type() == IVL_VT_BOOL + || tmp->expr_type() == IVL_VT_LOGIC)) + tmp->set_width(expr_wid); + tmp->set_line(*this); + return tmp; +} + unsigned PEBComp::test_width(Design*, NetScope*,unsigned, unsigned, bool&) const { return 1; @@ -376,6 +549,11 @@ unsigned PEBShift::test_width(Design*des, NetScope*scope, // The right expression is self-determined and has no impact // on the expression size that is generated. + if (wid_left < min) + wid_left = min; + if (wid_left < lval) + wid_left = lval; + return wid_left; } @@ -995,36 +1173,70 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, symbol_search(des, scope, path_, net, par, eve, ex1, ex2); - if (net != 0) { - const name_component_t&name_tail = path_.back(); - index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; - if (!name_tail.index.empty()) - use_sel = name_tail.index.back().sel; + // If there is a part/bit select expression, then process it + // here. This constrains the results no matter what kind the + // name is. - unsigned use_width = net->vector_width(); - switch (use_sel) { - case index_component_t::SEL_NONE: - break; - case index_component_t::SEL_PART: - { long msb, lsb; - calculate_parts_(des, scope, msb, lsb); - use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb)); - break; - } - case index_component_t::SEL_IDX_UP: - case index_component_t::SEL_IDX_DO: - { unsigned long tmp = 0; - calculate_up_do_width_(des, scope, tmp); - use_width = tmp; - break; - } - case index_component_t::SEL_BIT: - use_width = 1; - break; - default: - ivl_assert(*this, 0); - } + const name_component_t&name_tail = path_.back(); + index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; + if (!name_tail.index.empty()) + use_sel = name_tail.index.back().sel; + + unsigned use_width = UINT_MAX; + switch (use_sel) { + case index_component_t::SEL_NONE: + break; + case index_component_t::SEL_PART: + { long msb, lsb; + calculate_parts_(des, scope, msb, lsb); + use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb)); + break; + } + case index_component_t::SEL_IDX_UP: + case index_component_t::SEL_IDX_DO: + { unsigned long tmp = 0; + calculate_up_do_width_(des, scope, tmp); + use_width = tmp; + break; + } + case index_component_t::SEL_BIT: + use_width = 1; + break; + default: + ivl_assert(*this, 0); + } + + if (use_width != UINT_MAX) return use_width; + + // The width of a signal expression is the width of the signal. + if (net != 0) + return net->vector_width(); + + // The width of a parameter name is the width of the range for + // the parameter name, if a range is declared. Otherwise, the + // width is undefined. + if (par != 0) { + if (ex1) { + ivl_assert(*this, ex2); + const NetEConst*ex1_const = dynamic_cast (ex1); + const NetEConst*ex2_const = dynamic_cast (ex2); + ivl_assert(*this, ex1_const && ex2_const); + + long msb = ex1_const->value().as_long(); + long lsb = ex2_const->value().as_long(); + if (msb >= lsb) + return msb - lsb + 1; + else + return lsb - msb + 1; + } + + // This is a parameter. If it is sized (meaning it was + // declared with range expresions) then the range + // expressions would have been caught above. So if we + // got there there we know this is an unsized constant. + unsized_flag = true; + return par->expr_width(); } return min; @@ -1123,6 +1335,22 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } } + if (error_implicit==false + && sys_task_arg==false + && path_.size()==1 + && scope->default_nettype() != NetNet::NONE) { + NetNet::Type nettype = scope->default_nettype(); + net = new NetNet(scope, peek_tail_name(path_), nettype, 1); + net->data_type(IVL_VT_LOGIC); + net->set_line(*this); + if (warn_implicit) { + cerr << get_fileline() << ": warning: implicit " + "definition of wire " << scope_path(scope) + << "." << peek_tail_name(path_) << "." << endl; + } + return elaborate_expr_net(des, scope, net, scope, sys_task_arg); + } + // At this point we've exhausted all the possibilities that // are not scopes. If this is not a system task argument, then // it cannot be a scope name, so give up. @@ -1943,7 +2171,7 @@ static bool test_ternary_operand_compat(ivl_variable_type_t l, * parsed so I can presume that they exist, and call elaboration * methods. If any elaboration fails, then give up and return 0. */ -NetETernary*PETernary::elaborate_expr(Design*des, NetScope*scope, +NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, int expr_wid, bool) const { assert(expr_); @@ -1962,17 +2190,54 @@ NetETernary*PETernary::elaborate_expr(Design*des, NetScope*scope, << " and " << fal_wid << endl; } - NetExpr*con = expr_->elaborate_expr(des, scope, -1, false); + // Elaborate and evaluate the condition expression. Note that + // it is always self-determined. + NetExpr*con = elab_and_eval(des, scope, expr_, -1); if (con == 0) return 0; - NetExpr*tru = tru_->elaborate_expr(des, scope, expr_wid, false); + /* Make sure the condition expression reduces to a single bit. */ + con = condition_reduce(con); + + // Verilog doesn't say that we must do short circuit + // evaluation of ternary expressions, but it doesn't disallow + // it. The disadvantage of doing this is that semantic errors + // in the unused clause will be missed, but people don't seem + // to mind, and do apreciate the optimization available here. + if (NetEConst*tmp = dynamic_cast (con)) { + verinum cval = tmp->value(); + ivl_assert(*this, cval.len()==1); + + // Condition is constant TRUE, so we only need the true claue. + if (cval.get(0) == verinum::V1) { + cerr << get_fileline() << ": debug: " + << "Short-circuit elaborate TRUE clause of ternary." + << endl; + NetExpr*tru = elab_and_eval(des, scope, tru_, expr_wid); + return pad_to_width(tru, expr_wid); + } + + // Condition is constant FALSE, so we only need the + // false clause. + if (cval.get(0) == verinum::V0) { + cerr << get_fileline() << ": debug: " + << "Short-circuit elaborate FALSE clause of ternary." + << endl; + NetExpr*fal = elab_and_eval(des, scope, fal_, expr_wid); + return pad_to_width(fal, expr_wid); + } + + // X and Z conditions need to blend both results, so we + // can't short-circuit. + } + + NetExpr*tru = elab_and_eval(des, scope, tru_, expr_wid); if (tru == 0) { delete con; return 0; } - NetExpr*fal = fal_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*fal = elab_and_eval(des, scope, fal_, expr_wid); if (fal == 0) { delete con; delete tru; @@ -1998,6 +2263,38 @@ NetETernary*PETernary::elaborate_expr(Design*des, NetScope*scope, return res; } +unsigned PEUnary::test_width(Design*des, NetScope*scope, + unsigned min, unsigned lval, + bool&unsized_flag) const +{ + switch (op_) { + case '!': + case '&': + case '|': // Reduction OR + case '^': // Reduction XOR + case 'A': // Reduction NAND (~&) + case 'N': // Reduction NOR (~|) + case 'X': // Reduction NXOR (~^) + return 1; + } + + unsigned test_wid = expr_->test_width(des, scope, min, lval, unsized_flag); + switch (op_) { + // For these operators, the act of padding to the + // minimum width can have an important impact on the + // calculation. So don't let the tested width be less + // then the tested width. + case '-': + case '+': + if (test_wid < min) + test_wid = min; + break; + } + + return test_wid; +} + + NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, int expr_wid, bool) const { @@ -2055,6 +2352,8 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, delete ip; } else { + if (expr_wid > 0) + ip = pad_to_width(ip, expr_wid); tmp = new NetEUnary(op_, ip); tmp->set_line(*this); } diff --git a/elab_net.cc b/elab_net.cc index 23dd1e1f7..a94446fe8 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -355,7 +355,7 @@ static NetNet* compare_eq_constant(Design*des, NetScope*scope, : verinum::V1, 1); NetEConst*ogate = new NetEConst(oval); - NetNet*osig = ogate->synthesize(des); + NetNet*osig = ogate->synthesize(des, scope); osig->data_type(lsig->data_type()); osig->set_line(*lsig); osig->rise_time(rise); @@ -484,7 +484,7 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, use of the situation, or 0 if it cannot. */ if (NetEConst*tmp = dynamic_cast(rexp)) { - lsig = lexp->synthesize(des); + lsig = lexp->synthesize(des, scope); if (lsig == 0) return 0; delete lexp; lexp = 0; @@ -492,7 +492,7 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, if (real_arg) { verireal vrl(tmp->value().as_double()); NetECReal rlval(vrl); - rsig = rlval.synthesize(des); + rsig = rlval.synthesize(des, scope); delete rexp; rexp = 0; } else { @@ -508,7 +508,7 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, if (NetEConst*tmp = dynamic_cast(lexp)) { - rsig = rexp->synthesize(des); + rsig = rexp->synthesize(des, scope); if (rsig == 0) return 0; delete rexp; rexp = 0; @@ -516,7 +516,7 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, if (real_arg) { verireal vrl(tmp->value().as_double()); NetECReal rlval(vrl); - lsig = rlval.synthesize(des); + lsig = rlval.synthesize(des, scope); delete lexp; lexp = 0; } else { @@ -531,13 +531,13 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope, } if (lsig == 0) { - lsig = lexp->synthesize(des); + lsig = lexp->synthesize(des, scope); if (lsig == 0) return 0; delete lexp; } if (rsig == 0) { - rsig = rexp->synthesize(des); + rsig = rexp->synthesize(des, scope); if (rsig == 0) return 0; delete rexp; } @@ -1737,14 +1737,14 @@ NetNet* PEIdent::elaborate_net_bitmux_(Design*des, NetScope*scope, sel_expr = make_sub_expr(sig->lsb(), sel_expr); eval_expr(sel_expr); - sel = sel_expr->synthesize(des); + sel = sel_expr->synthesize(des, scope); } else if (sig->lsb() != 0) { NetExpr*sel_expr = index_tail.msb->elaborate_expr(des, scope, -1,false); sel_expr = make_add_expr(sel_expr, - sig->lsb()); eval_expr(sel_expr); - sel = sel_expr->synthesize(des); + sel = sel_expr->synthesize(des, scope); } else { sel = index_tail.msb->elaborate_net(des, scope, 0, 0, 0, 0); @@ -2188,7 +2188,7 @@ NetNet* PEIdent::elaborate_net_net_idx_up_(Design*des, NetScope*scope, base = make_sub_expr(vwid-offset-wid, base); } - NetPartSelect*sel = new NetPartSelect(sig, base->synthesize(des), wid); + NetPartSelect*sel = new NetPartSelect(sig, base->synthesize(des, scope), wid); sel->set_line(*this); des->add_node(sel); @@ -2281,7 +2281,7 @@ NetNet* PEIdent::elaborate_net_array_(Design*des, NetScope*scope, index_ex = make_add_expr(index_ex, 0-array_base); } - NetNet*index_net = index_ex->synthesize(des); + NetNet*index_net = index_ex->synthesize(des, scope); connect(mux->pin_Address(), index_net->pin(0)); NetNet*tmp = new NetNet(scope, scope->local_symbol(), @@ -3159,7 +3159,7 @@ NetNet* PETernary::elaborate_net(Design*des, NetScope*scope, * on this for now. */ break; } - expr_sig = expr->synthesize(des); + expr_sig = expr->synthesize(des, scope); if (expr_sig == 0 || tru_sig == 0 || fal_sig == 0) return 0; @@ -3355,7 +3355,7 @@ NetNet* PEUnary::elaborate_net(Design*des, NetScope*scope, drive0, drive1); } - NetNet* sub_sig = expr->synthesize(des); + NetNet* sub_sig = expr->synthesize(des, scope); if (sub_sig == 0) return 0; @@ -3621,7 +3621,7 @@ NetNet* PEUnary::elab_net_unary_real_(Design*des, NetScope*scope, << *this << "."<synthesize(des); + NetNet* sub_sig = expr->synthesize(des, scope); if (sub_sig == 0) return 0; delete expr; diff --git a/elaborate.cc b/elaborate.cc index e693f45d5..43b3c860f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -91,7 +91,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const return; } - assert(lval->pin_count() == 1); + ivl_assert(*this, lval->pin_count() == 1); if (debug_elaborate) { cerr << get_fileline() << ": debug: PGAssign: elaborated l-value" @@ -99,208 +99,75 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const << ", type=" << lval->data_type() << endl; } - /* Handle the special case that the rval is simply an - identifier. Get the rval as a NetNet, then use NetBUFZ - objects to connect it to the l-value. This is necessary to - direct drivers. This is how I attach strengths to the - assignment operation. */ - if (const PEIdent*id = dynamic_cast(pin(1))) { - NetNet*rid = id->elaborate_net(des, scope, lval->vector_width(), - 0, 0, 0, Link::STRONG, - Link::STRONG); - if (rid == 0) { - des->errors += 1; - return; - } + bool unsized_flag = false; + unsigned use_width = pin(1)->test_width(des, scope, lval->vector_width(), + lval->vector_width(), unsized_flag); - /* Cast the right side when needed. */ - if ((lval->data_type() == IVL_VT_REAL && - rid->data_type() != IVL_VT_REAL)) { - rid = cast_to_real(des, scope, rid); - } else if ((lval->data_type() != IVL_VT_REAL && - rid->data_type() == IVL_VT_REAL)) { - rid = cast_to_int(des, scope, rid, lval->vector_width()); - } - - ivl_assert(*this, rid); - if (rid->pin_count() != 1) { - cerr << get_fileline() << ": internal error: " - << "Invalid elaborate_net results here:" << endl; - rid->dump_net(cerr, 4); - des->errors += 1; - return; - } - ivl_assert(*this, rid->pin_count() == 1); - - /* If the right hand net is the same type as the left - side net (i.e., WIRE/WIRE) then it is enough to just - connect them together. Otherwise, put a bufz between - them to carry strengths from the rval. - - While we are at it, handle the case where the r-value - is not as wide as the l-value by padding with a - constant-0. */ - - unsigned cnt = lval->vector_width(); - if (rid->vector_width() < cnt) - cnt = rid->vector_width(); - - bool need_driver_flag = false; - - /* If the device is linked to itself, a driver is - needed. Should I print a warning here? */ - if (lval->pin(0) .is_linked (rid->pin(0))) - need_driver_flag = true; - - /* If the nets are different type (i.e., reg vs. tri) then - a driver is needed. */ - if (rid->type() != lval->type()) - need_driver_flag = true; - - /* If there is a delay, then I need a driver to carry - it. */ - if (rise_time || fall_time || decay_time) - need_driver_flag = true; - - /* If there is a strength to be carried, then I need a - driver to carry that strength. */ - if (rid->pin(0).drive0() != drive0) - need_driver_flag = true; - - if (rid->pin(0).drive1() != drive1) - need_driver_flag = true; - - /* If the r-value is more narrow then the l-value, pad - it to the desired width. */ - if (cnt < lval->vector_width()) { - if (lval->get_signed() && rid->get_signed()) { - - unsigned use_width = lval->vector_width(); - - if (debug_elaborate) - cerr << get_fileline() << ": debug: PGassign " - << "Generate sign-extend node." << endl; - - rid = pad_to_width_signed(des, rid, use_width); - - } else { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: PGAssign " - << "Unsigned pad r-value from " - << cnt << " bits to " - << lval->vector_width() << " bits." << endl; - - NetNet*tmp = pad_to_width(des, rid, - lval->vector_width()); - rid = tmp; - } - - } else if (cnt < rid->vector_width()) { - - if (debug_elaborate) - cerr << get_fileline() << ": debug: PGAssign " - << "Truncate r-value from " - << cnt << " bits to " - << lval->vector_width() << " bits." << endl; - - NetNet*tmp = crop_to_width(des, rid, lval->vector_width()); - rid = tmp; - } - - if (! need_driver_flag) { - - /* Don't need a driver, presumably because the - r-value already has the needed drivers. Just - hook things up. If the r-value is too narrow - for the l-value, then sign extend it or zero - extend it, whichever makes sense. */ - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: PGAssign: " - << "Connect lval directly to " - << id->path() << endl; - } - - connect(lval->pin(0), rid->pin(0)); - - } else { - /* Do need a driver. Use BUFZ objects to carry the - strength and delays. */ - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: PGAssign: " - << "Connect lval to " << id->path() - << " through bufz. delay=("; - if (rise_time) - cerr << *rise_time << ":"; - else - cerr << ":"; - if (fall_time) - cerr << *fall_time << ":"; - else - cerr << ":"; - if (decay_time) - cerr << *decay_time; - else - cerr << ""; - cerr << ")" << endl; - } - - NetBUFZ*dev = new NetBUFZ(scope, scope->local_symbol(), - rid->vector_width()); - connect(lval->pin(0), dev->pin(0)); - connect(rid->pin(0), dev->pin(1)); - dev->rise_time(rise_time); - dev->fall_time(fall_time); - dev->decay_time(decay_time); - dev->pin(0).drive0(drive0); - dev->pin(0).drive1(drive1); - des->add_node(dev); - - } - - return; + if (debug_elaborate) { + cerr << get_fileline() << ": debug: PGAssign: r-value tested " + << "width is " << use_width + << ", min=" << lval->vector_width() + << ", unsized_flag=" << (unsized_flag?"true":"false") << endl; } - /* Elaborate the r-value. Account for the initial decays, - which are going to be attached to the last gate before the - generated NetNet. */ - NetNet*rval = pin(1)->elaborate_net(des, scope, - lval->vector_width(), - 0, 0, 0, - drive0, drive1); - if (rval == 0) { + int expr_wid = unsized_flag? -1 : use_width; + NetExpr*rval_expr = elab_and_eval(des, scope, pin(1), + expr_wid, lval->vector_width()); + + if (rval_expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate r-value: " << *pin(1) << endl; des->errors += 1; return; } + NetNet*rval = rval_expr->synthesize(des, scope); + + if (rval == 0) { + cerr << get_fileline() << ": internal error: " + << "Failed to synthesize expression: " << *rval_expr << endl; + des->errors += 1; + return; + } + if (debug_elaborate) { cerr << get_fileline() << ": debug: PGAssign: elaborated r-value" - << " width="<vector_width() - << ", type="<< rval->data_type() << endl; + << " width="<< rval->vector_width() + << ", type="<< rval->data_type() + << ", expr=" << *rval_expr << endl; } assert(lval && rval); assert(rval->pin_count() == 1); + // Detect the case that the rvalue-expression is a simple + // expression. In this case, we will need to create a driver + // (later) to carry strengths. + bool need_driver_flag = false; + if (dynamic_cast(rval_expr)) + need_driver_flag = true; + /* Cast the right side when needed. */ if ((lval->data_type() == IVL_VT_REAL && rval->data_type() != IVL_VT_REAL)) { rval = cast_to_real(des, scope, rval); + need_driver_flag = false; } else if ((lval->data_type() != IVL_VT_REAL && rval->data_type() == IVL_VT_REAL)) { rval = cast_to_int(des, scope, rval, lval->vector_width()); + need_driver_flag = false; } /* If the r-value insists on being smaller then the l-value (perhaps it is explicitly sized) the pad it out to be the right width so that something is connected to all the bits of the l-value. */ - if (lval->vector_width() > rval->vector_width()) - rval = pad_to_width(des, rval, lval->vector_width()); + if (lval->vector_width() > rval->vector_width()) { + if (rval->get_signed()) + rval = pad_to_width_signed(des, rval, lval->vector_width()); + else + rval = pad_to_width(des, rval, lval->vector_width()); + } /* If, on the other hand, the r-value insists on being LARGER then the l-value, use a part select to chop it down @@ -313,17 +180,40 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, lval->vector_width()); osig->set_line(*this); + osig->local_flag(true); osig->data_type(rval->data_type()); connect(osig->pin(0), tmp->pin(0)); rval = osig; + need_driver_flag = false; } - /* If there is a rise/fall/decay time, then attach that delay - to the drivers for this net. */ - if (rise_time || fall_time || decay_time) { - rval->pin(0).drivers_delays(rise_time, fall_time, decay_time); + if (need_driver_flag) { + NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(), + rval->vector_width()); + driver->set_line(*this); + des->add_node(driver); + + connect(rval->pin(0), driver->pin(1)); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, rval->vector_width()); + tmp->set_line(*this); + tmp->data_type(rval->data_type()); + tmp->local_flag(true); + + connect(driver->pin(0), tmp->pin(0)); + + rval = tmp; } + /* Set the drive and delays for the r-val. */ + + if (drive0 != Link::STRONG || drive1 != Link::STRONG) + rval->pin(0).drivers_drive(drive0, drive1); + + if (rise_time || fall_time || decay_time) + rval->pin(0).drivers_delays(rise_time, fall_time, decay_time); + connect(lval->pin(0), rval->pin(0)); if (lval->local_flag()) @@ -1705,6 +1595,29 @@ NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const return lval_->elaborate_lval(des, scope, false); } +NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, + unsigned lv_width) const +{ + ivl_assert(*this, rval_); + + + /* Find out what the r-value width is going to be. We guess it + will be the l-value width, but it may turn out to be + something else based on self-determined widths inside. */ + unsigned use_width = lv_width; + bool unsized_flag = false; + unsigned tmp_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag); + if (tmp_width > use_width) + use_width = tmp_width; + + /* Now elaborate to the expected width. Pass the lwidth to + prune any constant result to fit with the lvalue at hand. */ + NetExpr*rv = elab_and_eval(des, scope, rval_, use_width, lv_width); + if (rv == 0) return 0; + + return rv; +} + /* * This function elaborates delay expressions. This is a little * different from normal elaboration because the result may need to be @@ -1786,20 +1699,8 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const delay = elaborate_delay_expr(delay_, des, scope); - assert(rval()); - /* Elaborate the r-value expression, then try to evaluate it. */ - - /* Find out what the r-value width is going to be. We guess it - will be the l-value width, but it may turn out to be - something else based on self-determined widths inside. */ - unsigned use_width = lv->lwidth(); - bool unsized_flag = false; - use_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag); - - /* Now elaborate to the expected width. Pass the lwidth to - prune any constant result to fit with the lvalue at hand. */ - NetExpr*rv = elab_and_eval(des, scope, rval(), use_width, lv->lwidth()); + NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv)); if (rv == 0) return 0; assert(rv); @@ -1964,12 +1865,7 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; - assert(rval()); - - /* Elaborate and precalculate the r-value. */ - NetExpr*rv = elab_and_eval(des, scope, rval(), count_lval_width(lv)); - if (rv == 0) - return 0; + NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv)); /* Handle the (common) case that the r-value is a vector. This includes just about everything but reals. In this case, we @@ -3515,7 +3411,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const // FIXME: Look for constant expressions here? // Get a net form. - condit_sig = tmp->synthesize(des); + condit_sig = tmp->synthesize(des, scope); ivl_assert(*condition, condit_sig); } @@ -3658,6 +3554,7 @@ static void elaborate_tasks(Design*des, NetScope*scope, bool Module::elaborate(Design*des, NetScope*scope) const { bool result_flag = true; + error_implicit = true; if (gn_specify_blocks_flag) { // Elaborate specparams @@ -3722,6 +3619,7 @@ bool Module::elaborate(Design*des, NetScope*scope) const // complex. const list&gl = get_gates(); + error_implicit = false; for (list::const_iterator gt = gl.begin() ; gt != gl.end() ; gt ++ ) { @@ -3729,6 +3627,8 @@ bool Module::elaborate(Design*des, NetScope*scope) const (*gt)->elaborate(des, scope); } + error_implicit = true; + // Elaborate the behaviors, making processes out of them. This // involves scanning the PProcess* list, creating a NetProcTop // for each process. diff --git a/eval_tree.cc b/eval_tree.cc index 5c8630621..e8f045b2b 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -528,6 +528,13 @@ NetEConst* NetEBComp::eval_gteq_() } } +/* + * Evaluate == or !=. The equality operator checks all the + * bits and returns true(false) if there are any bits in the vector + * that are defined (0 or 1) and different. If all the defined bits + * are equal, but there are are x/z bits, then the situation is + * ambiguous so the result is x. + */ NetEConst* NetEBComp::eval_eqeq_real_(NetExpr*le, NetExpr*ri, bool ne_flag) { NetEConst*vtmp; @@ -606,11 +613,14 @@ NetEConst* NetEBComp::eval_eqeq_(bool ne_flag) for (unsigned idx = 0 ; idx < top ; idx += 1) { + bool x_bit_present = false; + switch (lv.get(idx)) { case verinum::Vx: case verinum::Vz: res = verinum::Vx; + x_bit_present = true; break; default: @@ -622,17 +632,20 @@ NetEConst* NetEBComp::eval_eqeq_(bool ne_flag) case verinum::Vx: case verinum::Vz: res = verinum::Vx; + x_bit_present = true; break; default: break; } - if (res == verinum::Vx) - break; + if (x_bit_present) + continue; - if (rv.get(idx) != lv.get(idx)) + if (rv.get(idx) != lv.get(idx)) { res = ne_res; + break; + } } if (res != verinum::Vx) { diff --git a/expr_synth.cc b/expr_synth.cc index 4bec199dc..49ab9b306 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -26,14 +26,14 @@ # include "netmisc.h" # include "ivl_assert.h" -NetNet* convert_to_real_const(Design*des, NetExpr*expr, NetExpr*obj) +static NetNet* convert_to_real_const(Design*des, NetScope*scope, NetExpr*expr, NetExpr*obj) { NetNet* sig; if (NetEConst*tmp = dynamic_cast(expr)) { verireal vrl(tmp->value().as_double()); NetECReal rlval(vrl); - sig = rlval.synthesize(des); + sig = rlval.synthesize(des, scope); } else { cerr << obj->get_fileline() << ": sorry: Cannot convert " "bit based value (" << *expr << ") to real." << endl; @@ -45,9 +45,10 @@ NetNet* convert_to_real_const(Design*des, NetExpr*expr, NetExpr*obj) } /* Note that lsig, rsig and real_args are references. */ -bool process_binary_args(Design*des, NetExpr*left, NetExpr*right, - NetNet*&lsig, NetNet*&rsig, bool&real_args, - NetExpr*obj) +static bool process_binary_args(Design*des, NetScope*scope, + NetExpr*left, NetExpr*right, + NetNet*&lsig, NetNet*&rsig, bool&real_args, + NetExpr*obj) { if (left->expr_type() == IVL_VT_REAL || right->expr_type() == IVL_VT_REAL) { @@ -56,20 +57,20 @@ bool process_binary_args(Design*des, NetExpr*left, NetExpr*right, /* Currently we will have a runtime assert if both expressions are not real, though we can convert constants. */ if (left->expr_type() == IVL_VT_REAL) { - lsig = left->synthesize(des); + lsig = left->synthesize(des, scope); } else { - lsig = convert_to_real_const(des, left, obj); + lsig = convert_to_real_const(des, scope, left, obj); } if (right->expr_type() == IVL_VT_REAL) { - rsig = right->synthesize(des); + rsig = right->synthesize(des, scope); } else { - rsig = convert_to_real_const(des, right, obj); + rsig = convert_to_real_const(des, scope, right, obj); } } else { real_args = false; - lsig = left->synthesize(des); - rsig = right->synthesize(des); + lsig = left->synthesize(des, scope); + rsig = right->synthesize(des, scope); } @@ -77,7 +78,7 @@ bool process_binary_args(Design*des, NetExpr*left, NetExpr*right, else return false; } -NetNet* NetExpr::synthesize(Design*des) +NetNet* NetExpr::synthesize(Design*des, NetScope*scope) { cerr << get_fileline() << ": internal error: cannot synthesize expression: " << *this << endl; @@ -88,13 +89,13 @@ NetNet* NetExpr::synthesize(Design*des) /* * Make an LPM_ADD_SUB device from addition operators. */ -NetNet* NetEBAdd::synthesize(Design*des) +NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope) { - assert((op()=='+') || (op()=='-')); + ivl_assert(*this, (op()=='+') || (op()=='-')); NetNet *lsig=0, *rsig=0; bool real_args=false; - if (process_binary_args(des, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -138,10 +139,10 @@ NetNet* NetEBAdd::synthesize(Design*des) * signals, then just connect a single gate to each bit of the vector * of the expression. */ -NetNet* NetEBBits::synthesize(Design*des) +NetNet* NetEBBits::synthesize(Design*des, NetScope*scope) { - NetNet*lsig = left_->synthesize(des); - NetNet*rsig = right_->synthesize(des); + NetNet*lsig = left_->synthesize(des, scope); + NetNet*rsig = right_->synthesize(des, scope); if (lsig == 0 || rsig == 0) return 0; @@ -154,9 +155,6 @@ NetNet* NetEBBits::synthesize(Design*des) return 0; } - NetScope*scope = lsig->scope(); - assert(scope); - unsigned width = lsig->vector_width(); if (rsig->vector_width() > width) width = rsig->vector_width(); @@ -205,13 +203,13 @@ NetNet* NetEBBits::synthesize(Design*des) return osig; } -NetNet* NetEBComp::synthesize(Design*des) +NetNet* NetEBComp::synthesize(Design*des, NetScope*scope) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -222,13 +220,16 @@ NetNet* NetEBComp::synthesize(Design*des) width = lsig->vector_width(); if (rsig->vector_width() > width) width = rsig->vector_width(); - lsig = pad_to_width(des, lsig, width); - rsig = pad_to_width(des, rsig, width); + if (lsig->get_signed()) + lsig = pad_to_width_signed(des, lsig, width); + else + lsig = pad_to_width(des, lsig, width); + if (rsig->get_signed()) + rsig = pad_to_width_signed(des, rsig, width); + else + rsig = pad_to_width(des, rsig, width); } - NetScope*scope = lsig->scope(); - assert(scope); - NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, 1); osig->set_line(*this); @@ -236,10 +237,30 @@ NetNet* NetEBComp::synthesize(Design*des) osig->data_type(IVL_VT_LOGIC); bool signed_compare = lsig->get_signed() && rsig->get_signed(); + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Comparison (" << op_ << ")" + << " is " << (signed_compare? "signed" : "unsigned") + << endl; + cerr << get_fileline() << ": : lsig is " + << (lsig->get_signed()? "signed" : "unsigned") + << " rsig is " << (rsig->get_signed()? "signed" : "unsigned") + << endl; + } + + if (op_ == 'E' || op_ == 'N') { + NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), + width, op_=='E'?true:false); + gate->set_line(*this); + connect(gate->pin(0), osig->pin(0)); + connect(gate->pin(1), lsig->pin(0)); + connect(gate->pin(2), rsig->pin(0)); + des->add_node(gate); + return osig; + } /* Handle the special case of a single bit equality operation. Make an XNOR gate instead of a comparator. */ - if ((width == 1) && ((op_ == 'e') || (op_ == 'E')) && !real_args) { + if ((width == 1) && (op_ == 'e') && !real_args) { NetLogic*gate = new NetLogic(scope, scope->local_symbol(), 3, NetLogic::XNOR, 1); gate->set_line(*this); @@ -253,7 +274,7 @@ NetNet* NetEBComp::synthesize(Design*des) /* Handle the special case of a single bit inequality operation. This is similar to single bit equality, but uses an XOR instead of an XNOR gate. */ - if ((width == 1) && ((op_ == 'n') || (op_ == 'N')) && !real_args) { + if ((width == 1) && (op_ == 'n') && !real_args) { NetLogic*gate = new NetLogic(scope, scope->local_symbol(), 3, NetLogic::XOR, 1); gate->set_line(*this); @@ -321,12 +342,12 @@ NetNet* NetEBComp::synthesize(Design*des) return osig; } -NetNet* NetEBPow::synthesize(Design*des) +NetNet* NetEBPow::synthesize(Design*des, NetScope*scope) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -334,9 +355,6 @@ NetNet* NetEBPow::synthesize(Design*des) if (real_args) width = 1; else width = expr_width(); - NetScope*scope = lsig->scope(); - assert(scope); - NetPow*powr = new NetPow(scope, scope->local_symbol(), width, lsig->vector_width(), rsig->vector_width()); @@ -359,12 +377,12 @@ NetNet* NetEBPow::synthesize(Design*des) return osig; } -NetNet* NetEBMult::synthesize(Design*des) +NetNet* NetEBMult::synthesize(Design*des, NetScope*scope) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -372,9 +390,6 @@ NetNet* NetEBMult::synthesize(Design*des) if (real_args) width = 1; else width = expr_width(); - NetScope*scope = lsig->scope(); - assert(scope); - NetMult*mult = new NetMult(scope, scope->local_symbol(), width, lsig->vector_width(), @@ -398,12 +413,12 @@ NetNet* NetEBMult::synthesize(Design*des) return osig; } -NetNet* NetEBDiv::synthesize(Design*des) +NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope) { NetNet *lsig=0, *rsig=0; unsigned width; bool real_args=false; - if (process_binary_args(des, left_, right_, lsig, rsig, + if (process_binary_args(des, scope, left_, right_, lsig, rsig, real_args, this)) { return 0; } @@ -411,12 +426,11 @@ NetNet* NetEBDiv::synthesize(Design*des) if (real_args) width = 1; else width = expr_width(); - NetScope*scope = lsig->scope(); - NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, width); osig->set_line(*this); osig->data_type(lsig->data_type()); + osig->set_signed(has_sign()); osig->local_flag(true); switch (op()) { @@ -427,6 +441,7 @@ NetNet* NetEBDiv::synthesize(Design*des) lsig->vector_width(), rsig->vector_width()); div->set_line(*this); + div->set_signed(has_sign()); des->add_node(div); connect(div->pin_DataA(), lsig->pin(0)); @@ -471,10 +486,10 @@ NetNet* NetEBDiv::synthesize(Design*des) return osig; } -NetNet* NetEBLogic::synthesize(Design*des) +NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope) { - NetNet*lsig = left_->synthesize(des); - NetNet*rsig = right_->synthesize(des); + NetNet*lsig = left_->synthesize(des, scope); + NetNet*rsig = right_->synthesize(des, scope); if (lsig == 0 || rsig == 0) return 0; @@ -487,9 +502,6 @@ NetNet* NetEBLogic::synthesize(Design*des) return 0; } - NetScope*scope = lsig->scope(); - assert(scope); - NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, 1); osig->data_type(expr_type()); @@ -551,11 +563,11 @@ NetNet* NetEBLogic::synthesize(Design*des) return osig; } -NetNet* NetEBShift::synthesize(Design*des) +NetNet* NetEBShift::synthesize(Design*des, NetScope*scope) { eval_expr(right_); - NetNet*lsig = left_->synthesize(des); + NetNet*lsig = left_->synthesize(des, scope); if (lsig == 0) return 0; @@ -568,10 +580,8 @@ NetNet* NetEBShift::synthesize(Design*des) return 0; } - bool right_flag = op_ == 'r' || op_ == 'R'; - bool signed_flag = op_ == 'R'; - - NetScope*scope = lsig->scope(); + const bool right_flag = op_ == 'r' || op_ == 'R'; + const bool signed_flag = op_ == 'R'; /* Detect the special case where the shift amount is constant. Evaluate the shift amount, and simply reconnect @@ -580,7 +590,7 @@ NetNet* NetEBShift::synthesize(Design*des) verinum shift_v = rcon->value(); long shift = shift_v.as_long(); - if (op() == 'r') + if (right_flag) shift = 0-shift; if (shift == 0) @@ -593,34 +603,13 @@ NetNet* NetEBShift::synthesize(Design*des) // ushift is the amount of pad created by the shift. unsigned long ushift = shift>=0? shift : -shift; - if (ushift > osig->vector_width()) - ushift = osig->vector_width(); + ivl_assert(*this, ushift < osig->vector_width()); // part_width is the bits of the vector that survive the shift. unsigned long part_width = osig->vector_width() - ushift; - verinum znum (verinum::V0, ushift, true); - NetConst*zcon = new NetConst(scope, scope->local_symbol(), - znum); - des->add_node(zcon); - - /* Detect the special case that the shift is the size of - the whole expression. Simply connect the pad to the - osig and escape. */ - if (ushift >= osig->vector_width()) { - connect(zcon->pin(0), osig->pin(0)); - return osig; - } - - NetNet*zsig = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, znum.len()); - zsig->data_type(osig->data_type()); - zsig->local_flag(true); - zsig->set_line(*this); - connect(zcon->pin(0), zsig->pin(0)); - - /* Create a part select to reduce the width of the lsig - to the amount left by the shift. */ + // Create a part select to reduce the width of the lsig + // to the amount left by the shift. NetPartSelect*psel = new NetPartSelect(lsig, shift<0? ushift : 0, part_width, NetPartSelect::VP); @@ -633,6 +622,34 @@ NetNet* NetEBShift::synthesize(Design*des) psig->set_line(*this); connect(psig->pin(0), psel->pin(0)); + // Handle the special case of a signed right shift. In + // this case, use the NetSignExtend device to pad the + // result to the desired width. + if (signed_flag && right_flag) { + NetSignExtend*pad = new NetSignExtend(scope, scope->local_symbol(), + osig->vector_width()); + des->add_node(pad); + pad->set_line(*this); + + connect(pad->pin(1), psig->pin(0)); + connect(pad->pin(0), osig->pin(0)); + return osig; + } + + // Other cases are handled by zero-extending on the + // proper end. + verinum znum (verinum::V0, ushift, true); + NetConst*zcon = new NetConst(scope, scope->local_symbol(), + znum); + des->add_node(zcon); + + NetNet*zsig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, znum.len()); + zsig->data_type(osig->data_type()); + zsig->local_flag(true); + zsig->set_line(*this); + connect(zcon->pin(0), zsig->pin(0)); + NetConcat*ccat = new NetConcat(scope, scope->local_symbol(), osig->vector_width(), 2); ccat->set_line(*this); @@ -652,7 +669,7 @@ NetNet* NetEBShift::synthesize(Design*des) return osig; } - NetNet*rsig = right_->synthesize(des); + NetNet*rsig = right_->synthesize(des, scope); if (rsig == 0) return 0; @@ -678,13 +695,13 @@ NetNet* NetEBShift::synthesize(Design*des) return osig; } -NetNet* NetEConcat::synthesize(Design*des) +NetNet* NetEConcat::synthesize(Design*des, NetScope*scope) { /* First, synthesize the operands. */ NetNet**tmp = new NetNet*[parms_.count()]; bool flag = true; for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { - tmp[idx] = parms_[idx]->synthesize(des); + tmp[idx] = parms_[idx]->synthesize(des, scope); if (tmp[idx] == 0) flag = false; } @@ -692,9 +709,7 @@ NetNet* NetEConcat::synthesize(Design*des) if (flag == false) return 0; - assert(tmp[0]); - NetScope*scope = tmp[0]->scope(); - assert(scope); + ivl_assert(*this, tmp[0]); /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); @@ -712,7 +727,9 @@ NetNet* NetEConcat::synthesize(Design*des) unsigned cur_pin = 1; for (unsigned rpt = 0; rpt < repeat(); rpt += 1) { for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { - connect(concat->pin(cur_pin), tmp[parms_.count()-idx-1]->pin(0)); + unsigned concat_item = parms_.count()-idx-1; + ivl_assert(*this, tmp[concat_item]); + connect(concat->pin(cur_pin), tmp[concat_item]->pin(0)); cur_pin += 1; } } @@ -721,11 +738,8 @@ NetNet* NetEConcat::synthesize(Design*des) return osig; } -NetNet* NetEConst::synthesize(Design*des) +NetNet* NetEConst::synthesize(Design*des, NetScope*scope) { - NetScope*scope = des->find_root_scope(); - assert(scope); - perm_string path = scope->local_symbol(); unsigned width=expr_width(); @@ -743,11 +757,8 @@ NetNet* NetEConst::synthesize(Design*des) /* * Create a NetLiteral object to represent real valued constants. */ -NetNet* NetECReal::synthesize(Design*des) +NetNet* NetECReal::synthesize(Design*des, NetScope*scope) { - NetScope*scope = des->find_root_scope(); - assert(scope); - perm_string path = scope->local_symbol(); NetNet*osig = new NetNet(scope, path, NetNet::WIRE, 1); @@ -768,9 +779,9 @@ NetNet* NetECReal::synthesize(Design*des) * The bitwise unary logic operator (there is only one) is turned * into discrete gates just as easily as the binary ones above. */ -NetNet* NetEUBits::synthesize(Design*des) +NetNet* NetEUBits::synthesize(Design*des, NetScope*scope) { - NetNet*isig = expr_->synthesize(des); + NetNet*isig = expr_->synthesize(des, scope); if (isig == 0) return 0; @@ -782,9 +793,6 @@ NetNet* NetEUBits::synthesize(Design*des) return 0; } - NetScope*scope = isig->scope(); - assert(scope); - unsigned width = isig->vector_width(); NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, width); @@ -810,9 +818,46 @@ NetNet* NetEUBits::synthesize(Design*des) return osig; } -NetNet* NetEUReduce::synthesize(Design*des) +NetNet* NetEUnary::synthesize(Design*des, NetScope*scope) { - NetNet*isig = expr_->synthesize(des); + if (op_ == '+') + return expr_->synthesize(des, scope); + + if (op_ == '-') { + NetNet*sig = expr_->synthesize(des, scope); + sig = sub_net_from(des, scope, 0, sig); + return sig; + } + + if (op_ == 'm') { + NetNet*sub = expr_->synthesize(des, scope); + if (expr_->has_sign() == false) + return sub; + + NetNet*sig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, sub->vector_width()); + sig->set_line(*this); + sig->local_flag(true); + sig->data_type(sub->data_type()); + + NetAbs*tmp = new NetAbs(scope, scope->local_symbol(), sub->vector_width()); + des->add_node(tmp); + tmp->set_line(*this); + + connect(tmp->pin(1), sub->pin(0)); + connect(tmp->pin(0), sig->pin(0)); + return sig; + } + + cerr << get_fileline() << ": iternal error: " + << "NetEUnary::synthesize cannot handle op_=" << op_ << endl; + des->errors += 1; + return expr_->synthesize(des, scope); +} + +NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope) +{ + NetNet*isig = expr_->synthesize(des, scope); if (isig == 0) return 0; @@ -824,9 +869,6 @@ NetNet* NetEUReduce::synthesize(Design*des) return 0; } - NetScope*scope = isig->scope(); - assert(scope); - NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, 1); osig->data_type(expr_type()); @@ -849,7 +891,7 @@ NetNet* NetEUReduce::synthesize(Design*des) rtype = NetUReduce::XOR; break; case 'A': - rtype = NetUReduce::XNOR; + rtype = NetUReduce::NAND; break; case 'X': rtype = NetUReduce::XNOR; @@ -871,22 +913,101 @@ NetNet* NetEUReduce::synthesize(Design*des) return osig; } -NetNet* NetESelect::synthesize(Design *des) +/* + * Turn a part/bit select expression into gates. + * We know some things about the expression that elaboration enforces + * for us: + * + * - Expression elaboration already converted the offset expression into + * cannonical form, so we don't have to worry about that here. + */ +NetNet* NetESelect::synthesize(Design *des, NetScope*scope) { - NetNet*sub = expr_->synthesize(des); + NetNet*sub = expr_->synthesize(des, scope); if (sub == 0) return 0; - NetScope*scope = sub->scope(); - NetNet*off = 0; + // Detect the special case that there is a base expression and + // it is constant. In this case we can generate fixed part selects. + if (NetEConst*base_const = dynamic_cast(base_)) { + verinum base_tmp = base_const->value(); + ivl_assert(*this, base_tmp.is_defined()); + + long base_val = base_tmp.as_long(); + unsigned select_width = expr_width(); + + // Any below X bits? + NetNet*below = 0; + if (base_val < 0) { + unsigned below_width = abs(base_val); + base_val = 0; + ivl_assert(*this, below_width < select_width); + select_width -= below_width; + + below = make_const_x(des, scope, below_width); + below->set_line(*this); + } + + // Any above bits?. + NetNet*above = 0; + if (base_val+select_width > sub->vector_width()) { + select_width = sub->vector_width() - base_val; + unsigned above_width = expr_width() - select_width; + + above = make_const_x(des, scope, above_width); + above->set_line(*this); + } + + // Make the make part select. + NetPartSelect*sel = new NetPartSelect(sub, base_val, select_width, + NetPartSelect::VP); + des->add_node(sel); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, select_width); + tmp->data_type(sub->data_type()); + tmp->local_flag(true); + tmp->set_line(*this); + connect(sel->pin(0), tmp->pin(0)); + + unsigned concat_count = 1; + if (above) + concat_count += 1; + if (below) + concat_count += 1; + if (concat_count > 1) { + NetConcat*cat = new NetConcat(scope, scope->local_symbol(), + expr_width(), concat_count); + cat->set_line(*this); + des->add_node(cat); + if (below) { + connect(cat->pin(1), below->pin(0)); + connect(cat->pin(2), tmp->pin(0)); + } else { + connect(cat->pin(1), tmp->pin(0)); + } + if (above) { + connect(cat->pin(concat_count), above->pin(0)); + } + + tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, expr_width()); + tmp->data_type(sub->data_type()); + tmp->local_flag(true); + tmp->set_line(*this); + connect(cat->pin(0), tmp->pin(0)); + } + return tmp; + } + // This handles the case that the NetESelect exists to do an // actual part/bit select. Generate a NetPartSelect object to // do the work, and replace "sub" with the selected output. if (base_ != 0) { - off = base_->synthesize(des); + off = base_->synthesize(des, scope); NetPartSelect*sel = new NetPartSelect(sub, off, expr_width()); sel->set_line(*this); @@ -967,11 +1088,11 @@ NetNet* NetESelect::synthesize(Design *des) * expressions to the B and A inputs. This way, when the select input * is one, the B input, which is the true expression, is selected. */ -NetNet* NetETernary::synthesize(Design *des) +NetNet* NetETernary::synthesize(Design *des, NetScope*scope) { - NetNet*csig = cond_->synthesize(des), - *tsig = true_val_->synthesize(des), - *fsig = false_val_->synthesize(des); + NetNet*csig = cond_->synthesize(des, scope), + *tsig = true_val_->synthesize(des, scope), + *fsig = false_val_->synthesize(des, scope); if (csig == 0 || tsig == 0 || fsig == 0) return 0; @@ -993,7 +1114,7 @@ NetNet* NetETernary::synthesize(Design *des) perm_string path = csig->scope()->local_symbol(); - assert(csig->vector_width() == 1); + ivl_assert(*this, csig->vector_width() == 1); unsigned width=expr_width(); NetNet*osig = new NetNet(csig->scope(), path, NetNet::IMPLICIT, width); @@ -1025,26 +1146,25 @@ NetNet* NetETernary::synthesize(Design *des) * a bit more work needs to be done. Return a temporary that represents * the selected word. */ -NetNet* NetESignal::synthesize(Design*des) +NetNet* NetESignal::synthesize(Design*des, NetScope*scope) { if (word_ == 0) return net_; - NetScope*scope = net_->scope(); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, net_->vector_width()); tmp->set_line(*this); tmp->local_flag(true); tmp->data_type(net_->data_type()); + // For NetExpr objects, the word index is already converted to + // a canonical (lsb==0) address. Just use the index directly. + if (NetEConst*index_co = dynamic_cast (word_)) { + long index = index_co->value().as_long(); - - assert(net_->array_index_is_valid(index)); - index = net_->array_index_to_address(index); - connect(tmp->pin(0), net_->pin(index)); + } else { unsigned selwid = word_->expr_width(); @@ -1053,7 +1173,7 @@ NetNet* NetESignal::synthesize(Design*des) mux->set_line(*this); des->add_node(mux); - NetNet*index_net = word_->synthesize(des); + NetNet*index_net = word_->synthesize(des, scope); connect(mux->pin_Address(), index_net->pin(0)); connect(tmp->pin(0), mux->pin_Result()); @@ -1061,22 +1181,68 @@ NetNet* NetESignal::synthesize(Design*des) return tmp; } -NetNet* NetESFunc::synthesize(Design*des) +NetNet* NetESFunc::synthesize(Design*des, NetScope*scope) { - cerr << get_fileline() << ": sorry: cannot synthesize system function: " - << *this << " in this context" << endl; - des->errors += 1; - return 0; + + const struct sfunc_return_type*def = lookup_sys_func(name_); + + /* We cannot use the default value for system functions in a + * continuous assignment since the function name is NULL. */ + if (def == 0 || def->name == 0) { + cerr << get_fileline() << ": error: System function " + << name_ << " not defined in system " + "table or SFT file(s)." << endl; + des->errors += 1; + return 0; + } + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Net system function " + << name_ << " returns " << def->type << endl; + } + + NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(), + def, 1+nparms_); + net->set_line(*this); + des->add_node(net); + + NetNet*osig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, def->wid); + osig->local_flag(true); + osig->set_signed(def->type==IVL_VT_REAL? true : false); + osig->data_type(def->type); + osig->set_line(*this); + + connect(net->pin(0), osig->pin(0)); + + unsigned errors = 0; + for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) { + NetNet*tmp = parms_[idx]->synthesize(des, scope); + if (tmp == 0) { + cerr << get_fileline() << ": error: Unable to elaborate " + << "argument " << idx << " of call to " << name_ << + "." << endl; + errors += 1; + des->errors += 1; + continue; + } + + connect(net->pin(1+idx), tmp->pin(0)); + } + + if (errors > 0) return 0; + + return osig; } -NetNet* NetEUFunc::synthesize(Design*des) +NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope) { svector eparms (parms_.count()); /* Synthesize the arguments. */ bool errors = false; for (unsigned idx = 0; idx < eparms.count(); idx += 1) { - NetNet*tmp = parms_[idx]->synthesize(des); + NetNet*tmp = parms_[idx]->synthesize(des, scope); if (tmp == 0) { cerr << get_fileline() << ": error: Unable to synthesize " "port " << idx << " of call to " diff --git a/net_expr.cc b/net_expr.cc index e749cdca1..90ea8cbf8 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -302,10 +302,6 @@ ivl_variable_type_t NetEBPow::expr_type() const return IVL_VT_REAL; if (left_->expr_type() == IVL_VT_REAL) return IVL_VT_REAL; - if (left_->has_sign()) - return IVL_VT_REAL; - if (right_->has_sign()) - return IVL_VT_REAL; return IVL_VT_LOGIC; } diff --git a/net_link.cc b/net_link.cc index e105a98eb..54271616b 100644 --- a/net_link.cc +++ b/net_link.cc @@ -103,6 +103,11 @@ void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) nexus_->drivers_delays(rise, fall, decay); } +void Link::drivers_drive(strength_t drive0, strength_t drive1) +{ + nexus_->drivers_drive(drive0, drive1); +} + void Link::drive0(Link::strength_t str) { drive0_ = str; @@ -298,6 +303,17 @@ void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay) } } +void Nexus::drivers_drive(Link::strength_t drive0, Link::strength_t drive1) +{ + for (Link*cur = list_ ; cur ; cur = cur->next_) { + if (cur->get_dir() != Link::OUTPUT) + continue; + + cur->drive0(drive0); + cur->drive1(drive1); + } +} + void Nexus::unlink(Link*that) { if (name_) { diff --git a/netlist.cc b/netlist.cc index 4da3f5755..abe72999b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -627,7 +627,10 @@ void NetNet::data_type(ivl_variable_type_t t) bool NetNet::get_signed() const { - return signed_; + if (data_type_ == IVL_VT_REAL) + return true; + else + return signed_; } void NetNet::set_signed(bool flag) @@ -2329,6 +2332,8 @@ const NetExpr* NetETernary::false_expr() const ivl_variable_type_t NetETernary::expr_type() const { + ivl_assert(*this, true_val_); + ivl_assert(*this, false_val_); ivl_variable_type_t tru = true_val_->expr_type(); ivl_variable_type_t fal = false_val_->expr_type(); if (tru == IVL_VT_LOGIC && fal == IVL_VT_BOOL) diff --git a/netlist.h b/netlist.h index a8826ceaa..b693db836 100644 --- a/netlist.h +++ b/netlist.h @@ -210,6 +210,10 @@ class Link { void drive0(strength_t); void drive1(strength_t); + // This sets the drives for all drivers of this link, and not + // just the current link. + void drivers_drive(strength_t d0, strength_t d1); + strength_t drive0() const; strength_t drive1() const; @@ -312,6 +316,7 @@ class Nexus { verinum::V get_init() const; void drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay); + void drivers_drive(Link::strength_t d0, Link::strength_t d1); Link*first_nlink(); const Link* first_nlink()const; @@ -1576,8 +1581,16 @@ class NetExpr : public LineInfo { virtual NexusSet* nex_input(bool rem_out = true) =0; // Return a version of myself that is structural. This is used - // for converting expressions to gates. - virtual NetNet*synthesize(Design*); + // for converting expressions to gates. The arguments are: + // + // des, scope: The context where this work is done + // + // rise/fall/decay: Attach these delays to the driver for the + // expression output. + // + // drive0/drive1: Attach these strengths tp the driver for + // the expression output. + virtual NetNet*synthesize(Design*des, NetScope*scope); protected: @@ -1615,7 +1628,7 @@ class NetEConst : public NetExpr { virtual void dump(ostream&) const; virtual NetEConst* dup_expr() const; - virtual NetNet*synthesize(Design*); + virtual NetNet*synthesize(Design*, NetScope*scope); virtual NexusSet* nex_input(bool rem_out = true); private: @@ -1668,7 +1681,7 @@ class NetECReal : public NetExpr { virtual void dump(ostream&) const; virtual NetECReal* dup_expr() const; - virtual NetNet*synthesize(Design*); + virtual NetNet*synthesize(Design*, NetScope*scope); virtual NexusSet* nex_input(bool rem_out = true); private: @@ -2905,7 +2918,7 @@ class NetEUFunc : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual NetNet* synthesize(Design*des); + virtual NetNet* synthesize(Design*des, NetScope*scope); private: NetScope*scope_; @@ -3120,7 +3133,7 @@ class NetEBAdd : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBAdd* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: NetECReal* eval_tree_real_(); @@ -3142,7 +3155,7 @@ class NetEBDiv : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBDiv* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); }; /* @@ -3169,7 +3182,7 @@ class NetEBBits : public NetEBinary { virtual NetEBBits* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); }; /* @@ -3200,7 +3213,7 @@ class NetEBComp : public NetEBinary { virtual NetEBComp* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag); @@ -3232,7 +3245,7 @@ class NetEBLogic : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBLogic* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: }; @@ -3270,7 +3283,7 @@ class NetEBMult : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBMult* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: @@ -3292,7 +3305,7 @@ class NetEBPow : public NetEBinary { virtual bool set_width(unsigned w, bool last_chance); virtual NetEBPow* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: @@ -3324,7 +3337,7 @@ class NetEBShift : public NetEBinary { virtual NetEBShift* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); private: }; @@ -3358,7 +3371,7 @@ class NetEConcat : public NetExpr { virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); - virtual NetNet*synthesize(Design*); + virtual NetNet*synthesize(Design*, NetScope*scope); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -3434,7 +3447,7 @@ class NetESelect : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(int prune_to_width = -1); virtual NetESelect* dup_expr() const; - virtual NetNet*synthesize(Design*des); + virtual NetNet*synthesize(Design*des, NetScope*scope); virtual void dump(ostream&) const; private: @@ -3514,7 +3527,7 @@ class NetESFunc : public NetExpr { virtual void expr_scan(struct expr_scan_t*) const; virtual NetESFunc*dup_expr() const; - virtual NetNet*synthesize(Design*); + virtual NetNet*synthesize(Design*, NetScope*scope); private: const char* name_; @@ -3551,7 +3564,7 @@ class NetETernary : public NetExpr { virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; - virtual NetNet*synthesize(Design*); + virtual NetNet*synthesize(Design*, NetScope*scope); private: NetExpr*cond_; @@ -3588,6 +3601,7 @@ class NetEUnary : public NetExpr { virtual NetEUnary* dup_expr() const; virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetNet* synthesize(Design*, NetScope*scope); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -3608,7 +3622,7 @@ class NetEUBits : public NetEUnary { NetEUBits(char op, NetExpr*ex); ~NetEUBits(); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); virtual NetExpr* eval_tree(int prune_to_width = -1); virtual ivl_variable_type_t expr_type() const; @@ -3621,7 +3635,7 @@ class NetEUReduce : public NetEUnary { ~NetEUReduce(); virtual bool set_width(unsigned w, bool last_chance); - virtual NetNet* synthesize(Design*); + virtual NetNet* synthesize(Design*, NetScope*scope); virtual NetEUReduce* dup_expr() const; virtual NetEConst* eval_tree(int prune_to_width = -1); virtual ivl_variable_type_t expr_type() const; @@ -3648,7 +3662,7 @@ class NetESignal : public NetExpr { virtual bool set_width(unsigned, bool last_chance); virtual NetESignal* dup_expr() const; - NetNet* synthesize(Design*des); + NetNet* synthesize(Design*des, NetScope*scope); NexusSet* nex_input(bool rem_out = true); // This is the expression for selecting an array word, if this diff --git a/netmisc.cc b/netmisc.cc index 7f82fd129..37d781a29 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -76,6 +76,46 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val) #endif } +NetNet* sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig) +{ + NetNet*zero_net = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, sig->vector_width()); + zero_net->data_type(sig->data_type()); + zero_net->local_flag(true); + + if (sig->data_type() == IVL_VT_REAL) { + verireal zero (val); + NetLiteral*zero_obj = new NetLiteral(scope, scope->local_symbol(), zero); + des->add_node(zero_obj); + + connect(zero_net->pin(0), zero_obj->pin(0)); + + } else { + verinum zero ((int64_t)val); + zero = pad_to_width(zero, sig->vector_width()); + NetConst*zero_obj = new NetConst(scope, scope->local_symbol(), zero); + des->add_node(zero_obj); + + connect(zero_net->pin(0), zero_obj->pin(0)); + } + + NetAddSub*adder = new NetAddSub(scope, scope->local_symbol(), sig->vector_width()); + des->add_node(adder); + adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB")); + + connect(zero_net->pin(0), adder->pin_DataA()); + connect(adder->pin_DataB(), sig->pin(0)); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, sig->vector_width()); + tmp->data_type(sig->data_type()); + tmp->local_flag(true); + + connect(adder->pin_Result(), tmp->pin(0)); + + return tmp; +} + NetNet* cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid) { if (src->data_type() != IVL_VT_REAL) @@ -170,6 +210,27 @@ NetEConst* make_const_x(unsigned long wid) return resx; } +NetEConst* make_const_0(unsigned long wid) +{ + verinum xxx (verinum::V0, wid); + NetEConst*resx = new NetEConst(xxx); + return resx; +} + +NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid) +{ + verinum xxx (verinum::Vx, wid); + NetConst*res = new NetConst(scope, scope->local_symbol(), xxx); + des->add_node(res); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, wid); + sig->local_flag(true); + sig->data_type(IVL_VT_LOGIC); + + connect(sig->pin(0), res->pin(0)); + return sig; +} + NetExpr* condition_reduce(NetExpr*expr) { if (expr->expr_width() == 1) diff --git a/netmisc.h b/netmisc.h index 7d1b42a90..f54237b03 100644 --- a/netmisc.h +++ b/netmisc.h @@ -91,6 +91,7 @@ extern NetNet*crop_to_width(Design*des, NetNet*n, unsigned w); * return a new NetNet value that is the output of an addition. */ extern NetNet*add_to_net(Design*des, NetNet*sig, long val); +extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig); /* * These functions make various sorts of expressions, given operands @@ -113,6 +114,12 @@ extern NetExpr*make_sub_expr(long val, NetExpr*expr); * Make a NetEConst object that contains only X bits. */ extern NetEConst*make_const_x(unsigned long wid); +extern NetEConst*make_const_0(unsigned long wid); + +/* + * Make A const net + */ +extern NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid); /* * In some cases the lval is accessible as a pointer to the head of @@ -130,9 +137,11 @@ extern unsigned count_lval_width(const class NetAssign_*first); * The expr_width is the width of the context where the expression is * being elaborated, or -1 if the expression is self-determined width. * - * Also, the prune_width is the maximum width of the result, and it - * passed to the eval_tree method of the expression to limit constant - * results if possible. + * The prune_width is the maximum width of the result, and is passed + * to the eval_tree method of the expression to limit constant + * results. The evaluation will prune any constant result down to the + * prune_width (if >0) so should only be used at the point where it is + * bound to the destination. */ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, diff --git a/pform_dump.cc b/pform_dump.cc index fbda728a3..91f715ffc 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -295,6 +295,9 @@ void PEBinary::dump(ostream&out) const case 'l': out << "<<"; break; + case 'L': + out << "<="; + break; case 'n': out << "!="; break; diff --git a/syn-rules.y b/syn-rules.y index 49ea2586b..78c4b4588 100644 --- a/syn-rules.y +++ b/syn-rules.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 2000-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2008 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,7 +141,7 @@ static void make_DFF_CE(Design*des, NetProcTop*top, NetEvWait*wclk, NetEvProbe*pclk = eclk->probe(0); NetESignal*d = dynamic_cast (asn->rval()); - NetNet*ce = cexp? cexp->synthesize(des) : 0; + NetNet*ce = cexp? cexp->synthesize(des, top->scope()) : 0; if (d == 0) { cerr << asn->get_fileline() << ": internal error: " diff --git a/synth.cc b/synth.cc index e92276d3a..ac2aaeb87 100644 --- a/synth.cc +++ b/synth.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2008 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 @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: synth.cc,v 1.14 2002/08/12 01:35:00 steve Exp $" -#endif # include "config.h" @@ -36,12 +33,13 @@ class do_expr : public proc_match_t { public: - do_expr(Design*d) - : des_(d) { } + do_expr(Design*d, NetScope*s) + : des_(d), scope_(s) { } private: Design*des_; + NetScope*scope_; virtual int assign(NetAssign*); virtual int assign_nb(NetAssignNB*); @@ -55,7 +53,7 @@ int do_expr::assign(NetAssign*stmt) if (dynamic_cast(stmt->rval())) return 0; - NetNet*tmp = stmt->rval()->synthesize(des_); + NetNet*tmp = stmt->rval()->synthesize(des_, scope_); if (tmp == 0) return 0; @@ -70,7 +68,7 @@ int do_expr::assign_nb(NetAssignNB*stmt) if (dynamic_cast(stmt->rval())) return 0; - NetNet*tmp = stmt->rval()->synthesize(des_); + NetNet*tmp = stmt->rval()->synthesize(des_, scope_); if (tmp == 0) return 0; @@ -84,7 +82,7 @@ int do_expr::condit(NetCondit*stmt) { /* synthesize the condition expression, if necessary. */ if (! dynamic_cast(stmt->expr())) { - NetNet*tmp = stmt->expr()->synthesize(des_); + NetNet*tmp = stmt->expr()->synthesize(des_, scope_); if (tmp) { NetESignal*tmpe = new NetESignal(tmp); @@ -144,13 +142,13 @@ void synth_f::process(class Design*des, class NetProcTop*top) void synth_f::proc_always_(class Design*des) { - do_expr expr_pat(des); + do_expr expr_pat(des, top_->scope()); top_->statement()->match_proc(&expr_pat); } void synth_f::proc_initial_(class Design*des) { - do_expr expr_pat(des); + do_expr expr_pat(des, top_->scope()); top_->statement()->match_proc(&expr_pat); } @@ -159,28 +157,3 @@ void synth(Design*des) synth_f synth_obj; des->functor(&synth_obj); } - -/* - * $Log: synth.cc,v $ - * Revision 1.14 2002/08/12 01:35:00 steve - * conditional ident string using autoconfig. - * - * Revision 1.13 2002/06/05 03:44:25 steve - * Add support for memory words in l-value of - * non-blocking assignments, and remove the special - * NetAssignMem_ and NetAssignMemNB classes. - * - * Revision 1.12 2001/07/25 03:10:49 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.11 2000/11/22 21:18:42 steve - * synthesize the rvalue of <= statements. - * - * Revision 1.10 2000/05/13 20:55:47 steve - * Use yacc based synthesizer. - * - * Revision 1.9 2000/04/16 22:57:34 steve - * Catch expressions that are part of conditionals. - */ - diff --git a/synth2.cc b/synth2.cc index 812dceb03..dbd843759 100644 --- a/synth2.cc +++ b/synth2.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 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 @@ -60,7 +60,7 @@ bool NetProc::synth_sync(Design*des, NetScope*scope, NetFF*ff, bool NetAssignBase::synth_async(Design*des, NetScope*scope, const NetBus&nex_map, NetBus&nex_out) { - NetNet*rsig = rval_->synthesize(des); + NetNet*rsig = rval_->synthesize(des, scope); assert(rsig); NetNet*lsig = lval_->sig(); @@ -155,7 +155,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope, const NetBus&nex_map, NetBus&nex_out) { /* Synthesize the select expression. */ - NetNet*esig = expr_->synthesize(des); + NetNet*esig = expr_->synthesize(des, scope); unsigned sel_width = esig->vector_width(); assert(sel_width > 0); diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index e060fac7d..39f387498 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -168,12 +168,23 @@ static int draw_number_real(ivl_expr_t exp) unsigned long mant = 0, mask = -1UL; int vexp = 0x1000; - for (idx = 0 ; idx < wid ; idx += 1) { + for (idx = 0 ; idx < wid && idx < 8*sizeof(mant) ; idx += 1) { mask <<= 1; if (bits[idx] == '1') mant |= 1 << idx; } + for ( ; idx < wid ; idx += 1) { + if (ivl_expr_signed(exp) && (bits[idx] == bits[8*sizeof(mant)-1])) + continue; + + if (bits[idx] == '0') + continue; + + fprintf(stderr, "internal error: mantissa doesn't fit!\n"); + assert(0); + } + /* If this is actually a negative number, then get the positive equivalent, and set the sign bit in the exponent field. @@ -188,8 +199,8 @@ static int draw_number_real(ivl_expr_t exp) vexp |= 0x4000; } - fprintf(vvp_out, " %%loadi/wr %d, %lu, %d; load(num)= %c%lu\n", - res, mant, vexp, (vexp&0x4000)? '-' : '+', mant); + fprintf(vvp_out, " %%loadi/wr %d, %lu, %d; load(num)= %c%lu (wid=%u)\n", + res, mant, vexp, (vexp&0x4000)? '-' : '+', mant, wid); return res; } diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 89cc691a9..32780a41d 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1650,15 +1650,19 @@ static void draw_lpm_re(ivl_lpm_t net, const char*type) static void draw_lpm_repeat(ivl_lpm_t net) { - fprintf(vvp_out, "L_%p .repeat %u, %u, %s;\n", net, + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .repeat %u, %u, %s;\n", net, dly, ivl_lpm_width(net), ivl_lpm_size(net), draw_net_input(ivl_lpm_data(net,0))); } static void draw_lpm_sign_ext(ivl_lpm_t net) { - fprintf(vvp_out, "L_%p .extend/s %u, %s;\n", - net, ivl_lpm_width(net), + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .extend/s %u, %s;\n", + net, dly, ivl_lpm_width(net), draw_net_input(ivl_lpm_data(net,0))); } diff --git a/vvp/arith.cc b/vvp/arith.cc index 0afea9c29..9a2061504 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -330,24 +330,24 @@ void vvp_arith_mult::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) { dispatch_operand_(ptr, bit); - if (wid_ > 8 * sizeof(unsigned long)) { + if (wid_ > 8 * sizeof(long)) { wide_(ptr); return ; } - unsigned long a; - if (! vector4_to_value(op_a_, a)) { + long a; + if (! vector4_to_value(op_a_, a, true, true)) { vvp_send_vec4(ptr.ptr()->out, x_val_); return; } - unsigned long b; - if (! vector4_to_value(op_b_, b)) { + long b; + if (! vector4_to_value(op_b_, b, true, true)) { vvp_send_vec4(ptr.ptr()->out, x_val_); return; } - unsigned long val = a * b; + long val = a * b; assert(wid_ <= 8*sizeof(val)); vvp_vector4_t vval (wid_); @@ -364,105 +364,6 @@ void vvp_arith_mult::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) } -#if 0 -void vvp_arith_mult::set(vvp_ipoint_t i, bool push, unsigned val, unsigned) -{ - put(i, val); - vvp_ipoint_t base = ipoint_make(i,0); - - if(wid_ > 8*sizeof(unsigned long)) { - wide(base, push); - return; - } - - unsigned long a = 0, b = 0; - - for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { - vvp_ipoint_t ptr = ipoint_index(base,idx); - functor_t obj = functor_index(ptr); - - unsigned val = obj->ival; - if (val & 0xaa) { - output_x_(base, push); - return; - } - - if (val & 0x01) - a += 1UL << idx; - if (val & 0x04) - b += 1UL << idx; - } - - output_val_(base, push, a*b); -} -#endif - -#if 0 -void vvp_arith_mult::wide(vvp_ipoint_t base, bool push) -{ - unsigned char *a, *b, *sum; - a = new unsigned char[wid_]; - b = new unsigned char[wid_]; - sum = new unsigned char[wid_]; - - unsigned mxa = 0; - unsigned mxb = 0; - - for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { - vvp_ipoint_t ptr = ipoint_index(base, idx); - functor_t obj = functor_index(ptr); - - unsigned ival = obj->ival; - if (ival & 0xaa) { - output_x_(base, push); - delete[]sum; - delete[]b; - delete[]a; - return; - } - - if((a[idx] = ((ival & 0x01) != 0))) mxa=idx+1; - if((b[idx] = ((ival & 0x04) != 0))) mxb=idx; - sum[idx] = 0; - } - - /* do the a*b multiply using the long method we learned in - grade school. We know at this point that there are no X or - Z values in the a or b vectors. */ - - for(unsigned i=0 ; i<=mxb ; i += 1) { - if(b[i]) { - unsigned char carry=0; - unsigned char temp; - - for(unsigned j=0 ; j<=mxa ; j += 1) { - - if((i+j) >= wid_) - break; - - temp=sum[i+j] + a[j] + carry; - sum[i+j]=(temp&1); - carry=(temp>>1); - } - } - } - - for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { - vvp_ipoint_t ptr = ipoint_index(base,idx); - functor_t obj = functor_index(ptr); - - unsigned val = sum[idx]; - - obj->put_oval(val, push); - } - - delete[]sum; - delete[]b; - delete[]a; -} -#endif - - // Power vvp_arith_pow::vvp_arith_pow(unsigned wid, bool signed_flag) @@ -701,7 +602,11 @@ void vvp_cmp_ne::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) { dispatch_operand_(ptr, bit); - assert(op_a_.size() == op_b_.size()); + if (op_a_.size() != op_b_.size()) { + cerr << "internal error: vvp_cmp_ne: op_a_=" << op_a_ + << ", op_b_=" << op_b_ << endl; + assert(op_a_.size() == op_b_.size()); + } vvp_vector4_t res (1); res.set_bit(0, BIT4_0); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index d0fb5d4d5..f467bf468 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1117,13 +1117,7 @@ static bool of_CMPIU_the_hard_way(vthread_t thr, vvp_code_t cp) thr_check_addr(thr, idx1+wid-1); vvp_bit4_t lv = thr_get_bit(thr, idx1); - if (bit4_is_xz(lv)) { - thr_put_bit(thr, 4, BIT4_X); - thr_put_bit(thr, 5, BIT4_X); - thr_put_bit(thr, 6, BIT4_0); - } - - vvp_bit4_t eq = BIT4_0; + vvp_bit4_t eq = BIT4_1; for (unsigned idx = 0 ; idx < wid ; idx += 1) { vvp_bit4_t rv = (imm & 1UL)? BIT4_1 : BIT4_0; imm >>= 1UL; @@ -1131,12 +1125,13 @@ static bool of_CMPIU_the_hard_way(vthread_t thr, vvp_code_t cp) if (bit4_is_xz(lv)) { eq = BIT4_X; } else if (lv != rv) { + eq = BIT4_0; break; } if (idx1 >= 4) { idx1 += 1; - if (idx1 < wid) + if ((idx+1) < wid) lv = thr_get_bit(thr, idx1); } } diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 179305dd3..fa869bf0a 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -1849,7 +1849,7 @@ static void div_mod (vvp_vector2_t dividend, vvp_vector2_t divisor, mask >>= 1; } - remainder = dividend; + remainder = vvp_vector2_t(dividend, mask.size()); } vvp_vector2_t operator / (const vvp_vector2_t÷nd,