Handle ternary expressions with mixed argument types.

If the true and false alternatives are mixed types, then vectored
arguments are treated as if in a self-determined context then cast
to REAL.
This commit is contained in:
Stephen Williams 2009-01-05 19:44:52 -08:00
parent 8eb7e4b3d4
commit 707a3ebe27
5 changed files with 83 additions and 29 deletions

View File

@ -621,6 +621,10 @@ class PETernary : public PExpr {
virtual NetETernary*elaborate_pexpr(Design*des, NetScope*sc) const; virtual NetETernary*elaborate_pexpr(Design*des, NetScope*sc) const;
virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual verinum* eval_const(Design*des, NetScope*sc) const;
private:
NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope,
PExpr*expr, int use_wid) const;
private: private:
PExpr*expr_; PExpr*expr_;
PExpr*tru_; PExpr*tru_;

View File

@ -84,19 +84,21 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope,
PExpr*expr) PExpr*expr)
{ {
bool unsized_flag = type_is_vectorable(data_type_lv)? false : true; bool unsized_flag = type_is_vectorable(data_type_lv)? false : true;
ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; unsigned use_lval_wid = type_is_vectorable(data_type_lv)? expr_wid_lv : 0;
unsigned use_min_wid = expr_wid_lv;
/* Find out what the r-value width is going to be. We /* Find out what the r-value width is going to be. We
guess it will be the l-value width, but it may turn guess it will be the l-value width, but it may turn
out to be something else based on self-determined out to be something else based on self-determined
widths inside. */ widths inside. */
int expr_wid = expr->test_width(des, scope, expr_wid_lv, expr_wid_lv, rval_type, unsized_flag); ivl_variable_type_t rval_type = IVL_VT_NO_TYPE;
int expr_wid = expr->test_width(des, scope, use_min_wid, use_lval_wid, rval_type, unsized_flag);
if (debug_elaborate) { if (debug_elaborate) {
cerr << expr->get_fileline() << ": debug: r-value tested " cerr << expr->get_fileline() << ": debug: r-value tested "
<< "type=" << rval_type << "type=" << rval_type
<< ", width=" << expr_wid << ", width=" << expr_wid
<< ", min=" << expr_wid_lv << ", min=" << use_min_wid
<< ", unsized_flag=" << (unsized_flag?"true":"false") << endl; << ", unsized_flag=" << (unsized_flag?"true":"false") << endl;
} }
@ -2799,7 +2801,7 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope,
unsigned PENumber::test_width(Design*, NetScope*, unsigned PENumber::test_width(Design*, NetScope*,
unsigned min, unsigned lval, unsigned min, unsigned lval,
ivl_variable_type_t&expr_type__, ivl_variable_type_t&use_expr_type,
bool&unsized_flag) bool&unsized_flag)
{ {
expr_type_ = IVL_VT_LOGIC; expr_type_ = IVL_VT_LOGIC;
@ -2813,7 +2815,7 @@ unsigned PENumber::test_width(Design*, NetScope*,
if (lval > 0 && lval < use_wid) if (lval > 0 && lval < use_wid)
use_wid = lval; use_wid = lval;
expr_type__ = expr_type_; use_expr_type = expr_type_;
expr_width_ = use_wid; expr_width_ = use_wid;
return use_wid; return use_wid;
} }
@ -2866,7 +2868,7 @@ NetEConst* PEString::elaborate_expr(Design*des, NetScope*,
unsigned PETernary::test_width(Design*des, NetScope*scope, unsigned PETernary::test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval, unsigned min, unsigned lval,
ivl_variable_type_t&expr_type__, ivl_variable_type_t&use_expr_type,
bool&flag) bool&flag)
{ {
// The condition of the ternary is self-determined, but we // The condition of the ternary is self-determined, but we
@ -2893,16 +2895,32 @@ unsigned PETernary::test_width(Design*des, NetScope*scope,
tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag); tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag);
} }
if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) // If either of the alternatives is IVL_VT_REAL, then the
// expression as a whole is IVL_VT_REAL. Otherwise, if either
// of the alternatives is IVL_VT_LOGIC, then the expression as
// a whole is IVL_VT_LOGIC. The fallback assumes that the
// types are the same and we take that.
if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) {
expr_type_ = IVL_VT_REAL; expr_type_ = IVL_VT_REAL;
else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) expr_width_ = 1;
} else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) {
expr_type_ = IVL_VT_LOGIC; expr_type_ = IVL_VT_LOGIC;
else
expr_type_ = tru_type;
expr_width_ = max(tru_wid,fal_wid); expr_width_ = max(tru_wid,fal_wid);
} else {
ivl_assert(*this, tru_type == fal_type);
expr_type_ = tru_type;
expr_width_ = max(tru_wid,fal_wid);
}
expr_type__ = expr_type_; if (debug_elaborate)
cerr << get_fileline() << ": debug: "
<< "Ternary expression type=" << expr_type_
<< ", width=" << expr_width_
<< ", unsized_flag=" << flag
<< " (tru_type=" << tru_type
<< ", fal_type=" << fal_type << ")" << endl;
use_expr_type = expr_type_;
return expr_width_; return expr_width_;
} }
@ -2982,8 +3000,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope,
<< " of expression: " << *this << endl; << " of expression: " << *this << endl;
} }
ivl_assert(*this, use_wid > 0); ivl_assert(*this, use_wid > 0);
NetExpr*tru = elab_and_eval(des, scope, tru_, use_wid); NetExpr*tmp = elab_and_eval_alternative_(des, scope, tru_, use_wid);
return pad_to_width(tru, use_wid, *this); return pad_to_width(tmp, use_wid, *this);
} }
// Condition is constant FALSE, so we only need the // Condition is constant FALSE, so we only need the
@ -3000,21 +3018,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope,
<< " of expression: " << *this << endl; << " of expression: " << *this << endl;
} }
ivl_assert(*this, use_wid > 0); ivl_assert(*this, use_wid > 0);
NetExpr*fal = elab_and_eval(des, scope, fal_, use_wid); NetExpr*tmp = elab_and_eval_alternative_(des, scope, fal_, use_wid);
return pad_to_width(fal, use_wid, *this); return pad_to_width(tmp, use_wid, *this);
} }
// X and Z conditions need to blend both results, so we // X and Z conditions need to blend both results, so we
// can't short-circuit. // can't short-circuit.
} }
NetExpr*tru = elab_and_eval(des, scope, tru_, use_wid); NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, use_wid);
if (tru == 0) { if (tru == 0) {
delete con; delete con;
return 0; return 0;
} }
NetExpr*fal = elab_and_eval(des, scope, fal_, use_wid); NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, use_wid);
if (fal == 0) { if (fal == 0) {
delete con; delete con;
delete tru; delete tru;
@ -3042,6 +3060,22 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope,
return res; return res;
} }
/*
* When elaborating the true or false alternative expression of a
* ternary, take into account the overall expression type. If the type
* is not vectorable, then the alternative expression is evaluated as
* self-determined.
*/
NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope,
PExpr*expr, int use_wid) const
{
if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) {
return elab_and_eval(des, scope, expr, -1);
}
return elab_and_eval(des, scope, expr, use_wid);
}
unsigned PEUnary::test_width(Design*des, NetScope*scope, unsigned PEUnary::test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval, unsigned min, unsigned lval,
ivl_variable_type_t&expr_type__, ivl_variable_type_t&expr_type__,

View File

@ -1484,7 +1484,9 @@ NetExpr* NetETernary::eval_tree(int prune_to_width)
<< "constant condition value: "; << "constant condition value: ";
print_ternary_cond(cond_); print_ternary_cond(cond_);
cerr << get_fileline() << ": : Blending real cases " cerr << get_fileline() << ": : Blending real cases "
<< "to get " << val << endl; << "true=" << tv.as_double()
<< ", false=" << fv.as_double()
<< ", to get " << val << endl;
} }
NetECReal*rc = new NetECReal(val); NetECReal*rc = new NetECReal(val);

