Do not evaluate parameters too early.

This patch pushes the evaluation of constant system functions into
normal elaboration vs doing them in the preliminary parameter
elaboration.

It also fixes the compiler version of clog2 to return integer_width
vs a fixed 32 bits.
This commit is contained in:
Cary R 2008-10-01 20:35:26 -07:00 committed by Stephen Williams
parent aebd9c2bc7
commit bd504ea14e
2 changed files with 97 additions and 90 deletions

View File

@ -268,66 +268,57 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
return tmp; return tmp;
} }
/* Reuse these routines from eval_tree.cc. */
NetExpr* evaluate_clog2(NetExpr*arg);
NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name);
NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name);
NetExpr* evaluate_abs(NetExpr*arg);
NetExpr* evaluate_min_max(NetExpr*arg0, NetExpr*arg1, const char*name);
NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
{ {
/* Only $clog2 and the builtin mathematical functions can /* Only $clog2 and the builtin mathematical functions can
* be a constant system function. */ * be a constant system function. */
perm_string name = peek_tail_name(path_); perm_string nm = peek_tail_name(path_);
if (name[0] == '$' && (generation_flag >= GN_VER2005 || if (nm[0] == '$' && (generation_flag >= GN_VER2005 ||
gn_icarus_misc_flag || gn_verilog_ams_flag)) { gn_icarus_misc_flag || gn_verilog_ams_flag)) {
if (name == "$clog2" || if (nm == "$clog2" ||
name == "$ln" || nm == "$ln" ||
name == "$log10" || nm == "$log10" ||
name == "$exp" || nm == "$exp" ||
name == "$sqrt" || nm == "$sqrt" ||
name == "$floor" || nm == "$floor" ||
name == "$ceil" || nm == "$ceil" ||
name == "$sin" || nm == "$sin" ||
name == "$cos" || nm == "$cos" ||
name == "$tan" || nm == "$tan" ||
name == "$asin" || nm == "$asin" ||
name == "$acos" || nm == "$acos" ||
name == "$atan" || nm == "$atan" ||
name == "$sinh" || nm == "$sinh" ||
name == "$cosh" || nm == "$cosh" ||
name == "$tanh" || nm == "$tanh" ||
name == "$asinh" || nm == "$asinh" ||
name == "$acosh" || nm == "$acosh" ||
name == "$atanh") { nm == "$atanh") {
if (parms_.size() != 1 || parms_[0] == 0) { if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: " << name cerr << get_fileline() << ": error: " << nm
<< " takes a single argument." << endl; << " takes a single argument." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope); NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
if (arg == 0) return 0; if (arg == 0) return 0;
eval_expr(arg); NetESFunc*rtn;
NetExpr*rtn; if (nm == "$clog2") {
if (peek_tail_name(path_) == "$clog2") { rtn = new NetESFunc(nm, IVL_VT_BOOL, integer_width, 1);
rtn = evaluate_clog2(arg);
} else { } else {
rtn = evaluate_math_one_arg(arg, name.str()); rtn = new NetESFunc(nm, IVL_VT_REAL, 1, 1);
}
delete arg;
if (rtn != 0) {
rtn->set_line(*this);
return rtn;
} }
rtn->set_line(*this);
rtn->cast_signed(true);
rtn->parm(0, arg);
return rtn;
} }
if (name == "$pow" || if (nm == "$pow" ||
name == "$atan2" || nm == "$atan2" ||
name == "$hypot") { nm == "$hypot") {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) { if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name cerr << get_fileline() << ": error: " << nm
<< " takes two arguments." << endl; << " takes two arguments." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
@ -335,45 +326,44 @@ NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope); NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope); NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
if (arg0 == 0 || arg1 == 0) return 0; if (arg0 == 0 || arg1 == 0) return 0;
eval_expr(arg0); NetESFunc*rtn = new NetESFunc(nm, IVL_VT_REAL, 1, 2);
eval_expr(arg1); rtn->set_line(*this);
NetExpr*rtn = evaluate_math_two_args(arg0, arg1, name.str()); rtn->cast_signed(true);
delete arg0; rtn->parm(0, arg0);
delete arg1; rtn->parm(1, arg1);
if (rtn != 0) { return rtn;
rtn->set_line(*this);
return rtn;
}
} }
/* These are only available with verilog-ams or icarus-misc. */ /* These are only available with verilog-ams or icarus-misc. */
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$log" || name == "$abs")) { (nm == "$log" || nm == "$abs")) {
if (parms_.size() != 1 || parms_[0] == 0) { if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: " << name cerr << get_fileline() << ": error: " << nm
<< " takes a single argument." << endl; << " takes a single argument." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
} }
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope); NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
if (arg == 0) return 0; if (arg == 0) return 0;
eval_expr(arg); NetESFunc*rtn;
NetExpr*rtn; if (nm == "$log") {
if (peek_tail_name(path_) == "$log") { rtn = new NetESFunc(nm, IVL_VT_REAL, 1, 1);
rtn = evaluate_math_one_arg(arg, name.str());
} else { } else {
rtn = evaluate_abs(arg); /* This can return either a real or an arbitrary
} * width vector, so set things to fail if this
delete arg; * does not get replaced with a constant during
if (rtn != 0) { * elaboration. */
rtn->set_line(*this); rtn = new NetESFunc(nm, IVL_VT_NO_TYPE, 0, 1);
return rtn;
} }
rtn->set_line(*this);
rtn->cast_signed(true);
rtn->parm(0, arg);
return rtn;
} }
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$min" || name == "$max")) { (nm == "$min" || nm == "$max")) {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) { if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name cerr << get_fileline() << ": error: " << nm
<< " takes two arguments." << endl; << " takes two arguments." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
@ -381,15 +371,13 @@ NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope); NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope); NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
if (arg0 == 0 || arg1 == 0) return 0; if (arg0 == 0 || arg1 == 0) return 0;
eval_expr(arg0); /* See $log above for why this has no type or width. */
eval_expr(arg1); NetESFunc*rtn = new NetESFunc(nm, IVL_VT_NO_TYPE, 0, 2);
NetExpr*rtn = evaluate_min_max(arg0, arg1, name.str()); rtn->set_line(*this);
delete arg0; rtn->cast_signed(true);
delete arg1; rtn->parm(0, arg0);
if (rtn != 0) { rtn->parm(1, arg1);
rtn->set_line(*this); return rtn;
return rtn;
}
} }
cerr << get_fileline() << ": error: this is not a constant " cerr << get_fileline() << ": error: this is not a constant "

