diff --git a/Statement.h b/Statement.h index b82d6e456..d817ff7a2 100644 --- a/Statement.h +++ b/Statement.h @@ -103,7 +103,8 @@ class PAssign_ : public Statement { protected: NetAssign_* elaborate_lval(Design*, NetScope*scope) const; - NetExpr* elaborate_rval_(Design*, NetScope*, unsigned lv_width) const; + NetExpr* elaborate_rval_(Design*, NetScope*, unsigned lv_width, + ivl_variable_type_t type) const; PExpr* delay_; PEventStatement*event_; diff --git a/elab_expr.cc b/elab_expr.cc index 497a6f5f6..3a464be5c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -31,6 +31,17 @@ # include "util.h" # include "ivl_assert.h" +static bool type_is_vectorable(ivl_variable_type_t type) +{ + switch (type) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + return true; + default: + return false; + } +} + /* * The default behavior for the test_width method is to just return the * minimum width that is passed in. @@ -61,8 +72,8 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, { bool flag_left = false; bool flag_right = false; - unsigned wid_left = left_->test_width(des,scope, min, lval, flag_left); - unsigned wid_right = right_->test_width(des,scope, min, lval, flag_right); + unsigned wid_left = left_->test_width(des,scope, min, 0, flag_left); + unsigned wid_right = right_->test_width(des,scope, min, 0, flag_right); if (flag_left || flag_right) unsized_flag = true; @@ -82,8 +93,16 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, min = lval; break; - case 'l': - ivl_assert(*this, 0); // Should be handled bin PEBShift + case 'l': // << Should be handled by PEBShift + case '<': // < Should be handled by PEBComp + case '>': // > Should be handled by PEBComp + case 'e': // == Should be handled by PEBComp + case 'E': // === Should be handled by PEBComp + case 'L': // <= Should be handled by PEBComp + case 'G': // >= Should be handled by PEBComp + case 'n': // != Should be handled by PEBComp + case 'N': // !== Should be handled by PEBComp + ivl_assert(*this, 0); default: if (wid_left > min) min = wid_left; @@ -114,6 +133,24 @@ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, return 0; } + // Handle the special case that one of the operands is a real + // value and the other is a vector type. In that case, + // re-elaborate the vectorable argument as self-determined + // lossless. + if (lp->expr_type()==IVL_VT_REAL + && type_is_vectorable(rp->expr_type()) + && expr_wid != -2) { + delete rp; + rp = right_->elaborate_expr(des, scope, -2, false); + } + + if (rp->expr_type()==IVL_VT_REAL + && type_is_vectorable(lp->expr_type()) + && expr_wid != -2) { + delete lp; + lp = left_->elaborate_expr(des, scope, -2, false); + } + NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); return tmp; } @@ -513,7 +550,17 @@ NetExpr* PEBinary::elaborate_expr_base_add_(Design*des, int expr_wid) const { NetExpr*tmp; - tmp = new NetEBAdd(op_, lp, rp, expr_wid==-2? true : false); + bool use_lossless_flag = expr_wid == -2; + + // If this expression is not vectorable, then do NOT pass the + // lossless flag to the NetEBAdd constructor. For non- + // vectorable, lossless is implicit. + if (! type_is_vectorable(lp->expr_type())) + use_lossless_flag = false; + if (! type_is_vectorable(rp->expr_type())) + use_lossless_flag = false; + + tmp = new NetEBAdd(op_, lp, rp, use_lossless_flag); if (expr_wid > 0 && (tmp->expr_type() == IVL_VT_BOOL || tmp->expr_type() == IVL_VT_LOGIC)) tmp->set_width(expr_wid); diff --git a/elaborate.cc b/elaborate.cc index 81a5e4304..1e5af629a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -100,8 +100,14 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const } bool unsized_flag = false; - unsigned use_width = pin(1)->test_width(des, scope, lval->vector_width(), - lval->vector_width(), unsized_flag); + unsigned use_width = 0; + if (lval->data_type() == IVL_VT_REAL) { + unsized_flag = true; + use_width = pin(1)->test_width(des, scope, 0, 0, unsized_flag); + } else { + use_width = pin(1)->test_width(des, scope, lval->vector_width(), + lval->vector_width(), unsized_flag); + } if (debug_elaborate) { cerr << get_fileline() << ": debug: PGAssign: r-value tested " @@ -113,6 +119,8 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const int expr_wid = lval->vector_width(); if (use_width > (unsigned)expr_wid) expr_wid = (int)use_width; + if (lval->data_type() == IVL_VT_REAL) + expr_wid = -2; NetExpr*rval_expr = elab_and_eval(des, scope, pin(1), expr_wid, lval->vector_width()); @@ -1707,7 +1715,8 @@ NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const } NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, - unsigned lv_width) const + unsigned lv_width, + ivl_variable_type_t lv_type) const { ivl_assert(*this, rval_); @@ -1717,13 +1726,26 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, 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; + unsigned tmp_width = 0; + + if (lv_type == IVL_VT_REAL) { + unsized_flag = true; + tmp_width = rval()->test_width(des, scope, 0, 0, unsized_flag); + } else { + tmp_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag); + if (tmp_width > use_width) + use_width = tmp_width; + } + + int expr_wid = use_width; + if (lv_type == IVL_VT_REAL) { + expr_wid = -2; + lv_width = 0; + } /* 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); + NetExpr*rv = elab_and_eval(des, scope, rval_, expr_wid, lv_width); if (rv == 0) return 0; return rv; @@ -1811,7 +1833,7 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const /* Elaborate the r-value expression, then try to evaluate it. */ - NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv)); + NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); if (rv == 0) return 0; assert(rv); @@ -1976,7 +1998,7 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; - NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv)); + NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); /* Handle the (common) case that the r-value is a vector. This includes just about everything but reals. In this case, we diff --git a/net_assign.cc b/net_assign.cc index 4b84e6324..d44d89999 100644 --- a/net_assign.cc +++ b/net_assign.cc @@ -81,6 +81,11 @@ unsigned NetAssign_::lwidth() const return lwid_; } +ivl_variable_type_t NetAssign_::expr_type() const +{ + return sig_->data_type(); +} + perm_string NetAssign_::name() const { if (sig_) { diff --git a/netlist.h b/netlist.h index 52fd0154d..2ee9d9727 100644 --- a/netlist.h +++ b/netlist.h @@ -2139,6 +2139,7 @@ class NetAssign_ { // method accounts for the presence of the mux, so it is not // necessarily the same as the pin_count(). unsigned lwidth() const; + ivl_variable_type_t expr_type() const; // Get the name of the underlying object. perm_string name() const; diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 98092d127..5adc6b8b4 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -81,16 +81,24 @@ static void show_binary_expression(ivl_expr_t net, unsigned ind) switch (ivl_expr_opcode(net)) { case '*': - /* The width of multiply expressions is the sum of the - widths of the operands. This is slightly different - from the way the Verilog standard does it, but allows - us to keep operands smaller. */ - width = ivl_expr_width(ivl_expr_oper1(net)); - width += ivl_expr_width(ivl_expr_oper2(net)); - if (ivl_expr_width(net) != width) { - fprintf(out, "%*sERROR: Result width incorrect. Expecting %u, got %u\n", - ind+3, "", width, ivl_expr_width(net)); - stub_errors += 1; + if (ivl_expr_value(net) == IVL_VT_REAL) { + if (ivl_expr_width(net) != 1) { + fprintf(out, "%*sERROR: Result width incorrect. Expecting 1, got %u\n", + ind+3, "", ivl_expr_width(net)); + stub_errors += 1; + } + } else { + /* The width of multiply expressions is the sum of the + widths of the operands. This is slightly different + from the way the Verilog standard does it, but allows + us to keep operands smaller. */ + width = ivl_expr_width(ivl_expr_oper1(net)); + width += ivl_expr_width(ivl_expr_oper2(net)); + if (ivl_expr_width(net) != width) { + fprintf(out, "%*sERROR: Result width incorrect. Expecting %u, got %u\n", + ind+3, "", width, ivl_expr_width(net)); + stub_errors += 1; + } } break;