Merge branch 'master' into elaborate-net-rework

This commit is contained in:
Stephen Williams 2008-08-29 22:13:07 -07:00
commit bc3411e28e
17 changed files with 907 additions and 129 deletions

View File

@ -20,8 +20,8 @@
# include "config.h"
# include <iostream>
# include <cstring>
# include "compiler.h"
# include "PExpr.h"
# include "Module.h"
# include <typeinfo>
@ -131,18 +131,76 @@ PECallFunction::~PECallFunction()
bool PECallFunction::is_constant(Module*mod) const
{
/* Only $clog2 can be a constant system function. */
if (peek_tail_name(path_)[0] == '$') {
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
/* Only $clog2 and the builtin mathematical functions can
* be a constant system function. */
perm_string name = peek_tail_name(path_);
if (name[0] == '$' && (generation_flag >= GN_VER2005 ||
gn_icarus_misc_flag || gn_verilog_ams_flag)) {
if (name == "$clog2" ||
name == "$ln" ||
name == "$log10" ||
name == "$exp" ||
name == "$sqrt" ||
name == "$floor" ||
name == "$ceil" ||
name == "$sin" ||
name == "$cos" ||
name == "$tan" ||
name == "$asin" ||
name == "$acos" ||
name == "$atan" ||
name == "$sinh" ||
name == "$cosh" ||
name == "$tanh" ||
name == "$asinh" ||
name == "$acosh" ||
name == "$atanh") {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: $clog2 takes a "
"single argument." << endl;
cerr << get_fileline() << ": error: " << name
<< " takes a single argument." << endl;
return false;
}
/* If the argument is constant $clog2 is constant. */
/* If the argument is constant the function is constant. */
return parms_[0]->is_constant(mod);
}
return false; /* Most system functions are not constant. */
if (name == "$pow" ||
name == "$atan2" ||
name == "$hypot") {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes two arguments." << endl;
return false;
/* If the arguments are constant the function is constant. */
return parms_[0]->is_constant(mod) &&
parms_[1]->is_constant(mod);
}
}
/* These are only available with verilog-ams or icarus-misc. */
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$log" || name == "$abs")) {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes a single argument." << endl;
return false;
}
/* If the argument is constant the function is constant. */
return parms_[0]->is_constant(mod);
}
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$min" || name == "$max")) {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes two arguments." << endl;
return false;
/* If the arguments are constant the function is constant. */
return parms_[0]->is_constant(mod) &&
parms_[1]->is_constant(mod);
}
}
return false; /* The other system functions are not constant. */
}
/* Checking for constant user functions goes here. */

View File

