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:
parent
8eb7e4b3d4
commit
707a3ebe27
4
PExpr.h
4
PExpr.h
|
|
@ -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_;
|
||||||
|
|
|
||||||
70
elab_expr.cc
70
elab_expr.cc
|
|
@ -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__,
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue