diff --git a/eval_tree.cc b/eval_tree.cc index fcecd9bea..b06425bb1 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -34,6 +34,18 @@ NetExpr* NetExpr::eval_tree() return 0; } +static void eval_debug(const NetExpr*expr, NetExpr*res, bool is_real) +{ + if (res != 0) { + res->set_line(*expr); + if (debug_eval_tree) { + cerr << expr->get_fileline() << ": debug: Evaluated"; + if (is_real) cerr << " (real)"; + cerr << ": " << *expr << " --> " << *res << endl; + } + } +} + static bool get_real_arg_(const NetExpr*expr, verireal&val) { switch (expr->expr_type()) { @@ -1179,7 +1191,7 @@ NetEConst* NetEConcat::eval_arguments_(const vector&vals, NetEConst*res = new NetEConst(val); ivl_assert(*this, res); - res->set_line(*this); + res->set_line(*this); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluated: " << *this << " --> " << *res << endl; @@ -1596,12 +1608,10 @@ NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const return tmp; } -static NetEConst* evaluate_clog2(NetExpr*&arg_) +NetEConst* NetESFunc::evaluate_clog2_(const NetExpr*arg_) const { - eval_expr(arg_); - - NetEConst*tmpi = dynamic_cast(arg_); - NetECReal*tmpr = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi == 0 && tmpr == 0) return 0; @@ -1620,7 +1630,7 @@ static NetEConst* evaluate_clog2(NetExpr*&arg_) tmp.has_sign(true); rtn = new NetEConst(tmp); - ivl_assert(*arg_, rtn); + ivl_assert(*this, rtn); } else { bool is_neg = false; uint64_t res = 0; @@ -1649,18 +1659,17 @@ static NetEConst* evaluate_clog2(NetExpr*&arg_) tmp.has_sign(true); rtn = new NetEConst(tmp); - ivl_assert(*arg_, rtn); + ivl_assert(*this, rtn); } + eval_debug(this, rtn, false); return rtn; } -static NetECReal* evaluate_math_one_arg(NetExpr*&arg_, const char*name) +NetECReal* NetESFunc::evaluate_math_one_arg_(ID id, const NetExpr*arg_) const { - eval_expr(arg_); - - NetEConst*tmpi = dynamic_cast(arg_); - NetECReal*tmpr = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); NetECReal*res = 0; @@ -1672,79 +1681,79 @@ static NetECReal* evaluate_math_one_arg(NetExpr*&arg_, const char*name) arg = tmpr->value().as_double(); } - if (strcmp(name, "$ln") == 0) { + switch (id) { + case LN: res = new NetECReal(verireal(log(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$log10") == 0) { + break; + case LOG10: res = new NetECReal(verireal(log10(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$exp") == 0) { + break; + case EXP: res = new NetECReal(verireal(exp(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sqrt") == 0) { + break; + case SQRT: res = new NetECReal(verireal(sqrt(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$floor") == 0) { + break; + case FLOOR: res = new NetECReal(verireal(floor(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$ceil") == 0) { + break; + case CEIL: res = new NetECReal(verireal(ceil(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sin") == 0) { + break; + case SIN: res = new NetECReal(verireal(sin(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$cos") == 0) { + break; + case COS: res = new NetECReal(verireal(cos(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$tan") == 0) { + break; + case TAN: res = new NetECReal(verireal(tan(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$asin") == 0) { + break; + case ASIN: res = new NetECReal(verireal(asin(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$acos") == 0) { + break; + case ACOS: res = new NetECReal(verireal(acos(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$atan") == 0) { + break; + case ATAN: res = new NetECReal(verireal(atan(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sinh") == 0) { + break; + case SINH: res = new NetECReal(verireal(sinh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$cosh") == 0) { + break; + case COSH: res = new NetECReal(verireal(cosh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$tanh") == 0) { + break; + case TANH: res = new NetECReal(verireal(tanh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$asinh") == 0) { + break; + case ASINH: res = new NetECReal(verireal(asinh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$acosh") == 0) { + break; + case ACOSH: res = new NetECReal(verireal(acosh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$atanh") == 0) { + break; + case ATANH: res = new NetECReal(verireal(atanh(arg))); - ivl_assert(*arg_, res); - } else { - cerr << arg_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, true); return res; } -static NetECReal* evaluate_math_two_args(NetExpr*&arg0_, NetExpr*&arg1_, - const char*name) +NetECReal* NetESFunc::evaluate_math_two_arg_(ID id, const NetExpr*arg0_, + const NetExpr*arg1_) const { - eval_expr(arg0_); - eval_expr(arg1_); - - NetEConst*tmpi0 = dynamic_cast(arg0_); - NetECReal*tmpr0 = dynamic_cast(arg0_); - NetEConst*tmpi1 = dynamic_cast(arg1_); - NetECReal*tmpr1 = dynamic_cast(arg1_); + const NetEConst*tmpi0 = dynamic_cast(arg0_); + const NetECReal*tmpr0 = dynamic_cast(arg0_); + const NetEConst*tmpi1 = dynamic_cast(arg1_); + const NetECReal*tmpr1 = dynamic_cast(arg1_); NetECReal*res = 0; @@ -1761,73 +1770,78 @@ static NetECReal* evaluate_math_two_args(NetExpr*&arg0_, NetExpr*&arg1_, arg1 = tmpr1->value().as_double(); } - if (strcmp(name, "$pow") == 0) { + switch (id) { + case POW: res = new NetECReal(verireal(pow(arg0, arg1))); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$atan2") == 0) { + break; + case ATAN2: res = new NetECReal(verireal(atan2(arg0, arg1))); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$hypot") == 0) { + break; + case HYPOT: res = new NetECReal(verireal(hypot(arg0, arg1))); - ivl_assert(*arg0_, res); - } else { - cerr << arg0_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, true); return res; } -static NetExpr* evaluate_abs(NetExpr*&arg_) +NetExpr* NetESFunc::evaluate_abs_(const NetExpr*arg_) const { - eval_expr(arg_); - NetExpr*res = 0; - NetEConst*tmpi = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); if (tmpi) { verinum arg = tmpi->value(); if (arg.is_negative()) { arg = v_not(arg) + verinum(1); } res = new NetEConst(arg); - ivl_assert(*arg_, res); + ivl_assert(*this, res); } - NetECReal*tmpr = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); if (tmpr) { double arg = tmpr->value().as_double(); res = new NetECReal(verireal(fabs(arg))); - ivl_assert(*arg_, res); + ivl_assert(*this, res); } + eval_debug(this, res, tmpr != 0); return res; } -static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, - const char*name) +NetExpr* NetESFunc::evaluate_min_max_(ID id, const NetExpr*arg0_, + const NetExpr*arg1_) const { - eval_expr(arg0_); - eval_expr(arg1_); - - NetEConst*tmpi0 = dynamic_cast(arg0_); - NetECReal*tmpr0 = dynamic_cast(arg0_); - NetEConst*tmpi1 = dynamic_cast(arg1_); - NetECReal*tmpr1 = dynamic_cast(arg1_); + const NetEConst*tmpi0 = dynamic_cast(arg0_); + const NetECReal*tmpr0 = dynamic_cast(arg0_); + const NetEConst*tmpi1 = dynamic_cast(arg1_); + const NetECReal*tmpr1 = dynamic_cast(arg1_); NetExpr*res = 0; if (tmpi0 && tmpi1) { verinum arg0 = tmpi0->value(); verinum arg1 = tmpi1->value(); - if (strcmp(name, "$min") == 0) { + switch (id) { + case MIN: res = new NetEConst( arg0 < arg1 ? arg0 : arg1); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$max") == 0) { + break; + case MAX: res = new NetEConst( arg0 < arg1 ? arg1 : arg0); - ivl_assert(*arg0_, res); + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); + } else if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) { double arg0, arg1; if (tmpi0) { @@ -1840,118 +1854,128 @@ static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, } else { arg1 = tmpr1->value().as_double(); } - if (strcmp(name, "$min") == 0) { + switch (id) { + case MIN: res = new NetECReal(verireal(arg0 < arg1 ? arg0 : arg1)); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$max") == 0) { + break; + case MAX: res = new NetECReal(verireal(arg0 < arg1 ? arg1 : arg0)); - ivl_assert(*arg0_, res); - } else { - cerr << arg0_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, tmpr0 || tmpr1); return res; } +NetExpr* NetESFunc::evaluate_one_arg_(ID id, const NetExpr*arg) const +{ + switch (id) { + case ABS: + return evaluate_abs_(arg); + case CLOG2: + return evaluate_clog2_(arg); + default: + return evaluate_math_one_arg_(id, arg); + } +} + +NetExpr* NetESFunc::evaluate_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const +{ + switch (id) { + case MIN: + case MAX: + return evaluate_min_max_(id, arg0, arg1); + default: + return evaluate_math_two_arg_(id, arg0, arg1); + } +} + +NetESFunc::ID NetESFunc::built_in_id_() const +{ + /* If we are not targeting at least Verilog-2005, Verilog-AMS + * or using the Icarus misc flag then we do not treat these + * functions as built-in. */ + if (generation_flag < GN_VER2005 && + !gn_icarus_misc_flag && !gn_verilog_ams_flag) { + return NOT_BUILT_IN; + } + + static map built_in_func; + + if (built_in_func.empty()) { + built_in_func["$clog2"] = CLOG2; + built_in_func["$ln" ] = LN; + built_in_func["$log10"] = LOG10; + built_in_func["$exp" ] = EXP; + built_in_func["$sqrt" ] = SQRT; + built_in_func["$floor"] = FLOOR; + built_in_func["$ceil" ] = CEIL; + built_in_func["$sin" ] = SIN; + built_in_func["$cos" ] = COS; + built_in_func["$tan" ] = TAN; + built_in_func["$asin" ] = ASIN; + built_in_func["$acos" ] = ACOS; + built_in_func["$atan" ] = ATAN; + built_in_func["$sinh" ] = SINH; + built_in_func["$cosh" ] = COSH; + built_in_func["$tanh" ] = TANH; + built_in_func["$asinh"] = ASINH; + built_in_func["$acosh"] = ACOSH; + built_in_func["$atanh"] = ATANH; + built_in_func["$abs" ] = ABS; + built_in_func["$pow" ] = POW; + built_in_func["$atan2"] = ATAN2; + built_in_func["$hypot"] = HYPOT; + built_in_func["$min" ] = MIN; + built_in_func["$max" ] = MAX; + } + + map::iterator idx = built_in_func.find(name_); + if (idx == built_in_func.end()) + return NOT_BUILT_IN; + + ID id = idx->second; + + if (is_ams_(id) && !gn_icarus_misc_flag && !gn_verilog_ams_flag) + return NOT_BUILT_IN; + + return id; +} + NetExpr* NetESFunc::eval_tree() { - /* If we are not targeting at least Verilog-2005, Verilog-AMS - * or using the Icarus misc flag then we do not support these - * functions as constant. */ - if (generation_flag < GN_VER2005 && - !gn_icarus_misc_flag && !gn_verilog_ams_flag) { + ID id = built_in_id_(); + if (id == NOT_BUILT_IN) + return 0; + + switch (nargs_(id)) { + case 1: + if (parms_.size() != 1) { + cerr << get_fileline() << ": error: " << name_ + << " takes one argument." << endl; + return 0; + } + eval_expr(parms_[0]); + return evaluate_one_arg_(id, parms_[0]); + case 2: + if (parms_.size() != 2) { + cerr << get_fileline() << ": error: " << name_ + << " takes two arguments." << endl; + return 0; + } + eval_expr(parms_[0]); + eval_expr(parms_[1]); + return evaluate_two_arg_(id, parms_[0], parms_[1]); + default: + ivl_assert(*this, 0); return 0; } - - const char*nm = name(); - NetExpr*rtn = 0; - /* Only $clog2 and the builtin mathematical functions can - * be a constant system function. */ - if (strcmp(nm, "$clog2") == 0 || - strcmp(nm, "$ln") == 0 || - strcmp(nm, "$log10") == 0 || - strcmp(nm, "$exp") == 0 || - strcmp(nm, "$sqrt") == 0 || - strcmp(nm, "$floor") == 0 || - strcmp(nm, "$ceil") == 0 || - strcmp(nm, "$sin") == 0 || - strcmp(nm, "$cos") == 0 || - strcmp(nm, "$tan") == 0 || - strcmp(nm, "$asin") == 0 || - strcmp(nm, "$acos") == 0 || - strcmp(nm, "$atan") == 0 || - strcmp(nm, "$sinh") == 0 || - strcmp(nm, "$cosh") == 0 || - strcmp(nm, "$tanh") == 0 || - strcmp(nm, "$asinh") == 0 || - strcmp(nm, "$acosh") == 0 || - strcmp(nm, "$atanh") == 0) { - if (nparms() != 1 || parm(0) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes a single argument." << endl; - return 0; - } - NetExpr*arg = parm(0)->dup_expr(); - if (strcmp(nm, "$clog2") == 0) { - rtn = evaluate_clog2(arg); - } else { - rtn = evaluate_math_one_arg(arg, nm); - } - delete arg; - } - - if (strcmp(nm, "$pow") == 0 || - strcmp(nm, "$atan2") == 0 || - strcmp(nm, "$hypot") == 0) { - if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes two arguments." << endl; - return 0; - } - 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) && - (strcmp(nm, "$abs") == 0)) { - if (nparms() != 1 || parm(0) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes a single argument." << endl; - return 0; - } - NetExpr*arg = parm(0)->dup_expr(); - rtn = evaluate_abs(arg); - delete arg; - } - - if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && - (strcmp(nm, "$min") == 0 || strcmp(nm, "$max") == 0)) { - if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes two arguments." << endl; - return 0; - } - 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) { - rtn->set_line(*this); - - if (debug_eval_tree) - cerr << get_fileline() << ": debug: Evaluated: " << *this - << " --> " << *rtn << endl; - } - - return rtn; } NetExpr* NetEUFunc::eval_tree() diff --git a/net_func_eval.cc b/net_func_eval.cc index 727d469cf..846bd7914 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -439,6 +439,41 @@ NetExpr* NetEUnary::evaluate_function(const LineInfo&loc, return res; } +NetExpr* NetESFunc::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + ID id = built_in_id_(); + if (id == NOT_BUILT_IN) { + cerr << get_fileline() << ": error: " << name_ + << " is not a built-in function, so cannot" + << " be used in a constant function." << endl; + return 0; + } + + NetExpr*val0 = 0; + NetExpr*val1 = 0; + NetExpr*res = 0; + switch (nargs_(id)) { + case 1: + val0 = parms_[0]->evaluate_function(loc, context_map); + if (val0 == 0) break; + res = evaluate_one_arg_(id, val0); + break; + case 2: + val0 = parms_[0]->evaluate_function(loc, context_map); + val1 = parms_[1]->evaluate_function(loc, context_map); + if (val0 == 0 || val1 == 0) break; + res = evaluate_two_arg_(id, val0, val1); + break; + default: + ivl_assert(*this, 0); + break; + } + delete val0; + delete val1; + return res; +} + NetExpr* NetEUFunc::evaluate_function(const LineInfo&loc, map&context_map) const { diff --git a/netlist.h b/netlist.h index 60034c049..b30bb3774 100644 --- a/netlist.h +++ b/netlist.h @@ -4005,6 +4005,8 @@ class NetESFunc : public NetExpr { const NetExpr* parm(unsigned idx) const; virtual NetExpr* eval_tree(); + virtual NetExpr* evaluate_function(const LineInfo&loc, + std::map&ctx) const; virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -4016,11 +4018,43 @@ class NetESFunc : public NetExpr { virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); private: + enum ID { NOT_BUILT_IN = 0x0, + MATH_ONE_ARG = 0x100, + CLOG2, LN, LOG10, EXP, SQRT, FLOOR, CEIL, + SIN, COS, TAN, ASIN, ACOS, ATAN, + SINH, COSH, TANH, ASINH, ACOSH, ATANH, + AMS_ONE_ARG = 0x180, + ABS, + MATH_TWO_ARG = 0x200, + POW, ATAN2, HYPOT, + AMS_TWO_ARG = 0x280, + MIN, MAX }; + + bool is_ams_(ID id) const { return id & 0x80; }; + unsigned nargs_(ID id) const { return id >> 8; }; + const char* name_; ivl_variable_type_t type_; netenum_t*enum_type_; std::vectorparms_; + ID built_in_id_() const; + + NetExpr* evaluate_one_arg_(ID id, const NetExpr*arg) const; + NetExpr* evaluate_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + + NetEConst* evaluate_clog2_(const NetExpr*arg) const; + + NetECReal* evaluate_math_one_arg_(ID id, const NetExpr*arg) const; + NetECReal* evaluate_math_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + + NetExpr* evaluate_abs_(const NetExpr*arg) const; + + NetExpr* evaluate_min_max_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + private: // not implemented NetESFunc(const NetESFunc&); NetESFunc& operator= (const NetESFunc&);