diff --git a/elab_expr.cc b/elab_expr.cc index a84995cfa..bc6bcec82 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -509,9 +509,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, if (NetEConst*lpc = dynamic_cast (lp)) { - // Special case: The left expression is zero. No matter - // what the shift, the result is going to be zero. - if (lpc->value().is_defined() && lpc->value().is_zero()) { + // Special case: The left expression is zero. If the + // shift value contains no 'x' or 'z' bits, the result + // is going to be zero. + if (lpc->value().is_defined() && lpc->value().is_zero() + && (rp->expr_type() == IVL_VT_BOOL)) { if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -527,6 +529,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, // Handle the super-special case that both // operands are constants. Precalculate the // entire value here. + if (!rpc->value().is_defined()) { + tmp = make_const_x(use_wid); + tmp->set_line(*this); + return tmp; + } verinum lpval = lpc->value(); unsigned shift = rpc->value().as_ulong(); verinum result = lpc->value() << shift; @@ -566,6 +573,20 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, } } else if (NetEConst*rpc = dynamic_cast (rp)) { + // Special case: The shift value contains 'x' or 'z' bits. + // Elaborate as a constant-x. + if (!rpc->value().is_defined()) { + + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Shift by undefined value. " + << "Elaborate as constant 'x'." << endl; + + tmp = make_const_x(expr_wid); + tmp->set_line(*this); + return tmp; + } + long shift = rpc->value().as_long(); use_wid = lp->expr_width(); if (expr_wid > 0) @@ -594,6 +615,11 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, // default way. if (expr_wid >= 0) lp = pad_to_width(lp, expr_wid, *this); + + rp = new NetESelect(rp, 0, rp->expr_width()); + rp->cast_signed(false); + rp->set_line(*this); + tmp = new NetEBShift(op_, lp, rp); } @@ -631,9 +657,11 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, if (NetEConst*lpc = dynamic_cast (lp)) { - // Special case: The left expression is zero. No matter - // what the shift, the result is going to be zero. - if (lpc->value().is_defined() && lpc->value().is_zero()) { + // Special case: The left expression is zero. If the + // shift value contains no 'x' or 'z' bits, the result + // is going to be zero. + if (lpc->value().is_defined() && lpc->value().is_zero() + && (rp->expr_type() == IVL_VT_BOOL)) { if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -647,6 +675,20 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, } if (NetEConst*rpc = dynamic_cast (rp)) { + // Special case: The shift value contains 'x' or 'z' bits. + // Elaborate as a constant-x. + if (!rpc->value().is_defined()) { + + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Shift by undefined value. " + << "Elaborate as constant 'x'." << endl; + + tmp = make_const_x(expr_wid); + tmp->set_line(*this); + return tmp; + } + unsigned long shift = rpc->value().as_ulong(); // Special case: The shift is the size of the entire @@ -692,36 +734,16 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, tmp = pad_to_width(tmp, use_wid, *this); return tmp; } - - // If this is lossless, then pad the left expression - // enough to cover the right shift. - if (expr_wid == -2 && use_wid+shift > lp->expr_width()) { - lp->cast_signed(lp->has_sign() && op_=='R'); - lp = pad_to_width(lp, use_wid + shift, *this); - } - - 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; - - ivl_assert(*this, tmp_wid > 0); - ivl_assert(*this, use_wid > 0); - - // Implement the right-shift by part-selecting the low - // bits out. Pad the result of the part select back out - // to the desired size. - 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, *this); - return tmp; } // Fallback, handle the general case. if (expr_wid > 0) lp = pad_to_width(lp, expr_wid, *this); + + rp = new NetESelect(rp, 0, rp->expr_width()); + rp->cast_signed(false); + rp->set_line(*this); + tmp = new NetEBShift(op_, lp, rp); tmp->set_line(*this); return tmp; diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 7ab313d11..0d3fe395b 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1157,8 +1157,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) case 'l': /* << (left shift) */ lv = draw_eval_expr_wid(le, wid, 0); - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) { @@ -1193,8 +1194,9 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); } - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) { @@ -1229,13 +1231,15 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid) lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0); } - /* shifting 0 gets 0. */ - if (lv.base == 0) + /* Shifting 0 gets 0, if we can be sure the shift value + contains no 'x' or 'z' bits. */ + if ((lv.base == 0) && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; - /* Sign extend any constant begets itself, if this - expression is signed. */ - if ((lv.base < 4) && (ivl_expr_signed(expr))) + /* Similarly, sign extending any constant bit begets itself, + if this expression is signed. */ + if ((lv.base < 4) && ivl_expr_signed(expr) + && (ivl_expr_value(re) == IVL_VT_BOOL)) return lv; if (lv.base < 4) {