View File

@ -1646,8 +1646,9 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width)
return new NetEConst(verinum(res, 1)); return new NetEConst(verinum(res, 1));
} }
NetExpr* evaluate_clog2(NetExpr*arg) NetExpr* evaluate_clog2(NetExpr*&arg)
{ {
eval_expr(arg);
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg); NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg); NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
if (tmpi || tmpr) { if (tmpi || tmpr) {
@ -1658,9 +1659,9 @@ NetExpr* evaluate_clog2(NetExpr*arg)
arg = verinum(tmpr->value().as_double(), true); arg = verinum(tmpr->value().as_double(), true);
} }
/* If we have an x in the verinum we return 32'bx. */ /* If we have an x in the verinum we return 'bx. */
if (!arg.is_defined()) { if (!arg.is_defined()) {
verinum tmp (verinum::Vx, 32); verinum tmp (verinum::Vx, integer_width);
tmp.has_sign(true); tmp.has_sign(true);
NetEConst*rtn = new NetEConst(tmp); NetEConst*rtn = new NetEConst(tmp);
return rtn; return rtn;
@ -1688,7 +1689,7 @@ NetExpr* evaluate_clog2(NetExpr*arg)
if (is_neg && res < integer_width) if (is_neg && res < integer_width)
res = integer_width; res = integer_width;
verinum tmp (res, 32); verinum tmp (res, integer_width);
NetEConst*rtn = new NetEConst(tmp); NetEConst*rtn = new NetEConst(tmp);
return rtn; return rtn;
} }
@ -1696,8 +1697,9 @@ NetExpr* evaluate_clog2(NetExpr*arg)
return 0; return 0;
} }
NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name) NetExpr* evaluate_math_one_arg(NetExpr*&arg, const char*name)
{ {
eval_expr(arg);
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg); NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg); NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
if (tmpi || tmpr) { if (tmpi || tmpr) {
@ -1752,8 +1754,10 @@ NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name)
return 0; return 0;
} }
NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name) NetExpr* evaluate_math_two_args(NetExpr*&arg0, NetExpr*&arg1, const char*name)
{ {
eval_expr(arg0);
eval_expr(arg1);
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0); NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0); NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1); NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
@ -1783,8 +1787,9 @@ NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name)
return 0; return 0;
} }
NetExpr* evaluate_abs(NetExpr*arg) NetExpr* evaluate_abs(NetExpr*&arg)
{ {
eval_expr(arg);
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg); NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
if (tmpi) { if (tmpi) {
verinum arg = tmpi->value(); verinum arg = tmpi->value();
@ -1803,8 +1808,10 @@ NetExpr* evaluate_abs(NetExpr*arg)
return 0; return 0;
} }
NetExpr* evaluate_min_max(NetExpr*arg0, NetExpr*arg1, const char*name) NetExpr* evaluate_min_max(NetExpr*&arg0, NetExpr*&arg1, const char*name)
{ {
eval_expr(arg0);
eval_expr(arg1);
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0); NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0); NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1); NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
@ -1879,11 +1886,13 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width)
<< " takes a single argument." << endl; << " takes a single argument." << endl;
return 0; return 0;
} }
NetExpr*arg = parm(0)->dup_expr();
if (strcmp(nm, "$clog2") == 0) { if (strcmp(nm, "$clog2") == 0) {
rtn = evaluate_clog2(parm(0)); rtn = evaluate_clog2(arg);
} else { } else {
rtn = evaluate_math_one_arg(parm(0), nm); rtn = evaluate_math_one_arg(arg, nm);
} }
delete arg;
} }
if (strcmp(nm, "$pow") == 0 || if (strcmp(nm, "$pow") == 0 ||
@ -1894,7 +1903,11 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width)
<< " takes two arguments." << endl; << " takes two arguments." << endl;
return 0; return 0;
} }
rtn = evaluate_math_two_args(parm(0), parm(1), nm); NetExpr*arg0 = parm(0)->dup_expr();
NetExpr*arg1 = parm(1)->dup_expr();
rtn = evaluate_math_two_args(arg0, arg1, nm);
delete arg0;
delete arg1;
} }
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
@ -1904,11 +1917,13 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width)
<< " takes a single argument." << endl; << " takes a single argument." << endl;
return 0; return 0;
} }
NetExpr*arg = parm(0)->dup_expr();
if (strcmp(nm, "$log") == 0) { if (strcmp(nm, "$log") == 0) {
rtn = evaluate_math_one_arg(parm(0), nm); rtn = evaluate_math_one_arg(arg, nm);
} else { } else {
rtn = evaluate_abs(parm(0)); rtn = evaluate_abs(arg);
} }
delete arg;
} }
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
@ -1918,7 +1933,11 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width)
<< " takes two arguments." << endl; << " takes two arguments." << endl;
return 0; return 0;
} }
rtn = evaluate_min_max(parm(0), parm(1), nm); NetExpr*arg0 = parm(0)->dup_expr();
NetExpr*arg1 = parm(1)->dup_expr();
rtn = evaluate_min_max(arg0, arg1, nm);
delete arg0;
delete arg1;
} }
if (rtn != 0) { if (rtn != 0) {