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).
This commit is contained in:
Cary R 2008-03-07 18:51:50 -08:00 committed by Stephen Williams
parent c2bae1ad6c
commit fe72d02cf6
20 changed files with 335 additions and 400 deletions

View File

@ -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.
*
*/

View File

@ -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<NetEConst*>(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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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(list<perm_string>roots)
return des;
}

View File

@ -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<NetECReal*> (expr);
if (c == 0) return false;
val = c->value();
break;
}
case IVL_VT_BOOL:
case IVL_VT_LOGIC: {
NetEConst*c = dynamic_cast<NetEConst*>(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<NetECReal*> (left_);
if (lc == 0) return false;
lval = lc->value();
break;
}
case IVL_VT_BOOL:
case IVL_VT_LOGIC: {
NetEConst*lc = dynamic_cast<NetEConst*>(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<NetECReal*> (right_);
if (rc == 0) return 0;
rval = rc->value();
break;
}
case IVL_VT_BOOL:
case IVL_VT_LOGIC: {
NetEConst*rc = dynamic_cast<NetEConst*>(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<NetEConst*>(left_);
NetEConst*rc = dynamic_cast<NetEConst*>(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<NetECReal*>(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<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(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<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(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<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(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<NetEConst*>(right_);
if (re == 0)
return 0;
eval_expr(left_);
eval_expr(right_);
NetEConst*le = dynamic_cast<NetEConst*>(left_);
if (le == 0)
return 0;
NetEConst*re = dynamic_cast<NetEConst*>(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<NetEConst*>(expr_);
if (expr == 0) {
NetExpr*tmp = expr_->eval_tree();
if (tmp != 0) {
delete expr_;
expr_ = tmp;
}
expr = dynamic_cast<NetEConst*>(expr_);
}
long bval = 0;
if (base_) {
eval_expr(base_);
NetEConst*base = dynamic_cast<NetEConst*>(base_);
if (base == 0) {
NetExpr*tmp = base_->eval_tree();
if (tmp != 0) {
delete base_;
base_ = tmp;
}
base = dynamic_cast<NetEConst*>(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<NetEConst*>(expr)) {
cerr << c->value() << endl;
return;
}
if (NetECReal*c = dynamic_cast<NetECReal*>(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<NetEConst*>(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<NetEConst*>(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<NetEConst*>(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<NetEConst*>(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<NetEConst*>(true_val_);
if (t == 0)
return 0;
NetEConst*f = dynamic_cast<NetEConst*>(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<NetEConst*>(expr_))
return;
if (dynamic_cast<NetECReal*>(expr_))
return;
NetExpr*oper = expr_->eval_tree();
if (oper == 0)
return;
delete expr_;
expr_ = oper;
}
NetExpr* NetEUnary::eval_tree_real_()
{
NetECReal*val= dynamic_cast<NetECReal*> (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<NetEConst*>(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<NetEConst*>(expr_);
if (rval == 0)
return 0;
if (rval == 0) return 0;
verinum val = rval->value();
verinum::V res;

View File

@ -554,13 +554,7 @@ NetNet* NetEBLogic::synthesize(Design*des)
NetNet* NetEBShift::synthesize(Design*des)
{
if (! dynamic_cast<NetEConst*>(right_)) {
NetExpr*tmp = right_->eval_tree();
if (tmp) {
delete right_;
right_ = tmp;
}
}
eval_expr(right_);
NetNet*lsig = left_->synthesize(des);

View File

@ -20,6 +20,7 @@
# include "config.h"
# include "netlist.h"
# include "compiler.h"
# include "netmisc.h"
# include <iostream>
/*
@ -366,15 +367,7 @@ unsigned NetEConcat::repeat()
if (repeat_calculated_)
return repeat_value_;
assert(repeat_);
if (! dynamic_cast<NetEConst*>(repeat_)) {
NetExpr*tmp = repeat_->eval_tree();
if (tmp != 0) {
delete repeat_;
repeat_ = tmp;
}
}
eval_expr(repeat_);
NetEConst*repeat_const = dynamic_cast<NetEConst*>(repeat_);

View File

@ -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:"

View File

@ -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_();
};

View File

@ -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<NetEConst*>(expr)) return;
if (dynamic_cast<NetECReal*>(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<NetEConst*>(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<const NetECReal*> (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<const NetEConst*> (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;
}

View File

@ -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<hname_t> 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

View File

@ -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",

View File

@ -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;
}

View File

@ -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);

View File

@ -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} },

View File

@ -139,6 +139,11 @@ manner like the expression (x ? <a> : <b>). The truth table is:
In other words, if the bits are identical, then take that
value. Otherwise, the value is x.
* %blend/wr <bit-l>, <bit-r>
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

View File

@ -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;