View File

@ -1184,12 +1184,21 @@ NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root)
osig->data_type(expr_type()); osig->data_type(expr_type());
osig->local_flag(true); osig->local_flag(true);
/* Make sure the types match. */
if (expr_type() == IVL_VT_REAL) {
tsig = cast_to_real(des, scope, tsig);
fsig = cast_to_real(des, scope, fsig);
}
/* Make sure both value operands are the right width. */ /* Make sure both value operands are the right width. */
if (type_is_vectorable(expr_type())) {
tsig = crop_to_width(des, pad_to_width(des, tsig, width, *this), width); tsig = crop_to_width(des, pad_to_width(des, tsig, width, *this), width);
fsig = crop_to_width(des, pad_to_width(des, fsig, width, *this), width); fsig = crop_to_width(des, pad_to_width(des, fsig, width, *this), width);
ivl_assert(*this, width == tsig->vector_width());
ivl_assert(*this, width == fsig->vector_width());
}
assert(width == tsig->vector_width());
assert(width == fsig->vector_width());
perm_string oname = csig->scope()->local_symbol(); perm_string oname = csig->scope()->local_symbol();
NetMux *mux = new NetMux(csig->scope(), oname, width, NetMux *mux = new NetMux(csig->scope(), oname, width,

View File

@ -2270,11 +2270,16 @@ ivl_variable_type_t NetESignal::expr_type() const
NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f) NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f)
: cond_(c), true_val_(t), false_val_(f) : cond_(c), true_val_(t), false_val_(f)
{ {
if (type_is_vectorable(expr_type())) {
// use widest result // use widest result
if (true_val_->expr_width() > false_val_->expr_width()) if (true_val_->expr_width() > false_val_->expr_width())
expr_width(true_val_->expr_width()); expr_width(true_val_->expr_width());
else else
expr_width(false_val_->expr_width()); expr_width(false_val_->expr_width());
} else {
expr_width(1);
}
cast_signed(c->has_sign() && t->has_sign() && f->has_sign()); cast_signed(c->has_sign() && t->has_sign() && f->has_sign());
} }