diff --git a/PDelays.cc b/PDelays.cc index 9201079ec..c547a2c08 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -24,6 +24,7 @@ # include "PDelays.h" # include "PExpr.h" # include "verinum.h" +# include "netmisc.h" PDelays::PDelays() { @@ -63,10 +64,7 @@ static NetExpr*calculate_val(Design*des, NetScope*scope, const PExpr*expr) { NetExpr*dex = expr->elaborate_expr(des, scope, -1, false); - if (NetExpr*tmp = dex->eval_tree()) { - delete dex; - dex = tmp; - } + eval_expr(dex); /* If the delay expression is a real constant or vector constant, then evaluate it, scale it to the local time @@ -163,63 +161,3 @@ void PDelays::eval_delays(Design*des, NetScope*scope, decay_time = 0; } } - -/* - * $Log: PDelays.cc,v $ - * Revision 1.15 2006/07/08 21:48:46 steve - * Handle real valued literals in net contexts. - * - * Revision 1.14 2006/06/02 04:48:49 steve - * Make elaborate_expr methods aware of the width that the context - * requires of it. In the process, fix sizing of the width of unary - * minus is context determined sizes. - * - * Revision 1.13 2006/01/03 05:22:14 steve - * Handle complex net node delays. - * - * Revision 1.12 2006/01/02 05:33:19 steve - * Node delays can be more general expressions in structural contexts. - * - * Revision 1.11 2003/06/21 01:21:42 steve - * Harmless fixup of warnings. - * - * Revision 1.10 2003/02/08 19:49:21 steve - * Calculate delay statement delays using elaborated - * expressions instead of pre-elaborated expression - * trees. - * - * Remove the eval_pexpr methods from PExpr. - * - * Revision 1.9 2002/08/12 01:34:58 steve - * conditional ident string using autoconfig. - * - * Revision 1.8 2001/12/29 20:19:31 steve - * Do not delete delay expressions of UDP instances. - * - * Revision 1.7 2001/11/22 06:20:59 steve - * Use NetScope instead of string for scope path. - * - * Revision 1.6 2001/11/07 04:01:59 steve - * eval_const uses scope instead of a string path. - * - * Revision 1.5 2001/07/25 03:10:48 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.4 2001/01/20 02:15:50 steve - * apologize for not supporting non-constant delays. - * - * Revision 1.3 2001/01/14 23:04:55 steve - * Generalize the evaluation of floating point delays, and - * get it working with delay assignment statements. - * - * Allow parameters to be referenced by hierarchical name. - * - * Revision 1.2 2000/02/23 02:56:53 steve - * Macintosh compilers do not support ident. - * - * Revision 1.1 1999/09/04 19:11:46 steve - * Add support for delayed non-blocking assignments. - * - */ - diff --git a/elab_expr.cc b/elab_expr.cc index fa46fca0e..a7327475d 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -135,19 +135,8 @@ NetEBinary* PEBinary::elaborate_eval_expr_base_(Design*des, { /* If either expression can be evaluated ahead of time, then do so. This can prove helpful later. */ - { NetExpr*tmp; - tmp = lp->eval_tree(); - if (tmp) { - delete lp; - lp = tmp; - } - - tmp = rp->eval_tree(); - if (tmp) { - delete rp; - rp = tmp; - } - } + eval_expr(lp); + eval_expr(rp); return elaborate_expr_base_(des, lp, rp, expr_wid); } @@ -533,12 +522,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w PExpr*expr = parms_[idx]; if (expr) { NetExpr*tmp1 = expr->elaborate_expr(des, scope, -1, true); - if (NetExpr*tmp2 = tmp1->eval_tree()) { - delete tmp1; - fun->parm(idx, tmp2); - } else { - fun->parm(idx, tmp1); - } + eval_expr(tmp1); + fun->parm(idx, tmp1); } else { missing_parms += 1; @@ -1147,13 +1132,7 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des, select attached to it. Generate a NetESelect object to select the bit as desired. */ NetExpr*mtmp = index_tail.msb->elaborate_expr(des, scope, -1,false); - if (! dynamic_cast(mtmp)) { - NetExpr*re = mtmp->eval_tree(); - if (re) { - delete mtmp; - mtmp = re; - } - } + eval_expr(mtmp); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -1305,9 +1284,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, if (long base = net->array_first()) { word_index = make_add_expr(word_index, 0-base); - if (NetExpr*tmp = word_index->eval_tree()) { - word_index = tmp; - } + eval_expr(word_index); } } @@ -1683,6 +1660,12 @@ static bool test_ternary_operand_compat(ivl_variable_type_t l, return true; if (l == IVL_VT_BOOL && r == IVL_VT_LOGIC) return true; + + if (l == IVL_VT_REAL && (r == IVL_VT_LOGIC || r == IVL_VT_BOOL)) + return true; + if (r == IVL_VT_REAL && (l == IVL_VT_LOGIC || l == IVL_VT_BOOL)) + return true; + if (l == r) return true; diff --git a/elab_lval.cc b/elab_lval.cc index 18b7a5ccd..14402801b 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -333,9 +333,7 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, if (long base = reg->array_first()) { word = make_add_expr(word, 0-base); - if (NetExpr*tmp = word->eval_tree()) { - word = tmp; - } + eval_expr(word); } NetAssign_*lv = new NetAssign_(reg); @@ -485,4 +483,3 @@ NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool) const des->errors += 1; return 0; } - diff --git a/elab_net.cc b/elab_net.cc index a28faf698..bd3c0357a 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -1713,20 +1713,14 @@ NetNet* PEIdent::elaborate_net_bitmux_(Design*des, NetScope*scope, if (sig->msb() < sig->lsb()) { NetExpr*sel_expr = index_tail.msb->elaborate_expr(des, scope, -1, false); sel_expr = make_sub_expr(sig->lsb(), sel_expr); - if (NetExpr*tmp = sel_expr->eval_tree()) { - delete sel_expr; - sel_expr = tmp; - } + eval_expr(sel_expr); sel = sel_expr->synthesize(des); } else if (sig->lsb() != 0) { NetExpr*sel_expr = index_tail.msb->elaborate_expr(des, scope, -1,false); sel_expr = make_add_expr(sel_expr, - sig->lsb()); - if (NetExpr*tmp = sel_expr->eval_tree()) { - delete sel_expr; - sel_expr = tmp; - } + eval_expr(sel_expr); sel = sel_expr->synthesize(des); @@ -2935,9 +2929,31 @@ NetNet* PETernary::elaborate_net(Design*des, NetScope*scope, Link::strength_t drive0, Link::strength_t drive1) const { - NetNet*expr_sig = expr_->elaborate_net(des, scope, 0, 0, 0, 0), - *tru_sig = tru_->elaborate_net(des, scope, width, 0, 0, 0), - *fal_sig = fal_->elaborate_net(des, scope, width, 0, 0, 0); + NetNet *expr_sig, *tru_sig, *fal_sig; + + NetExpr*expr = elab_and_eval(des, scope, expr_, 0); + if (expr == 0) return 0; + + /* If we have a constant conditional we can avoid some steps. */ + switch (const_logical(expr)) { + case C_0: + fal_sig = fal_->elaborate_net(des, scope, width, 0, 0, 0); + if (fal_sig == 0) return 0; +tru_sig = tru_->elaborate_net(des, scope, width, 0, 0, 0); + break; + + case C_1: + tru_sig = tru_->elaborate_net(des, scope, width, 0, 0, 0); + if (tru_sig == 0) return 0; +fal_sig = fal_->elaborate_net(des, scope, width, 0, 0, 0); + break; + + default: + tru_sig = tru_->elaborate_net(des, scope, width, 0, 0, 0); + fal_sig = fal_->elaborate_net(des, scope, width, 0, 0, 0); + break; + } + expr_sig = expr->synthesize(des); if (expr_sig == 0 || tru_sig == 0 || fal_sig == 0) return 0; @@ -2970,14 +2986,11 @@ NetNet* PETernary::elaborate_net(Design*des, NetScope*scope, but if we do not get a size from the context, or the expressions resist, we need to cope. */ unsigned iwidth = tru_sig->vector_width(); - if (fal_sig->vector_width() > iwidth) - iwidth = fal_sig->vector_width(); - + if (fal_sig->vector_width() > iwidth) iwidth = fal_sig->vector_width(); /* If the width is not passed from the context, then take the widest result as our width. */ - if (width == 0) - width = iwidth; + if (width == 0) width = iwidth; /* If the expression has width, then generate a boolean result by connecting an OR gate to calculate the truth value of diff --git a/elab_pexpr.cc b/elab_pexpr.cc index cfd31d9a1..53fdb1408 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -78,6 +78,7 @@ NetEConcat* PEConcat::elaborate_pexpr(Design*des, NetScope*scope) const "concatenation repeat expression cannot be evaluated." << endl; des->errors += 1; + return 0; } /* continue on even if the repeat expression doesn't @@ -108,7 +109,8 @@ NetEConcat* PEConcat::elaborate_pexpr(Design*des, NetScope*scope) const << "concatenation has indefinite width: " << *ex << endl; des->errors += 1; - + delete tmp; + return 0; } tmp->set(idx, ex); @@ -173,22 +175,26 @@ NetExpr*PEIdent::elaborate_pexpr(Design*des, NetScope*scope) const switch (use_sel) { case index_component_t::SEL_NONE: break; + default: case index_component_t::SEL_PART: cerr << get_fileline() << ": sorry: Cannot part select " - "bits of parameters." << endl; + "bits of parameters." << endl; des->errors += 1; - break; + delete res; + return 0; case index_component_t::SEL_BIT: /* We have here a bit select. Insert a NetESelect node to handle it. */ NetExpr*tmp = name_tail.index.back().msb->elaborate_pexpr(des, scope); - if (tmp != 0) { - res = new NetESelect(res, tmp, 1); - res->set_line(*this); + if (tmp == 0) { + delete res; + return 0; } + res = new NetESelect(res, tmp, 1); + res->set_line(*this); break; } @@ -214,9 +220,7 @@ NetETernary* PETernary::elaborate_pexpr(Design*des, NetScope*scope) const NetExpr*c = expr_->elaborate_pexpr(des, scope); NetExpr*t = tru_->elaborate_pexpr(des, scope); NetExpr*f = fal_->elaborate_pexpr(des, scope); - if (c == 0) return 0; - if (t == 0) return 0; - if (f == 0) return 0; + if (c == 0 || t == 0 || f == 0) return 0; NetETernary*tmp = new NetETernary(c, t, f); tmp->set_line(*this); @@ -238,10 +242,12 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const tmp = new NetEUnary(op_, ip); tmp->set_line(*this); break; + case '~': tmp = new NetEUBits(op_, ip); tmp->set_line(*this); break; + case '!': // Logical NOT case '&': // Reduction AND case '|': // Reduction OR @@ -253,5 +259,6 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const tmp->set_line(*this); break; } + return tmp; } diff --git a/elab_scope.cc b/elab_scope.cc index 149a87bba..c745e819e 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,6 +51,7 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur, assert(ex); NetExpr*val = ex->elaborate_pexpr(des, scope); + if (val == 0) return; NetExpr*msb = 0; NetExpr*lsb = 0; bool signed_flag = cur.signed_flag; diff --git a/elaborate.cc b/elaborate.cc index 49c72f683..b4b0a4f68 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2123,12 +2123,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const /* Attempt to pre-evaluate the parameters. It may be possible to at least partially reduce the expression. */ - if (eparms[idx]) { - if (NetExpr*tmp = eparms[idx]->eval_tree()) { - delete eparms[idx]; - eparms[idx] = tmp; - } - } + if (eparms[idx]) eval_expr(eparms[idx]); } NetSTask*cur = new NetSTask(peek_tail_name(path_), eparms); @@ -2725,10 +2720,7 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, } /* precalculate as much as possible of the wait expression. */ - if (NetExpr*tmp = expr->eval_tree()) { - delete expr; - expr = tmp; - } + eval_expr(expr); /* Detect the unusual case that the wait expression is constant. Constant true is OK (it becomes transparent) but @@ -2771,11 +2763,7 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, wait. */ assert(expr->expr_width() == 1); expr = new NetEBComp('N', expr, new NetEConst(verinum(verinum::V1))); - NetExpr*tmp = expr->eval_tree(); - if (tmp) { - delete expr; - expr = tmp; - } + eval_expr(expr); NetEvent*wait_event = new NetEvent(scope->local_symbol()); scope->add_event(wait_event); @@ -3766,4 +3754,3 @@ Design* elaborate(listroots) return des; } - diff --git a/eval_tree.cc b/eval_tree.cc index 539eaa886..1dd138421 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -25,82 +25,51 @@ # include "netlist.h" # include "ivl_assert.h" +# include "netmisc.h" NetExpr* NetExpr::eval_tree(int prune_to_width) { return 0; } -/* - * Some of the derived classes can be evaluated by the compiler, this - * method provides the common aid of evaluating the parameter - * expressions. - */ -void NetEBinary::eval_sub_tree_() +static bool get_real_arg_(NetExpr*expr, verireal&val) { - NetExpr*tmp = left_->eval_tree(); - if (tmp) { - delete left_; - left_ = tmp; - } - tmp = right_->eval_tree(); - if (tmp){ - delete right_; - right_ = tmp; + switch (expr->expr_type()) { + case IVL_VT_REAL: { + NetECReal*c = dynamic_cast (expr); + if (c == 0) return false; + val = c->value(); + break; + } + + case IVL_VT_BOOL: + case IVL_VT_LOGIC: { + NetEConst*c = dynamic_cast(expr); + if (c == 0) return false; + verinum tmp = c->value(); + val = verireal(tmp.as_double()); + break; + } + + default: + assert(0); } + + return true; } bool NetEBinary::get_real_arguments_(verireal&lval, verireal&rval) { - switch (left_->expr_type()) { - case IVL_VT_REAL: { - NetECReal*lc = dynamic_cast (left_); - if (lc == 0) return false; - lval = lc->value(); - break; - } - - case IVL_VT_BOOL: - case IVL_VT_LOGIC: { - NetEConst*lc = dynamic_cast(left_); - if (lc == 0) return false; - verinum tmp = lc->value(); - lval = verireal(tmp.as_double()); - break; - } - - default: - assert(0); - } - - switch (right_->expr_type()) { - case IVL_VT_REAL: { - NetECReal*rc = dynamic_cast (right_); - if (rc == 0) return 0; - rval = rc->value(); - break; - } - - case IVL_VT_BOOL: - case IVL_VT_LOGIC: { - NetEConst*rc = dynamic_cast(right_); - if (rc == 0) return 0; - verinum tmp = rc->value(); - rval = verireal(tmp.as_double()); - break; - } - - default: - assert(0); - } - + if (!get_real_arg_(left_, lval)) return false; + if (!get_real_arg_(right_, rval)) return false; return true; } NetExpr* NetEBAdd::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_, prune_to_width); + eval_expr(right_, prune_to_width); if (left_->expr_type() == IVL_VT_REAL || right_->expr_type()==IVL_VT_REAL) return eval_tree_real_(); @@ -170,6 +139,7 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) NetEConst*tmp = new NetEConst(val); left_ = se->left_->dup_expr(); delete se; + tmp->set_line(*right_); delete right_; right_ = tmp; /* We've changed the subexpression, but the result is @@ -208,27 +178,26 @@ NetECReal* NetEBAdd::eval_tree_real_() NetEConst* NetEBBits::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); NetEConst*lc = dynamic_cast(left_); NetEConst*rc = dynamic_cast(right_); + if (lc == 0 || rc == 0) return 0; /* Notice the special case where one of the operands is 0 and this is a bitwise &. If this happens, then the result is known to be 0. */ - if ((op() == '&') && lc && (lc->value() == verinum(0))) { + if ((op() == '&') && (lc->value() == verinum(0))) { verinum res (verinum::V0, expr_width()); return new NetEConst(res); } - if ((op() == '&') && rc && (rc->value() == verinum(0))) { + if ((op() == '&') && (rc->value() == verinum(0))) { verinum res (verinum::V0, expr_width()); return new NetEConst(res); } - if (lc == 0) return 0; - if (rc == 0) return 0; - verinum lval = lc->value(); verinum rval = rc->value(); @@ -737,12 +706,14 @@ NetEConst* NetEBComp::eval_neeqeq_() res = new NetEConst(verinum(verinum::V0,1)); delete tmp; + res->set_line(*this); return res; } NetEConst* NetEBComp::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); switch (op_) { case 'E': // Case equality (===) @@ -780,7 +751,8 @@ NetEConst* NetEBComp::eval_tree(int prune_to_width) */ NetExpr* NetEBDiv::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); if (expr_type() == IVL_VT_REAL) { NetECReal*lc = dynamic_cast(left_); @@ -850,11 +822,12 @@ NetExpr* NetEBDiv::eval_tree(int prune_to_width) NetEConst* NetEBLogic::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); + NetEConst*lc = dynamic_cast(left_); - if (lc == 0) return 0; NetEConst*rc = dynamic_cast(right_); - if (rc == 0) return 0; + if (lc == 0 || rc == 0) return 0; verinum::V lv = verinum::V0; verinum::V rv = verinum::V0; @@ -931,7 +904,8 @@ NetExpr* NetEBMult::eval_tree_real_() NetExpr* NetEBMult::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -939,9 +913,8 @@ NetExpr* NetEBMult::eval_tree(int prune_to_width) assert(expr_type() == IVL_VT_LOGIC); NetEConst*lc = dynamic_cast(left_); - if (lc == 0) return 0; NetEConst*rc = dynamic_cast(right_); - if (rc == 0) return 0; + if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); @@ -964,7 +937,8 @@ NetExpr* NetEBPow::eval_tree_real_() NetExpr* NetEBPow::eval_tree(int prune_to_width) { - eval_sub_tree_(); + eval_expr(left_); + eval_expr(right_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -972,9 +946,8 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) assert(expr_type() == IVL_VT_LOGIC); NetEConst*lc = dynamic_cast(left_); - if (lc == 0) return 0; NetEConst*rc = dynamic_cast(right_); - if (rc == 0) return 0; + if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); verinum rval = rc->value(); @@ -988,14 +961,12 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) */ NetEConst* NetEBShift::eval_tree(int prune_to_width) { - eval_sub_tree_(); - NetEConst*re = dynamic_cast(right_); - if (re == 0) - return 0; + eval_expr(left_); + eval_expr(right_); NetEConst*le = dynamic_cast(left_); - if (le == 0) - return 0; + NetEConst*re = dynamic_cast(right_); + if (le == 0 || re == 0) return 0; NetEConst*res; @@ -1018,7 +989,7 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) if ((wid == 0) || ! lv.has_len()) { /* If the caller doesn't care what the width is, - then calcuate a width from the trimmed left + then calculate a width from the trimmed left expression, plus the shift. This avoids data loss. */ lv = trim_vnum(lv); @@ -1064,8 +1035,7 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) res = new NetEConst(nv); } else { - if (wid == 0) - wid = left_->expr_width(); + if (wid == 0) wid = left_->expr_width(); verinum nv (verinum::Vx, wid); res = new NetEConst(nv); @@ -1089,9 +1059,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) // Parameter not here? This is an error, but presumably // already caught and we are here just to catch more. - if (parms_[idx] == 0) - continue; - + if (parms_[idx] == 0) continue; // If this parameter is already a constant, all is well // so go on. @@ -1106,6 +1074,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) assert(parms_[idx]); NetExpr*expr = parms_[idx]->eval_tree(0); if (expr) { + expr->set_line(*parms_[idx]); delete parms_[idx]; parms_[idx] = expr; @@ -1126,8 +1095,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) } - if (local_errors > 0) - return 0; + if (local_errors > 0) return 0; // Handle the special case that the repeat expression is // zero. In this case, just return a 0 value with the expected @@ -1282,38 +1250,20 @@ NetExpr* NetEParam::eval_tree(int prune_to_width) NetEConst* NetESelect::eval_tree(int prune_to_width) { + eval_expr(expr_); NetEConst*expr = dynamic_cast(expr_); - if (expr == 0) { - NetExpr*tmp = expr_->eval_tree(); - if (tmp != 0) { - delete expr_; - expr_ = tmp; - } - - expr = dynamic_cast(expr_); - } long bval = 0; if (base_) { + eval_expr(base_); NetEConst*base = dynamic_cast(base_); - if (base == 0) { - NetExpr*tmp = base_->eval_tree(); - if (tmp != 0) { - delete base_; - base_ = tmp; - } - base = dynamic_cast(base_); - } - - if (base == 0) - return 0; + if (base == 0) return 0; bval = base->value().as_long(); } - if (expr == 0) - return 0; + if (expr == 0) return 0; verinum eval = expr->value(); verinum oval (verinum::V0, expr_width(), true); @@ -1348,6 +1298,19 @@ NetEConst* NetESelect::eval_tree(int prune_to_width) } +static void print_ternary_cond(NetExpr*expr) +{ + if (NetEConst*c = dynamic_cast(expr)) { + cerr << c->value() << endl; + return; + } + if (NetECReal*c = dynamic_cast(expr)) { + cerr << c->value() << endl; + return; + } + assert(0); +} + /* * A ternary expression evaluation is controlled by the condition * expression. If the condition evaluates to true or false, then @@ -1357,98 +1320,89 @@ NetEConst* NetESelect::eval_tree(int prune_to_width) */ NetExpr* NetETernary::eval_tree(int prune_to_width) { - NetExpr*tmp; - - assert(cond_); - if (0 == dynamic_cast(cond_)) { - tmp = cond_->eval_tree(); - if (tmp != 0) { - delete cond_; - cond_ = tmp; + eval_expr(cond_); + switch (const_logical(cond_)) { + case C_0: + eval_expr(false_val_); + if (debug_eval_tree) { + + cerr << get_fileline() << ": debug: Evaluate ternary with " + << "constant condition value: "; + print_ternary_cond(cond_); + cerr << get_fileline() << ": : Selecting false case: " + << *false_val_ << endl; } - } - assert(true_val_); - if (0 == dynamic_cast(true_val_)) { - tmp = true_val_->eval_tree(); - if (tmp != 0) { - delete true_val_; - true_val_ = tmp; + if (expr_type() == IVL_VT_REAL && + false_val_->expr_type() != IVL_VT_REAL) { + verireal f; + if (get_real_arg_(false_val_, f)) { + NetECReal*rc = new NetECReal(f); + rc->set_line(*this); + return rc; + } } - } - assert(false_val_); - if (0 == dynamic_cast(false_val_)) { - tmp = false_val_->eval_tree(); - if (tmp != 0) { - delete false_val_; - false_val_ = tmp; - } - } + return false_val_->dup_expr(); - - NetEConst*c = dynamic_cast(cond_); - if (c == 0) - return 0; - - /* Check the boolean value of the constant condition - expression. Note that the X case is handled explicitly, so - we must differentiate. */ - - verinum cond_value = c->value(); - bool true_flag = false; - bool x_flag = false; - - for (unsigned idx = 0 ; idx < cond_value.len() ; idx += 1) { - switch (cond_value.get(idx)) { - case verinum::V1: - true_flag = true; - break; - case verinum::V0: - break; - default: - x_flag = true; - } - } - - - /* If the condition is 1 or 0, return the true or false - expression. Try to evaluate the expression down as far as - we can. */ - - if (true_flag) { + case C_1: + eval_expr(true_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " - << "constant condition value: " << c->value() << endl; + << "constant condition value: "; + print_ternary_cond(cond_); cerr << get_fileline() << ": : Selecting true case: " << *true_val_ << endl; } - return true_val_->dup_expr(); - } - if (! x_flag) { - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluate ternary with " - << "constant condition value: " << c->value() << endl; - cerr << get_fileline() << ": : Selecting false case: " - << *true_val_ << endl; + if (expr_type() == IVL_VT_REAL && + true_val_->expr_type() != IVL_VT_REAL) { + verireal t; + if (get_real_arg_(true_val_, t)) { + NetECReal*rc = new NetECReal(t); + rc->set_line(*this); + return rc; + } } - return false_val_->dup_expr(); + + return true_val_->dup_expr(); + + case C_X: + break; + + default: + return 0; } /* Here we have a more complex case. We need to evaluate both expressions down to constants then compare the values to build up a constant result. */ + eval_expr(true_val_); + eval_expr(false_val_); + NetEConst*t = dynamic_cast(true_val_); - if (t == 0) - return 0; - - NetEConst*f = dynamic_cast(false_val_); - if (f == 0) - return 0; + if (t == 0 || f == 0) { + verireal tv, fv; + if (!get_real_arg_(true_val_, tv)) return 0; + if (!get_real_arg_(false_val_, fv)) return 0; + verireal val = verireal(0.0); + if (tv.as_double() == fv.as_double()) val = tv; + + if (debug_eval_tree) { + cerr << get_fileline() << ": debug: Evaluate ternary with " + << "constant condition value: "; + print_ternary_cond(cond_); + cerr << get_fileline() << ": : Blending real cases " + << "to get " << val << endl; + } + + NetECReal*rc = new NetECReal(val); + rc->set_line(*this); + return rc; + } unsigned tsize = t->expr_width(); unsigned fsize = f->expr_width(); @@ -1458,17 +1412,16 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) verinum val (verinum::V0, rsize); for (unsigned idx = 0 ; idx < rsize ; idx += 1) { verinum::V tv = idx < tsize? t->value().get(idx) : verinum::V0; - verinum::V fv = idx < rsize? f->value().get(idx) : verinum::V0; + verinum::V fv = idx < fsize? f->value().get(idx) : verinum::V0; - if (tv == fv) - val.set(idx, tv); - else - val.set(idx, verinum::Vx); + if (tv == fv) val.set(idx, tv); + else val.set(idx, verinum::Vx); } if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " - << "constant condition value: " << c->value() << endl; + << "constant condition value: "; + print_ternary_cond(cond_); cerr << get_fileline() << ": : Blending cases to get " << val << endl; } @@ -1478,22 +1431,6 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) return rc; } -void NetEUnary::eval_expr_() -{ - assert(expr_); - if (dynamic_cast(expr_)) - return; - if (dynamic_cast(expr_)) - return; - - NetExpr*oper = expr_->eval_tree(); - if (oper == 0) - return; - - delete expr_; - expr_ = oper; -} - NetExpr* NetEUnary::eval_tree_real_() { NetECReal*val= dynamic_cast (expr_), *res; @@ -1504,10 +1441,12 @@ NetExpr* NetEUnary::eval_tree_real_() res = new NetECReal(val->value()); res->set_line(*this); return res; + case '-': res = new NetECReal(-(val->value())); res->set_line(*this); return res; + default: return 0; } @@ -1515,12 +1454,11 @@ NetExpr* NetEUnary::eval_tree_real_() NetExpr* NetEUnary::eval_tree(int prune_to_width) { - eval_expr_(); + eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); NetEConst*rval = dynamic_cast(expr_); - if (rval == 0) - return 0; + if (rval == 0) return 0; verinum val = rval->value(); @@ -1579,10 +1517,9 @@ NetExpr* NetEUBits::eval_tree(int prune_to_width) NetEConst* NetEUReduce::eval_tree(int prune_to_width) { - eval_expr_(); + eval_expr(expr_); NetEConst*rval = dynamic_cast(expr_); - if (rval == 0) - return 0; + if (rval == 0) return 0; verinum val = rval->value(); verinum::V res; diff --git a/expr_synth.cc b/expr_synth.cc index e9f6115ba..046a7acd1 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -554,13 +554,7 @@ NetNet* NetEBLogic::synthesize(Design*des) NetNet* NetEBShift::synthesize(Design*des) { - if (! dynamic_cast(right_)) { - NetExpr*tmp = right_->eval_tree(); - if (tmp) { - delete right_; - right_ = tmp; - } - } + eval_expr(right_); NetNet*lsig = left_->synthesize(des); diff --git a/net_expr.cc b/net_expr.cc index 73dd924f6..8130c4cd6 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -20,6 +20,7 @@ # include "config.h" # include "netlist.h" # include "compiler.h" +# include "netmisc.h" # include /* @@ -366,15 +367,7 @@ unsigned NetEConcat::repeat() if (repeat_calculated_) return repeat_value_; - assert(repeat_); - - if (! dynamic_cast(repeat_)) { - NetExpr*tmp = repeat_->eval_tree(); - if (tmp != 0) { - delete repeat_; - repeat_ = tmp; - } - } + eval_expr(repeat_); NetEConst*repeat_const = dynamic_cast(repeat_); diff --git a/netlist.cc b/netlist.cc index 8921c4a98..4946288a3 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2263,6 +2263,11 @@ ivl_variable_type_t NetETernary::expr_type() const if (tru == IVL_VT_BOOL && fal == IVL_VT_LOGIC) return IVL_VT_LOGIC; + if (tru == IVL_VT_REAL && (fal == IVL_VT_LOGIC || fal == IVL_VT_BOOL)) + return IVL_VT_REAL; + if (fal == IVL_VT_REAL && (tru == IVL_VT_LOGIC || tru == IVL_VT_BOOL)) + return IVL_VT_REAL; + if (tru != fal) { cerr << get_fileline() << ": internal error:" << " Unexpected ?: type clash:" diff --git a/netlist.h b/netlist.h index 533e6bdc0..b1d2d68b5 100644 --- a/netlist.h +++ b/netlist.h @@ -2678,7 +2678,6 @@ class NetEBinary : public NetExpr { NetExpr* left_; NetExpr* right_; - void eval_sub_tree_(); bool get_real_arguments_(verireal&lv, verireal&rv); }; @@ -3152,8 +3151,6 @@ class NetEUnary : public NetExpr { char op_; NetExpr* expr_; - void eval_expr_(); - private: virtual NetExpr* eval_tree_real_(); }; diff --git a/netmisc.cc b/netmisc.cc index 47ac976dc..1c8d7d4b3 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -127,17 +127,27 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, const PExpr*pe, int expr_wid, int prune_width) { NetExpr*tmp = pe->elaborate_expr(des, scope, expr_wid, false); - if (tmp == 0) - return 0; + if (tmp == 0) return 0; - if (NetExpr*tmp2 = tmp->eval_tree(prune_width)) { - delete tmp; - tmp = tmp2; - } + eval_expr(tmp, prune_width); return tmp; } +void eval_expr(NetExpr*&expr, int prune_width) +{ + assert(expr); + if (dynamic_cast(expr)) return; + if (dynamic_cast(expr)) return; + + NetExpr*tmp = expr->eval_tree(prune_width); + if (tmp != 0) { + tmp->set_line(*expr); + delete expr; + expr = tmp; + } +} + bool eval_as_long(long&value, NetExpr*expr) { if (NetEConst*tmp = dynamic_cast(expr) ) { @@ -221,3 +231,43 @@ const char *human_readable_op(const char op) } return type; } + +const_bool const_logical(const NetExpr*expr) +{ + switch (expr->expr_type()) { + case IVL_VT_REAL: { + const NetECReal*val = dynamic_cast (expr); + if (val == 0) return C_NON; + if (val->value().as_double() == 0.0) return C_0; + else return C_1; + } + + case IVL_VT_BOOL: + case IVL_VT_LOGIC: { + const NetEConst*val = dynamic_cast (expr); + if (val == 0) return C_NON; + verinum cval = val->value(); + const_bool res = C_0; + for (unsigned idx = 0; idx < cval.len(); idx += 1) { + switch (cval.get(idx)) { + case verinum::V1: + res = C_1; + break; + + case verinum::V0: + break; + + default: + if (res == C_0) res = C_X; + break; + } + } + return res; + } + + default: + break; + } + + return C_NON; +} diff --git a/netmisc.h b/netmisc.h index 3f9e00717..a29221f9c 100644 --- a/netmisc.h +++ b/netmisc.h @@ -119,6 +119,11 @@ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, const PExpr*pe, int expr_wid, int prune_width =-1); +/* + * This procedure elaborates an expression and if the elaboration is + * successful the original expression is replaced with the new one. + */ +void eval_expr(NetExpr*&expr, int prune_width =-1); /* * Get the long integer value for the passed in expression, if @@ -135,4 +140,16 @@ extern std::list eval_scope_path(Design*des, NetScope*scope, */ const char *human_readable_op(const char op); +/* + * Is the expression a constant value and if so what is its logical + * value. + * + * C_NON - the expression is not a constant value. + * C_0 - the expression is constant and it has a false value. + * C_1 - the expression is constant and it has a true value. + * C_X - the expression is constant and it has an 'bX value. + */ +enum const_bool { C_NON, C_0, C_1, C_X }; +const_bool const_logical(const NetExpr*expr); + #endif diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 4a33b217c..8382d003f 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -80,17 +80,6 @@ static int draw_binary_real(ivl_expr_t exp) case '%': fprintf(vvp_out, " %%mod/wr %d, %d;\n", l, r); break; -#if 0 - case '%': - { struct vector_info res = draw_eval_expr(exp, STUFF_OK_XZ); - l = allocate_word(); - fprintf(vvp_out, " %%ix/get %d, %u, %u;\n", - l, res.base, res.wid); - fprintf(vvp_out, " %%cvt/ri %d, %d;\n", l, l); - clr_vector(res); - } - break; -#endif case 'p': fprintf(vvp_out, " %%pow/wr %d, %d;\n", l, r); break; @@ -285,7 +274,7 @@ static int draw_signal_real_real(ivl_expr_t exp) word = get_number_immediate(ix); } - fprintf(vvp_out, " %%load/wr %d, v%p_%lu;\n", res, sig, word); + fprintf(vvp_out, " %%load/wr %d, v%p_%lu;\n", res, sig, word); return res; } @@ -316,6 +305,7 @@ static int draw_ternary_real(ivl_expr_t exp) unsigned lab_true = local_count++; unsigned lab_false = local_count++; + unsigned lab_out = local_count++; int tru, fal; int res = allocate_word(); @@ -335,21 +325,32 @@ static int draw_ternary_real(ivl_expr_t exp) tst.wid = 1; } - fprintf(vvp_out, " %%jmp/0 T_%d.%d, %u;\n", + fprintf(vvp_out, " %%jmp/0 T_%d.%d, %u;\n", thread_count, lab_true, tst.base); tru = draw_eval_real(true_ex); - fprintf(vvp_out, " %%mov/wr %d, %d;\n", res, tru); - fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_false); + fprintf(vvp_out, " %%mov/wr %d, %d;\n", res, tru); + fprintf(vvp_out, " %%jmp/1 T_%d.%d, %u; End of true expr.\n", + thread_count, lab_out, tst.base); clr_word(tru); fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_true); - fal = draw_eval_real(false_ex); - fprintf(vvp_out, " %%mov/wr %d, %d;\n", res, fal); + fprintf(vvp_out, " %%jmp/0 T_%d.%d, %u; End of false expr.\n", + thread_count, lab_false, tst.base); + + fprintf(vvp_out, " %%blend/wr %d, %d;\n", res, fal); + fprintf(vvp_out, " %%jmp T_%d.%d; End of blend\n", + thread_count, lab_out); + + fprintf(vvp_out, "T_%d.%d ; Move false result.\n", + thread_count, lab_false); + + fprintf(vvp_out, " %%mov/wr %d, %d;\n", res, fal); clr_word(fal); - fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_false); + /* This is the out label. */ + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out); clr_vector(tst); @@ -366,8 +367,8 @@ static int draw_unary_real(ivl_expr_t exp) if (ivl_expr_opcode(exp) == '-') { int res = allocate_word(); - fprintf(vvp_out, " %%loadi/wr %d, 0, 0; load 0.0\n", res); - fprintf(vvp_out, " %%sub/wr %d, %d;\n", res, sub); + fprintf(vvp_out, " %%loadi/wr %d, 0, 0; load 0.0\n", res); + fprintf(vvp_out, " %%sub/wr %d, %d;\n", res, sub); clr_word(sub); return res; @@ -424,10 +425,10 @@ int draw_eval_real(ivl_expr_t exp) clr_vector(sv); res = allocate_word(); - fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", + fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", sign_flag, res, sv.base, sv.wid); - fprintf(vvp_out, " %%cvt/ri %d, %d;\n", res, res); + fprintf(vvp_out, " %%cvt/ri %d, %d;\n", res, res); } else { fprintf(stderr, "XXXX Evaluate real expression (%d)\n", diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index cfb0f690c..e21d1675d 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -394,7 +394,7 @@ static int show_stmt_assign_sig_real(ivl_statement_t net) assert(ivl_signal_array_count(var) == 1); - fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", var, res); + fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", var, res); return 0; } diff --git a/vvp/codes.h b/vvp/codes.h index 26343bdc1..40113f895 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -47,6 +47,7 @@ extern bool of_ASSIGN_V0X1D(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_WR(vthread_t thr, vvp_code_t code); extern bool of_ASSIGN_X0(vthread_t thr, vvp_code_t code); extern bool of_BLEND(vthread_t thr, vvp_code_t code); +extern bool of_BLEND_WR(vthread_t thr, vvp_code_t code); extern bool of_BREAKPOINT(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_LINK(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_V(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 1028d0ce8..29e887a24 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -95,7 +95,8 @@ const static struct opcode_table_s opcode_table[] = { { "%assign/v0/x1/d",of_ASSIGN_V0X1D,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%assign/wr",of_ASSIGN_WR,3,{OA_VPI_PTR,OA_BIT1, OA_BIT2} }, { "%assign/x0",of_ASSIGN_X0,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, - { "%blend", of_BLEND, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%blend", of_BLEND, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%blend/wr", of_BLEND_WR,2, {OA_BIT1, OA_BIT2, OA_NONE} }, { "%breakpoint", of_BREAKPOINT, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%cassign/link",of_CASSIGN_LINK,2,{OA_FUNC_PTR,OA_FUNC_PTR2,OA_NONE} }, { "%cassign/v",of_CASSIGN_V,3,{OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index f8275e9a8..7628ed62e 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -139,6 +139,11 @@ manner like the expression (x ? : ). The truth table is: In other words, if the bits are identical, then take that value. Otherwise, the value is x. +* %blend/wr , + +This instruction blends real values for the ternary operator. If the +values match return that otherwise return 0.0. + * %breakpoint This instruction unconditionally breaks the simulator into the diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 5915a71bf..e4932ffb8 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -738,6 +738,14 @@ bool of_BLEND(vthread_t thr, vvp_code_t cp) return true; } +bool of_BLEND_WR(vthread_t thr, vvp_code_t cp) +{ + double t = thr->words[cp->bit_idx[0]].w_real; + double f = thr->words[cp->bit_idx[1]].w_real; + thr->words[cp->bit_idx[0]].w_real = (t == f) ? t : 0.0; + return true; +} + bool of_BREAKPOINT(vthread_t thr, vvp_code_t cp) { return true;