@ -106,7 +106,7 @@ const char*npath = 0;
const char*targ = "vvp";
const char*depfile = 0;
const char*generation = "2x";
const char*generation = "2005";
const char*gen_specify = "specify";
const char*gen_xtypes = "xtypes";
const char*gen_icarus = "icarus-misc";
@ -765,9 +765,18 @@ int main(int argc, char **argv)
how to handle them. */
fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep);
/* If verilog-ams is enabled, then include the va_math module
as well. */
if (strcmp(gen_verilog_ams,"verilog-ams") == 0) {
/* If verilog-2005 is enabled or icarus-misc or verilog-ams,
* then include the v2005_math library. */
if (strcmp(generation, "2005") == 0 ||
strcmp(gen_icarus, "icarus-misc") == 0 ||
strcmp(gen_verilog_ams, "verilog-ams") == 0) {
fprintf(iconfig_file, "sys_func:%s%cv2005_math.sft\n", base, sep);
fprintf(iconfig_file, "module:v2005_math\n");
}
/* If verilog-ams or icarus_misc is enabled, then include the
* va_math module as well. */
if (strcmp(gen_verilog_ams,"verilog-ams") == 0 ||
strcmp(gen_icarus, "icarus-misc") == 0) {
fprintf(iconfig_file, "sys_func:%s%cva_math.sft\n", base, sep);
fprintf(iconfig_file, "module:va_math\n");
}

View File

@ -25,7 +25,6 @@
# include "netmisc.h"
# include <cstdlib>
# include <cstring>
# include <iostream>
# include "ivl_assert.h"
@ -269,23 +268,54 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
return tmp;
}
/* Reuse the routine from eval_tree.cc. */
/* 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
{
/* For now only $clog2 can be a constant system function. */
if (peek_tail_name(path_)[0] == '$') {
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
/* Only $clog2 and the builtin mathematical functions can
* be a constant system function. */
perm_string name = peek_tail_name(path_);
if (name[0] == '$' && (generation_flag >= GN_VER2005 ||
gn_icarus_misc_flag || gn_verilog_ams_flag)) {
if (name == "$clog2" ||
name == "$ln" ||
name == "$log10" ||
name == "$exp" ||
name == "$sqrt" ||
name == "$floor" ||
name == "$ceil" ||
name == "$sin" ||
name == "$cos" ||
name == "$tan" ||
name == "$asin" ||
name == "$acos" ||
name == "$atan" ||
name == "$sinh" ||
name == "$cosh" ||
name == "$tanh" ||
name == "$asinh" ||
name == "$acosh" ||
name == "$atanh") {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: $clog2 takes a "
"single argument." << endl;
cerr << get_fileline() << ": error: " << name
<< " takes a single argument." << endl;
des->errors += 1;
return 0;
}
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
if (arg == 0) return 0;
eval_expr(arg);
NetExpr*rtn = evaluate_clog2(arg);
NetExpr*rtn;
if (peek_tail_name(path_) == "$clog2") {
rtn = evaluate_clog2(arg);
} else {
rtn = evaluate_math_one_arg(arg, name.str());
}
delete arg;
if (rtn != 0) {
rtn->set_line(*this);
@ -293,6 +323,75 @@ NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
}
}
if (name == "$pow" ||
name == "$atan2" ||
name == "$hypot") {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes two arguments." << endl;
des->errors += 1;
return 0;
}
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
if (arg0 == 0 || arg1 == 0) return 0;
eval_expr(arg0);
eval_expr(arg1);
NetExpr*rtn = evaluate_math_two_args(arg0, arg1, name.str());
delete arg0;
delete arg1;
if (rtn != 0) {
rtn->set_line(*this);
return rtn;
}
}
/* These are only available with verilog-ams or icarus-misc. */
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$log" || name == "$abs")) {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes a single argument." << endl;
des->errors += 1;
return 0;
}
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
if (arg == 0) return 0;
eval_expr(arg);
NetExpr*rtn;
if (peek_tail_name(path_) == "$log") {
rtn = evaluate_math_one_arg(arg, name.str());
} else {
rtn = evaluate_abs(arg);
}
delete arg;
if (rtn != 0) {
rtn->set_line(*this);
return rtn;
}
}
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(name == "$min" || name == "$max")) {
if (parms_.size() != 2 || parms_[0] == 0 || parms_[1] == 0) {
cerr << get_fileline() << ": error: " << name
<< " takes two arguments." << endl;
des->errors += 1;
return 0;
}
NetExpr*arg0 = parms_[0]->elaborate_pexpr(des, scope);
NetExpr*arg1 = parms_[1]->elaborate_pexpr(des, scope);
if (arg0 == 0 || arg1 == 0) return 0;
eval_expr(arg0);
eval_expr(arg1);
NetExpr*rtn = evaluate_min_max(arg0, arg1, name.str());
delete arg0;
delete arg1;
if (rtn != 0) {
rtn->set_line(*this);
return rtn;
}
}
cerr << get_fileline() << ": error: this is not a constant "
"system function (" << *this << ")." << endl;
des->errors += 1;

View File

