Account for real type l-values when working with widths.

Widths of real values are always 1. When paired with vectorable types
in expressions, the vectorable type is processed as losslessly self-
determined. ("unsized" in the test_width methods.)
This commit is contained in:
Stephen Williams 2008-09-22 21:09:06 -07:00
parent 2381fc72b0
commit ce7dd6b4ff
6 changed files with 109 additions and 25 deletions

View File

@ -103,7 +103,8 @@ class PAssign_ : public Statement {
protected: protected:
NetAssign_* elaborate_lval(Design*, NetScope*scope) const; 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_; PExpr* delay_;
PEventStatement*event_; PEventStatement*event_;

View File

@ -31,6 +31,17 @@
# include "util.h" # include "util.h"
# include "ivl_assert.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 * The default behavior for the test_width method is to just return the
* minimum width that is passed in. * minimum width that is passed in.
@ -61,8 +72,8 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope,
{ {
bool flag_left = false; bool flag_left = false;
bool flag_right = false; bool flag_right = false;
unsigned wid_left = left_->test_width(des,scope, min, lval, flag_left); unsigned wid_left = left_->test_width(des,scope, min, 0, flag_left);
unsigned wid_right = right_->test_width(des,scope, min, lval, flag_right); unsigned wid_right = right_->test_width(des,scope, min, 0, flag_right);
if (flag_left || flag_right) if (flag_left || flag_right)
unsized_flag = true; unsized_flag = true;
@ -82,8 +93,16 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope,
min = lval; min = lval;
break; break;
case 'l': case 'l': // << Should be handled by PEBShift
ivl_assert(*this, 0); // Should be handled bin 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: default:
if (wid_left > min) if (wid_left > min)
min = wid_left; min = wid_left;
@ -114,6 +133,24 @@ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope,
return 0; 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); NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid);
return tmp; return tmp;
} }
@ -513,7 +550,17 @@ NetExpr* PEBinary::elaborate_expr_base_add_(Design*des,
int expr_wid) const int expr_wid) const
{ {
NetExpr*tmp; 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 if (expr_wid > 0 && (tmp->expr_type() == IVL_VT_BOOL
|| tmp->expr_type() == IVL_VT_LOGIC)) || tmp->expr_type() == IVL_VT_LOGIC))
tmp->set_width(expr_wid); tmp->set_width(expr_wid);

View File

@ -100,8 +100,14 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
} }
bool unsized_flag = false; bool unsized_flag = false;
unsigned use_width = pin(1)->test_width(des, scope, lval->vector_width(), unsigned use_width = 0;
lval->vector_width(), unsized_flag); 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) { if (debug_elaborate) {
cerr << get_fileline() << ": debug: PGAssign: r-value tested " 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(); int expr_wid = lval->vector_width();
if (use_width > (unsigned)expr_wid) if (use_width > (unsigned)expr_wid)
expr_wid = (int)use_width; 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), NetExpr*rval_expr = elab_and_eval(des, scope, pin(1),
expr_wid, lval->vector_width()); 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, 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_); ivl_assert(*this, rval_);
@ -1717,13 +1726,26 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope,
something else based on self-determined widths inside. */ something else based on self-determined widths inside. */
unsigned use_width = lv_width; unsigned use_width = lv_width;
bool unsized_flag = false; bool unsized_flag = false;
unsigned tmp_width = rval()->test_width(des, scope, use_width, use_width, unsized_flag); unsigned tmp_width = 0;
if (tmp_width > use_width)
use_width = tmp_width; 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 /* Now elaborate to the expected width. Pass the lwidth to
prune any constant result to fit with the lvalue at hand. */ 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; if (rv == 0) return 0;
return rv; return rv;
@ -1811,7 +1833,7 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
/* Elaborate the r-value expression, then try to evaluate it. */ /* 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; if (rv == 0) return 0;
assert(rv); assert(rv);
@ -1976,7 +1998,7 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
NetAssign_*lv = elaborate_lval(des, scope); NetAssign_*lv = elaborate_lval(des, scope);
if (lv == 0) return 0; 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 /* Handle the (common) case that the r-value is a vector. This
includes just about everything but reals. In this case, we includes just about everything but reals. In this case, we

View File

@ -81,6 +81,11 @@ unsigned NetAssign_::lwidth() const
return lwid_; return lwid_;
} }
ivl_variable_type_t NetAssign_::expr_type() const
{
return sig_->data_type();
}
perm_string NetAssign_::name() const perm_string NetAssign_::name() const
{ {
if (sig_) { if (sig_) {

View File

@ -2139,6 +2139,7 @@ class NetAssign_ {
// method accounts for the presence of the mux, so it is not // method accounts for the presence of the mux, so it is not
// necessarily the same as the pin_count(). // necessarily the same as the pin_count().
unsigned lwidth() const; unsigned lwidth() const;
ivl_variable_type_t expr_type() const;
// Get the name of the underlying object. // Get the name of the underlying object.
perm_string name() const; perm_string name() const;

View File

@ -81,16 +81,24 @@ static void show_binary_expression(ivl_expr_t net, unsigned ind)
switch (ivl_expr_opcode(net)) { switch (ivl_expr_opcode(net)) {
case '*': case '*':
/* The width of multiply expressions is the sum of the if (ivl_expr_value(net) == IVL_VT_REAL) {
widths of the operands. This is slightly different if (ivl_expr_width(net) != 1) {
from the way the Verilog standard does it, but allows fprintf(out, "%*sERROR: Result width incorrect. Expecting 1, got %u\n",
us to keep operands smaller. */ ind+3, "", ivl_expr_width(net));
width = ivl_expr_width(ivl_expr_oper1(net)); stub_errors += 1;
width += ivl_expr_width(ivl_expr_oper2(net)); }
if (ivl_expr_width(net) != width) { } else {
fprintf(out, "%*sERROR: Result width incorrect. Expecting %u, got %u\n", /* The width of multiply expressions is the sum of the
ind+3, "", width, ivl_expr_width(net)); widths of the operands. This is slightly different
stub_errors += 1; 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; break;