From fe72d02cf63c76f34d1ee1c6fa519669e1658eee Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 7 Mar 2008 18:51:50 -0800 Subject: [PATCH] Major rework of the ternary operator elaboration code. This patch reworks much of the ternary code to short circuit when possible and supports real values better. It adds a blend operator for real values that returns 0.0 when the values differ and the value when they match. This deviates slightly from the standard which specifies that the value for reals is always 0.0 when the conditional is 'bx. There are also a couple bug fixes. These fixes have not been ported to continuous assignments yet. Ternary operators used at compile time and in procedural assignments should be complete (short circuit and support real values). --- PDelays.cc | 68 +------- elab_expr.cc | 41 ++--- elab_lval.cc | 7 +- elab_net.cc | 45 ++++-- elab_pexpr.cc | 25 +-- elab_scope.cc | 3 +- elaborate.cc | 19 +-- eval_tree.cc | 357 +++++++++++++++++------------------------- expr_synth.cc | 8 +- net_expr.cc | 11 +- netlist.cc | 5 + netlist.h | 3 - netmisc.cc | 62 +++++++- netmisc.h | 17 ++ tgt-vvp/eval_real.c | 45 +++--- tgt-vvp/vvp_process.c | 2 +- vvp/codes.h | 1 + vvp/compile.cc | 3 +- vvp/opcodes.txt | 5 + vvp/vthread.cc | 8 + 20 files changed, 335 insertions(+), 400 deletions(-) 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;