@ -340,19 +340,15 @@ NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag)
switch (le->expr_type()) {
case IVL_VT_REAL:
rtmp = dynamic_cast<NetECReal*> (le);
if (rtmp == 0)
return 0;
if (rtmp == 0) return 0;
lv = rtmp->value().as_double();
break;
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
vtmp = dynamic_cast<NetEConst*> (le);
if (vtmp == 0)
return 0;
lv = vtmp->value().as_long();
if (vtmp == 0) return 0;
lv = vtmp->value().as_double();
break;
default:
@ -361,23 +357,18 @@ NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag)
assert(0);
}
switch (ri->expr_type()) {
case IVL_VT_REAL:
rtmp = dynamic_cast<NetECReal*> (ri);
if (rtmp == 0)
return 0;
if (rtmp == 0) return 0;
rv = rtmp->value().as_double();
break;
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
vtmp = dynamic_cast<NetEConst*> (ri);
if (vtmp == 0)
return 0;
rv = vtmp->value().as_long();
if (vtmp == 0) return 0;
rv = vtmp->value().as_double();
break;
default:
@ -481,19 +472,6 @@ NetEConst* NetEBComp::eval_gt_()
return tmp;
}
/* Compare with a real value. Do it as double precision. */
if (right_->expr_type() == IVL_VT_REAL) {
NetECReal*tmp = dynamic_cast<NetECReal*>(right_);
if (tmp == 0)
return 0;
double rr = tmp->value().as_double();
double ll = lv.has_sign()? lv.as_long() : lv.as_ulong();
verinum result ((ll > rr)? verinum::V1 : verinum::V0, 1, true);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
@ -532,19 +510,6 @@ NetEConst* NetEBComp::eval_gteq_()
return tmp;
}
/* Compare with a real value. Do it as double precision. */
if (right_->expr_type() == IVL_VT_REAL) {
NetECReal*tmp = dynamic_cast<NetECReal*>(right_);
if (tmp == 0)
return 0;
double rr = tmp->value().as_double();
double ll = lv.has_sign()? lv.as_long() : lv.as_ulong();
verinum result ((ll >= rr)? verinum::V1 : verinum::V0, 1, true);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
@ -570,8 +535,66 @@ NetEConst* NetEBComp::eval_gteq_()
* are equal, but there are are x/z bits, then the situation is
* ambiguous so the result is x.
*/
NetEConst* NetEBComp::eval_eqeq_real_(NetExpr*le, NetExpr*ri, bool ne_flag)
{
NetEConst*vtmp;
NetECReal*rtmp;
double lv, rv;
switch (le->expr_type()) {
case IVL_VT_REAL:
rtmp = dynamic_cast<NetECReal*> (le);
if (rtmp == 0) return 0;
lv = rtmp->value().as_double();
break;
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
vtmp = dynamic_cast<NetEConst*> (le);
if (vtmp == 0) return 0;
lv = vtmp->value().as_double();
break;
default:
cerr << get_fileline() << ": internal error: "
<< "Unexpected expression type? " << le->expr_type() << endl;
assert(0);
}
switch (ri->expr_type()) {
case IVL_VT_REAL:
rtmp = dynamic_cast<NetECReal*> (ri);
if (rtmp == 0) return 0;
rv = rtmp->value().as_double();
break;
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
vtmp = dynamic_cast<NetEConst*> (ri);
if (vtmp == 0) return 0;
rv = vtmp->value().as_double();
break;
default:
cerr << get_fileline() << ": internal error: "
<< "Unexpected expression type? " << ri->expr_type() << endl;
assert(0);
}
verinum result((lv == rv ^ ne_flag) ? verinum::V1 : verinum::V0, 1);
vtmp = new NetEConst(result);
vtmp->set_line(*this);
return vtmp;
}
NetEConst* NetEBComp::eval_eqeq_(bool ne_flag)
{
if (right_->expr_type() == IVL_VT_REAL)
return eval_eqeq_real_(right_, left_, ne_flag);
if (left_->expr_type() == IVL_VT_REAL)
return eval_eqeq_real_(right_, left_, ne_flag);
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
NetEConst*r = dynamic_cast<NetEConst*>(right_);
@ -1673,25 +1696,238 @@ NetExpr* evaluate_clog2(NetExpr*arg)
return 0;
}
NetExpr* NetESFunc::eval_tree(int prune_to_width)
NetExpr* evaluate_math_one_arg(NetExpr*arg, const char*name)
{
/* For now only $clog2 can be a constant system function. */
if (strcmp(name(), "$clog2") == 0) {
if (nparms() != 1 || parm(0) == 0) {
cerr << get_fileline() << ": error: $clog2 takes a single "
"argument." << endl;
return 0;
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
if (tmpi || tmpr) {
double arg;
if (tmpi) {
arg = tmpi->value().as_double();
} else {
arg = tmpr->value().as_double();
}
NetExpr*rtn = evaluate_clog2(parm(0));
if (rtn != 0) {
rtn->set_line(*this);
if (debug_eval_tree) {
cerr << get_fileline() << ": debug: Evaluate "
"constant $clog2()." << endl;
}
return rtn;
if (strcmp(name, "$ln") == 0) {
return new NetECReal(verireal(log(arg)));
} else if (strcmp(name, "$log") == 0) {
return new NetECReal(verireal(log10(arg)));
} else if (strcmp(name, "$log10") == 0) {
return new NetECReal(verireal(log10(arg)));
} else if (strcmp(name, "$exp") == 0) {
return new NetECReal(verireal(exp(arg)));
} else if (strcmp(name, "$sqrt") == 0) {
return new NetECReal(verireal(sqrt(arg)));
} else if (strcmp(name, "$floor") == 0) {
return new NetECReal(verireal(floor(arg)));
} else if (strcmp(name, "$ceil") == 0) {
return new NetECReal(verireal(ceil(arg)));
} else if (strcmp(name, "$sin") == 0) {
return new NetECReal(verireal(sin(arg)));
} else if (strcmp(name, "$cos") == 0) {
return new NetECReal(verireal(cos(arg)));
} else if (strcmp(name, "$tan") == 0) {
return new NetECReal(verireal(tan(arg)));
} else if (strcmp(name, "$asin") == 0) {
return new NetECReal(verireal(asin(arg)));
} else if (strcmp(name, "$acos") == 0) {
return new NetECReal(verireal(acos(arg)));
} else if (strcmp(name, "$atan") == 0) {
return new NetECReal(verireal(atan(arg)));
} else if (strcmp(name, "$sinh") == 0) {
return new NetECReal(verireal(sinh(arg)));
} else if (strcmp(name, "$cosh") == 0) {
return new NetECReal(verireal(cosh(arg)));
} else if (strcmp(name, "$tanh") == 0) {
return new NetECReal(verireal(tanh(arg)));
} else if (strcmp(name, "$asinh") == 0) {
return new NetECReal(verireal(asinh(arg)));
} else if (strcmp(name, "$acosh") == 0) {
return new NetECReal(verireal(acosh(arg)));
} else if (strcmp(name, "$atanh") == 0) {
return new NetECReal(verireal(atanh(arg)));
}
}
return 0;
}
NetExpr* evaluate_math_two_args(NetExpr*arg0, NetExpr*arg1, const char*name)
{
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
NetECReal*tmpr1 = dynamic_cast<NetECReal *>(arg1);
if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) {
double arg0, arg1;
if (tmpi0) {
arg0 = tmpi0->value().as_double();
} else {
arg0 = tmpr0->value().as_double();
}
if (tmpi1) {
arg1 = tmpi1->value().as_double();
} else {
arg1 = tmpr1->value().as_double();
}
if (strcmp(name, "$pow") == 0) {
return new NetECReal(verireal(pow(arg0, arg1)));
} else if (strcmp(name, "$atan2") == 0) {
return new NetECReal(verireal(atan2(arg0, arg1)));
} else if (strcmp(name, "$hypot") == 0) {
return new NetECReal(verireal(hypot(arg0, arg1)));
}
}
return 0;
}
NetExpr* evaluate_abs(NetExpr*arg)
{
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
if (tmpi) {
verinum arg = tmpi->value();
if (arg.has_sign()) {
arg = v_not(arg) + verinum(1);
}
return new NetEConst(arg);
}
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
if (tmpr) {
double arg = tmpr->value().as_double();
return new NetECReal(verireal(fabs(arg)));
}
return 0;
}
NetExpr* evaluate_min_max(NetExpr*arg0, NetExpr*arg1, const char*name)
{
NetEConst*tmpi0 = dynamic_cast<NetEConst *>(arg0);
NetECReal*tmpr0 = dynamic_cast<NetECReal *>(arg0);
NetEConst*tmpi1 = dynamic_cast<NetEConst *>(arg1);
NetECReal*tmpr1 = dynamic_cast<NetECReal *>(arg1);
if (tmpi0 && tmpi1) {
verinum arg0 = tmpi0->value();
verinum arg1 = tmpi1->value();
if (strcmp(name, "$min") == 0) {
return new NetEConst( arg0 < arg1 ? arg0 : arg1);
} else if (strcmp(name, "$max") == 0) {
return new NetEConst( arg0 < arg1 ? arg1 : arg0);
}
}
if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) {
double arg0, arg1;
if (tmpi0) {
arg0 = tmpi0->value().as_double();
} else {
arg0 = tmpr0->value().as_double();
}
if (tmpi1) {
arg1 = tmpi1->value().as_double();
} else {
arg1 = tmpr1->value().as_double();
}
if (strcmp(name, "$min") == 0) {
return new NetECReal(verireal(arg0 < arg1 ? arg0 : arg1));
} else if (strcmp(name, "$max") == 0) {
return new NetECReal(verireal(arg0 < arg1 ? arg1 : arg0));
}
}
return 0;
}
NetExpr* NetESFunc::eval_tree(int prune_to_width)
{
/* 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) {
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;
}
if (strcmp(nm, "$clog2") == 0) {
rtn = evaluate_clog2(parm(0));
} else {
rtn = evaluate_math_one_arg(parm(0), nm);
}
}
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;
}
rtn = evaluate_math_two_args(parm(0), parm(1), nm);
}
if ((gn_icarus_misc_flag || gn_verilog_ams_flag) &&
(strcmp(nm, "$log") == 0 || strcmp(nm, "$abs") == 0)) {
if (nparms() != 1 || parm(0) == 0) {
cerr << get_fileline() << ": error: " << nm
<< " takes a single argument." << endl;
return 0;
}
if (strcmp(nm, "$log") == 0) {
rtn = evaluate_math_one_arg(parm(0), nm);
} else {
rtn = evaluate_abs(parm(0));
}
}
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;
}
rtn = evaluate_min_max(parm(0), parm(1), nm);
}
if (rtn != 0) {
rtn->set_line(*this);
if (debug_eval_tree) {
cerr << get_fileline() << ": debug: Evaluate constant "
<< nm << "." << endl;
}
}
return rtn;
}

View File

@ -3219,6 +3219,7 @@ class NetEBComp : public NetEBinary {
NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag);
NetEConst*eval_eqeq_(bool ne_flag);
NetEConst*eval_eqeq_real_(NetExpr*le, NetExpr*ri, bool ne_flag);
NetEConst*eval_less_();
NetEConst*eval_leeq_();
NetEConst*eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag);

View File

@ -217,13 +217,19 @@ static int draw_realnum_real(ivl_expr_t exp)
/* Handle the special case that the value is +-inf. */
if (isinf(value)) {
if (value > 0)
fprintf(vvp_out, " %%loadi/wr %d, 0, %d; load=+inf\n",
fprintf(vvp_out, " %%loadi/wr %d, 0, %d; load=+inf\n",
res, 0x3fff);
else
fprintf(vvp_out, " %%loadi/wr %d, 0, %d; load=-inf\n",
fprintf(vvp_out, " %%loadi/wr %d, 0, %d; load=-inf\n",
res, 0x7fff);
return res;
}
/* Handle the special case that the value is NaN. */
if (value != value) {
fprintf(vvp_out, " %%loadi/wr %d, 1, %d; load=NaN\n",
res, 0x3fff);
return res;
}
if (value < 0) {
sign = 0x4000;

View File

@ -330,6 +330,10 @@ char* draw_Cr_to_string(double value)
snprintf(tmp, sizeof(tmp), "Cr<m0g7fff>");
return strdup(tmp);
}
if (value != value) {
snprintf(tmp, sizeof(tmp), "Cr<m1g3fff>");
return strdup(tmp);
}
int sign = 0;
if (value < 0) {

View File

@ -43,7 +43,7 @@ CPPFLAGS = @ident_support@ -I. -I$(srcdir)/.. -I$(srcdir) -I.. @file64_support@
CFLAGS = -Wall @CFLAGS@
LDFLAGS = @LDFLAGS@
all: dep system.vpi va_math.vpi $(ALL32)
all: dep system.vpi va_math.vpi v2005_math.vpi $(ALL32)
check: all
@ -60,7 +60,7 @@ sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \
sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
sys_clog2.o vams_simparam.o
vams_simparam.o
ifeq (@HAVE_LIBZ@,yes)
ifeq (@HAVE_LIBBZ2@,yes)
@ -69,6 +69,9 @@ endif
O += sys_lxt2.o lxt2_write.o
endif
# Object files for v2005_math.vpi
M = sys_clog2.o v2005_math.o
# Object files for va_math.vpi
V = va_math.o
@ -94,12 +97,16 @@ sdf_lexor.c: sdf_lexor.lex
sdf_parse.c sdf_parse.h: $(srcdir)/sdf_parse.y
$(YACC) --verbose -d -p sdf -o sdf_parse.c $(srcdir)/sdf_parse.y
v2005_math.vpi: $M ../vvp/libvpi.a
$(CC) @shared@ -o $@ $M -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS)
va_math.vpi: $V ../vvp/libvpi.a
$(CC) @shared@ -o $@ $V -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS)
clean:
rm -rf *.o sys_readmem_lex.c dep system.vpi va_math.vpi bin32
rm -rf *.o sys_readmem_lex.c dep system.vpi bin32
rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h
rm -f va_math.vpi v2005_math.vpi
distclean: clean
rm -f Makefile config.status config.log vpi_config.h
@ -109,6 +116,7 @@ check: all
install: all installdirs \
$(vpidir)/system.vpi $(libdir)/ivl/system.sft \
$(vpidir)/va_math.vpi $(libdir)/ivl/va_math.sft \
$(vpidir)/v2005_math.vpi $(libdir)/ivl/v2005_math.sft \
$(vpidir)/include/
$(vpidir)/system.vpi: ./system.vpi
@ -123,6 +131,12 @@ $(vpidir)/va_math.vpi: ./va_math.vpi
$(libdir)/ivl/va_math.sft: va_math.sft
$(INSTALL_DATA) $< $@
$(vpidir)/v2005_math.vpi: ./v2005_math.vpi
$(INSTALL_PROGRAM) ./v2005_math.vpi $(vpidir)/v2005_math.vpi
$(libdir)/ivl/v2005_math.sft: v2005_math.sft
$(INSTALL_DATA) $< $@
installdirs: ../mkinstalldirs
$(srcdir)/../mkinstalldirs $(vpidir)
@ -131,6 +145,7 @@ uninstall:
rm -f $(libdir)/ivl/system.sft
rm -f $(vpidir)/va_math.vpi
rm -f $(libdir)/ivl/va_math.sft
rm -f $(vpidir)/v2005_math.vpi
rm -f $(libdir)/ivl/v2005_math.sft
-include $(patsubst %.o, dep/%.d, $O)

View File

@ -20,7 +20,39 @@
#include <math.h>
#include <string.h>
#include <vpi_user.h>
#include "sys_priv.h"
/*
* This routine returns 1 if the argument supports has a numeric value,
* otherwise it returns 0.
*
* This is copied from sys_priv.c.
*/
static unsigned is_numeric_obj(vpiHandle obj)
{
assert(obj);
unsigned rtn = 0;
switch(vpi_get(vpiType, obj)) {
case vpiConstant:
case vpiParameter:
/* These cannot be a string constant. */
if (vpi_get(vpiConstType, obj) != vpiStringConst) rtn = 1;
break;
/* These can have a valid numeric value. */
case vpiIntegerVar:
case vpiMemoryWord:
case vpiNet:
case vpiPartSelect:
case vpiRealVar:
case vpiReg:
case vpiTimeVar:
rtn = 1;;
break;
}
return rtn;
}
/*
* Check that the function is called with the correct argument.

View File

@ -35,6 +35,9 @@ PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time)
/*
* This routine returns 1 if the argument is a constant value,
* otherwise it returns 0.
*
* This routine was also copied to sys_clog2.c since it is not
* part of the standard system functions.
*/
unsigned is_constant_obj(vpiHandle obj)
{

View File

@ -38,7 +38,6 @@ extern void sys_time_register();
extern void sys_vcd_register();
extern void sys_vcdoff_register();
extern void sys_special_register();
extern void sys_clog2_register();
extern void vams_simparam_register();
#ifdef HAVE_LIBZ
@ -182,7 +181,6 @@ void (*vlog_startup_routines[])() = {
sys_lxt_or_vcd_register,
sys_sdf_register,
sys_special_register,
sys_clog2_register,
vams_simparam_register,
0
};

326
vpi/v2005_math.c Normal file
View File

@ -0,0 +1,326 @@
/*
* Verilog-2005 math library for Icarus Verilog
* http://www.icarus.com/eda/verilog/
*
* Copyright (C) 2007-2008 Cary R. (cygcary@yahoo.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vpi_config.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <vpi_user.h>
/* Single argument functions. */
typedef struct s_single_data {
const char *name;
double (*func)(double);
} t_single_data;
static t_single_data va_single_data[]= {
{"$sqrt", sqrt},
{"$ln", log},
{"$log10", log10},
{"$exp", exp},
{"$ceil", ceil},
{"$floor", floor},
{"$sin", sin},
{"$cos", cos},
{"$tan", tan},
{"$asin", asin},
{"$acos", acos},
{"$atan", atan},
{"$sinh", sinh},
{"$cosh", cosh},
{"$tanh", tanh},
{"$asinh", asinh},
{"$acosh", acosh},
{"$atanh", atanh},
{0, 0} /* Must be NULL terminated! */
};
/* Double argument functions. */
typedef struct s_double_data {
const char *name;
double (*func)(double, double);
} t_double_data;
static t_double_data va_double_data[]= {
{"$pow", pow},
{"$atan2", atan2},
{"$hypot", hypot},
{0, 0} /* Must be NULL terminated! */
};
/*
* This structure holds the single argument information.
*/
typedef struct {
vpiHandle arg;
double (*func)(double);
} va_single_t;
/*
* This structure holds the double argument information.
*/
typedef struct {
vpiHandle arg1;
vpiHandle arg2;
double (*func)(double, double);
} va_double_t;
/*
* Standard error message routine. The format string must take one
* string argument (the name of the function).
*/
static void va_error_message(vpiHandle callh, const char *format,
const char *name) {
vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf(format, name);
vpi_control(vpiFinish, 1);
}
/*
* Process an argument.
*/
vpiHandle va_process_argument(vpiHandle callh, const char *name,
vpiHandle arg, const char *post) {
PLI_INT32 type;
if (arg == NULL) return 0;
type = vpi_get(vpiType, arg);
/* Math function cannot do anything with a string. */
if ((type == vpiConstant || type == vpiParameter) &&
(vpi_get(vpiConstType, arg) == vpiStringConst)) {
const char* basemsg = "%s cannot process strings";
char* msg = malloc(strlen(basemsg)+strlen(post)+3);
strcpy(msg, basemsg);
strcat(msg, post);
strcat(msg, ".\n");
va_error_message(callh, msg, name);
free(msg);
return 0;
}
return arg;
}
/*
* Routine to check all the single argument math functions.
*/
static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8 *ud)
{
assert(ud != 0);
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
assert(callh != 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
t_single_data *data = (t_single_data *) ud;
const char *name = data->name;
va_single_t* fun_data = malloc(sizeof(va_single_t));
/* Check that malloc gave use some memory. */
if (fun_data == 0) {
va_error_message(callh, "%s failed to allocate memory.\n", name);
return 0;
}
/* Check that there are arguments. */
if (argv == 0) {
va_error_message(callh, "%s requires one argument.\n", name);
return 0;
}
/* In Icarus if we have an argv we have at least one argument. */
arg = vpi_scan(argv);
fun_data->arg = va_process_argument(callh, name, arg, "");
/* These functions only take one argument. */
arg = vpi_scan(argv);
if (arg != 0) {
va_error_message(callh, "%s takes only one argument.\n", name);
}
/* Get the function that is to be used by the calltf routine. */
fun_data->func = data->func;
vpi_put_userdata(callh, fun_data);
/* vpi_scan() returning 0 (NULL) has already freed argv. */
return 0;
}
/*
* Routine to implement the single argument math functions.
*/
static PLI_INT32 va_single_argument_calltf(PLI_BYTE8 *ud)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
s_vpi_value val;
(void) ud; /* Not used! */
/* Retrieve the function and argument data. */
va_single_t* fun_data = vpi_get_userdata(callh);
/* Calculate the result */
val.format = vpiRealVal;
vpi_get_value(fun_data->arg, &val);
val.value.real = (fun_data->func)(val.value.real);
/* Return the result */
vpi_put_value(callh, &val, 0, vpiNoDelay);
return 0;
}
/*
* Routine to check all the double argument math functions.
*/
static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8 *ud)
{
assert(ud != 0);
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
assert(callh != 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
t_double_data *data = (t_double_data *) ud;
const char *name = data->name;
va_double_t* fun_data = malloc(sizeof(va_double_t));
/* Check that malloc gave use some memory. */
if (fun_data == 0) {
va_error_message(callh, "%s failed to allocate memory.\n", name);
return 0;
}
/* Check that there are arguments. */
if (argv == 0) {
va_error_message(callh, "%s requires two arguments.\n", name);
return 0;
}
/* In Icarus if we have an argv we have at least one argument. */
arg = vpi_scan(argv);
fun_data->arg1 = va_process_argument(callh, name, arg, " (arg1)");
/* Check that there are at least two arguments. */
arg = vpi_scan(argv);
if (arg == 0) {
va_error_message(callh, "%s requires two arguments.\n", name);
}
fun_data->arg2 = va_process_argument(callh, name, arg, " (arg2)");
/* These functions only take two arguments. */
arg = vpi_scan(argv);
if (arg != 0) {
va_error_message(callh, "%s takes only two arguments.\n", name);
}
/* Get the function that is to be used by the calltf routine. */
fun_data->func = data->func;
vpi_put_userdata(callh, fun_data);
/* vpi_scan() returning 0 (NULL) has already freed argv. */
return 0;
}
/*
* Routine to implement the double argument math functions.
*/
static PLI_INT32 va_double_argument_calltf(PLI_BYTE8 *ud)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
s_vpi_value val;
double first_arg;
(void) ud; /* Not used! */
/* Retrieve the function and argument data. */
va_double_t* fun_data = vpi_get_userdata(callh);
/* Calculate the result */
val.format = vpiRealVal;
vpi_get_value(fun_data->arg1, &val);
first_arg = val.value.real;
vpi_get_value(fun_data->arg2, &val);
val.value.real = (fun_data->func)(first_arg, val.value.real);
/* Return the result */
vpi_put_value(callh, &val, 0, vpiNoDelay);
return 0;
}
/*
* Register all the functions with Verilog.
*/
static void sys_v2005_math_register(void)
{
s_vpi_systf_data tf_data;
unsigned idx;
/* Register the single argument functions. */
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiRealFunc;
tf_data.calltf = va_single_argument_calltf;
tf_data.compiletf = va_single_argument_compiletf;
tf_data.sizetf = 0;
for (idx=0; va_single_data[idx].name != 0; idx++) {
tf_data.tfname = va_single_data[idx].name;
tf_data.user_data = (PLI_BYTE8 *) &va_single_data[idx];
vpi_register_systf(&tf_data);
}
/* Register the double argument functions. */
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiRealFunc;
tf_data.calltf = va_double_argument_calltf;
tf_data.compiletf = va_double_argument_compiletf;
tf_data.sizetf = 0;
for (idx=0; va_double_data[idx].name != 0; idx++) {
tf_data.tfname = va_double_data[idx].name;
tf_data.user_data = (PLI_BYTE8 *) &va_double_data[idx];
vpi_register_systf(&tf_data);
}
}
/*
* Hook to get Icarus Verilog to find the registration function.
*/
extern void sys_clog2_register();
void (*vlog_startup_routines[])(void) = {
sys_v2005_math_register,
sys_clog2_register,
0
};

28
vpi/v2005_math.sft Normal file
View File

@ -0,0 +1,28 @@
#
# This is the system function descriptor table for the
# Verilog-2005 math functions.
#
# Single argument functions.
$sqrt vpiSysFuncReal
$ln vpiSysFuncReal
$log10 vpiSysFuncReal
$exp vpiSysFuncReal
$ceil vpiSysFuncReal
$floor vpiSysFuncReal
$sin vpiSysFuncReal
$cos vpiSysFuncReal
$tan vpiSysFuncReal
$asin vpiSysFuncReal
$acos vpiSysFuncReal
$atan vpiSysFuncReal
$sinh vpiSysFuncReal
$cosh vpiSysFuncReal
$tanh vpiSysFuncReal
$asinh vpiSysFuncReal
$acosh vpiSysFuncReal
$atanh vpiSysFuncReal
# Double argument functions.
$pow vpiSysFuncReal
$atan2 vpiSysFuncReal
$hypot vpiSysFuncReal

View File

@ -32,6 +32,10 @@
* The functions fmax() and fmin() may not be available in pre-c99
* libraries, so if they are not available you need to uncomment the
* -DUSE_MY_FMAX_AND_FMIN in the Makefile.
*
* Most of the math functions have been moved to v2005_math. This
* allows them to be supported separately from the Verilog-A
* specific functions.
*/
@ -66,30 +70,12 @@ typedef struct s_single_data {
} t_single_data;
static t_single_data va_single_data[]= {
{"$sqrt", sqrt},
{"$ln", log},
{"$log", log10}, /* NOTE: The $log function is replaced by the
$log10 function to eliminate confusion with
the natural log. In C, "log" is ln and log10
is log-base-10. The $log is being left in for
compatibility. */
{"$log10", log10},
{"$exp", exp},
{"$abs", fabs},
{"$ceil", ceil},
{"$floor", floor},
{"$sin", sin},
{"$cos", cos},
{"$tan", tan},
{"$asin", asin},
{"$acos", acos},
{"$atan", atan},
{"$sinh", sinh},
{"$cosh", cosh},
{"$tanh", tanh},
{"$asinh", asinh},
{"$acosh", acosh},
{"$atanh", atanh},
{0, 0} /* Must be NULL terminated! */
};
@ -111,9 +97,6 @@ static t_double_data va_double_data[]= {
#else
{"$min", va_fmin},
#endif
{"$pow", pow},
{"$atan2", atan2},
{"$hypot", hypot},
{0, 0} /* Must be NULL terminated! */
};

View File

@ -4,29 +4,8 @@
#
# Single argument functions.
$sqrt vpiSysFuncReal
$ln vpiSysFuncReal
$log vpiSysFuncReal
$log10 vpiSysFuncReal
$exp vpiSysFuncReal
$abs vpiSysFuncReal
$ceil vpiSysFuncReal
$floor vpiSysFuncReal
$sin vpiSysFuncReal
$cos vpiSysFuncReal
$tan vpiSysFuncReal
$asin vpiSysFuncReal
$acos vpiSysFuncReal
$atan vpiSysFuncReal
$sinh vpiSysFuncReal
$cosh vpiSysFuncReal
$tanh vpiSysFuncReal
$asinh vpiSysFuncReal
$acosh vpiSysFuncReal
$atanh vpiSysFuncReal
# Double argument functions.
$min vpiSysFuncReal
$max vpiSysFuncReal
$pow vpiSysFuncReal
$atan2 vpiSysFuncReal
$hypot vpiSysFuncReal

View File

@ -492,7 +492,7 @@ is removed from the <exp> before calculating the real value.
If <exp>==0x3fff and <mant> == 0, the value is +inf.
If <exp>==0x7fff and <mant> == 0, the value is -inf.
If <exp>--0x3fff and <mant> != 0, the value is NaN.
If <exp>==0x3fff and <mant> != 0, the value is NaN.
* %mod <bit-l>, <bit-r>, <wid>
* %mod/s <bit-l>, <bit-r>, <wid>

View File

@ -2658,8 +2658,9 @@ bool of_LOADI_WR(vthread_t thr, vvp_code_t cp)
return true;
}
// Detect NaN
if ( (exp&0x3fff) == 0x3fff ) {
if (exp==0x3fff) {
thr->words[idx].w_real = nan("");
return true;
}
double sign = (exp & 0x4000)? -1.0 : 1.0;