iverilog/eval_tree.cc

1278 lines
31 KiB
C++

/*
* Copyright (c) 1999-2000 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
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT) && !defined(macintosh)
#ident "$Id: eval_tree.cc,v 1.40 2002/05/25 16:43:47 steve Exp $"
#endif
# include "config.h"
# include <iostream>
# include "netlist.h"
NetExpr* NetExpr::eval_tree()
{
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_()
{
NetExpr*tmp = left_->eval_tree();
if (tmp) {
delete left_;
left_ = tmp;
}
tmp = right_->eval_tree();
if (tmp){
delete right_;
right_ = tmp;
}
}
NetEConst* NetEBAdd::eval_tree()
{
eval_sub_tree_();
NetEConst*lc = dynamic_cast<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(right_);
if (rc == 0) return 0;
verinum lval = lc->value();
verinum rval = rc->value();
verinum val;
switch (op_) {
case '+':
val = lval + rval;
break;
case '-':
val = lval - rval;
break;
default:
return 0;
}
return new NetEConst(val);
}
NetEConst* NetEBBits::eval_tree()
{
eval_sub_tree_();
NetEConst*lc = dynamic_cast<NetEConst*>(left_);
NetEConst*rc = dynamic_cast<NetEConst*>(right_);
/* 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))) {
verinum res (verinum::V0, expr_width());
return new NetEConst(res);
}
if ((op() == '&') && rc && (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();
unsigned lwid = lc->expr_width();
if (lwid == 0) lwid = lval.len();
unsigned rwid = rc->expr_width();
if (rwid == 0) rwid = rval.len();
unsigned wid = expr_width();
if (wid == 0)
wid = (rwid > lwid)? rwid : lwid;
verinum res (verinum::V0, wid);
if (lwid > wid)
lwid = wid;
if (rwid > wid)
rwid = wid;
switch (op()) {
case '|': {
unsigned cnt = lwid;
if (cnt > wid) cnt = wid;
if (cnt > rwid) cnt = rwid;
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
res.set(idx, lval.get(idx) | rval.get(idx));
if (lwid < rwid)
for (unsigned idx = lwid ; idx < rwid ; idx += 1)
res.set(idx, rval.get(idx));
if (rwid < lwid)
for (unsigned idx = rwid ; idx < lwid ; idx += 1)
res.set(idx, lval.get(idx));
break;
}
case '&': {
unsigned cnt = lwid;
if (cnt > wid) cnt = wid;
if (cnt > rwid) cnt = rwid;
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
res.set(idx, lval.get(idx) & rval.get(idx));
break;
}
default:
return 0;
}
return new NetEConst(res);
}
NetEConst* NetEBComp::eval_eqeq_()
{
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
const verinum&lv = l->value();
const verinum&rv = r->value();
unsigned s_len = lv.len();
if (rv.len() < s_len) s_len = rv.len();
verinum result(verinum::V1, 1);
for (unsigned idx = 0 ; idx < s_len; idx += 1) {
if (lv[idx] != rv[idx])
result = verinum::V0;
}
// If one operand was wider than the other, check that the
// remaining bits are all zero
if (lv.len() > s_len) {
for (unsigned idx = s_len; idx < lv.len(); idx += 1) {
if (lv[idx] != 0)
result = verinum::V0;
}
}
if (rv.len() > s_len) {
for (unsigned idx = s_len; idx < rv.len(); idx += 1) {
if (rv[idx] != 0)
result = verinum::V0;
}
}
return new NetEConst(result);
}
NetEConst* NetEBComp::eval_less_()
{
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
verinum rv = r->value();
if (! rv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
/* Detect the case where the right side is greater that or
equal to the largest value the left side can possibly
have. */
assert(left_->expr_width() > 0);
verinum lv (verinum::V1, left_->expr_width());
if (lv < rv) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
lv = l->value();
if (! lv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
if (lv.has_sign() && rv.has_sign()) {
if (lv.as_long() < rv.as_long()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
} else {
if (lv.as_ulong() < rv.as_ulong()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
}
verinum result(verinum::V0, 1);
return new NetEConst(result);
}
NetEConst* NetEBComp::eval_leeq_()
{
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
verinum rv = r->value();
if (! rv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
/* Detect the case where the right side is greater that or
equal to the largest value the left side can possibly
have. */
assert(left_->expr_width() > 0);
verinum lv (verinum::V1, left_->expr_width());
if (lv <= rv) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
lv = l->value();
if (! lv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
if (lv.has_sign() && rv.has_sign()) {
if (lv.as_long() <= rv.as_long()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
} else {
if (lv.as_ulong() <= rv.as_ulong()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
}
verinum result(verinum::V0, 1);
return new NetEConst(result);
}
NetEConst* NetEBComp::eval_gt_()
{
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
verinum lv = l->value();
if (! lv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
/* Detect the case where the left side is greater than the
largest value the right side can possibly have. */
assert(right_->expr_width() > 0);
verinum rv (verinum::V1, right_->expr_width());
if (lv > rv) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
rv = r->value();
if (! rv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
if (lv.has_sign() && rv.has_sign() && (lv.as_long() > rv.as_long())) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
if (lv.as_ulong() > rv.as_ulong()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
verinum result(verinum::V0, 1);
return new NetEConst(result);
}
NetEConst* NetEBComp::eval_gteq_()
{
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
verinum lv = l->value();
if (! lv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
/* Detect the case where the left side is greater than the
largest value the right side can possibly have. */
assert(right_->expr_width() > 0);
verinum rv (verinum::V1, right_->expr_width());
if (lv >= rv) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
/* Now go on to the normal test of the values. */
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
rv = r->value();
if (! rv.is_defined()) {
verinum result(verinum::Vx, 1);
return new NetEConst(result);
}
if (lv.has_sign() && rv.has_sign() && (lv.as_long() >= rv.as_long())) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
if (lv.as_ulong() >= rv.as_ulong()) {
verinum result(verinum::V1, 1);
return new NetEConst(result);
}
verinum result(verinum::V0, 1);
return new NetEConst(result);
}
NetEConst* NetEBComp::eval_neeq_()
{
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
const verinum&lv = l->value();
const verinum&rv = r->value();
verinum::V res = verinum::V0;
unsigned top = lv.len();
if (rv.len() < top)
top = rv.len();
for (unsigned idx = 0 ; idx < top ; idx += 1) {
switch (lv.get(idx)) {
case verinum::Vx:
case verinum::Vz:
res = verinum::Vx;
break;
default:
break;
}
switch (rv.get(idx)) {
case verinum::Vx:
case verinum::Vz:
res = verinum::Vx;
break;
default:
break;
}
if (res == verinum::Vx)
break;
if (rv.get(idx) != lv.get(idx))
res = verinum::V1;
}
if (res != verinum::Vx) {
for (unsigned idx = top ; idx < lv.len() ; idx += 1)
switch (lv.get(idx)) {
case verinum::Vx:
case verinum::Vz:
res = verinum::Vx;
break;
case verinum::V1:
if (res != verinum::Vx)
res = verinum::V1;
break;
default:
break;
}
for (unsigned idx = top ; idx < rv.len() ; idx += 1)
switch (rv.get(idx)) {
case verinum::Vx:
case verinum::Vz:
res = verinum::Vx;
break;
case verinum::V1:
if (res != verinum::Vx)
res = verinum::V1;
break;
default:
break;
}
}
return new NetEConst(verinum(res));
}
NetEConst* NetEBComp::eval_eqeqeq_()
{
NetEConst*l = dynamic_cast<NetEConst*>(left_);
if (l == 0) return 0;
NetEConst*r = dynamic_cast<NetEConst*>(right_);
if (r == 0) return 0;
const verinum&lv = l->value();
const verinum&rv = r->value();
verinum::V res = verinum::V1;
unsigned cnt = lv.len();
if (cnt > rv.len())
cnt = rv.len();
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
if (lv.get(idx) != rv.get(idx))
res = verinum::V0;
for (unsigned idx = cnt ; idx < lv.len() ; idx += 1)
if (lv.get(idx) != verinum::V0)
res = verinum::V0;
for (unsigned idx = cnt ; idx < rv.len() ; idx += 1)
if (rv.get(idx) != verinum::V0)
res = verinum::V0;
return new NetEConst(verinum(res, 1));
}
NetEConst* NetEBComp::eval_neeqeq_()
{
NetEConst*tmp = eval_eqeqeq_();
if (tmp == 0)
return 0;
NetEConst*res;
if (tmp->value().get(0) == verinum::V0)
res = new NetEConst(verinum(verinum::V1,1));
else
res = new NetEConst(verinum(verinum::V0,1));
delete tmp;
return res;
}
NetEConst* NetEBComp::eval_tree()
{
eval_sub_tree_();
switch (op_) {
case 'E': // Case equality (===)
return eval_eqeqeq_();
case 'e': // Equality (==)
return eval_eqeq_();
case 'G': // >=
return eval_gteq_();
case 'L': // <=
return eval_leeq_();
case 'N': // Cse inequality (!==)
return eval_neeqeq_();
case 'n': // not-equal (!=)
return eval_neeq_();
case '<': // Less than
return eval_less_();
case '>': // Greater then
return eval_gt_();
default:
return 0;
}
}
/*
* The NetEBDiv operator includes the / and % opeators. First evaluate
* the sub-expressions, then perform the required operation.
*/
NetEConst* NetEBDiv::eval_tree()
{
eval_sub_tree_();
NetEConst*lc = dynamic_cast<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(right_);
if (rc == 0) return 0;
verinum lval = lc->value();
verinum rval = rc->value();
switch (op_) {
case '/':
return new NetEConst(lval / rval);
case '%':
return new NetEConst(lval % rval);
}
return 0;
}
NetEConst* NetEBLogic::eval_tree()
{
eval_sub_tree_();
NetEConst*lc = dynamic_cast<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(right_);
if (rc == 0) return 0;
verinum::V lv = verinum::V0;
verinum::V rv = verinum::V0;
verinum v = lc->value();
for (unsigned idx = 0 ; idx < v.len() ; idx += 1)
if (v.get(idx) == verinum::V1)
lv = verinum::V1;
if (lv == verinum::V0)
for (unsigned idx = 0 ; idx < v.len() ; idx += 1)
if (v.get(idx) != verinum::V0)
lv = verinum::Vx;
v = rc->value();
for (unsigned idx = 0 ; idx < v.len() ; idx += 1)
if (v.get(idx) == verinum::V1)
rv = verinum::V1;
if (lv == verinum::V0)
for (unsigned idx = 0 ; idx < v.len() ; idx += 1)
if (v.get(idx) != verinum::V0)
rv = verinum::Vx;
verinum::V res;
switch (op_) {
case 'a': { // Logical AND (&&)
if ((lv == verinum::V0) || (rv == verinum::V0))
res = verinum::V0;
else if ((lv == verinum::V1) && (rv == verinum::V1))
res = verinum::V1;
else
res = verinum::Vx;
break;
}
case 'o': { // Logical OR (||)
if ((lv == verinum::V1) || (rv == verinum::V1))
res = verinum::V1;
else if ((lv == verinum::V0) && (rv == verinum::V0))
res = verinum::V0;
else
res = verinum::Vx;
break;
}
default:
return 0;
}
return new NetEConst(verinum(res, 1));
}
NetEConst* NetEBMult::eval_tree()
{
eval_sub_tree_();
NetEConst*lc = dynamic_cast<NetEConst*>(left_);
if (lc == 0) return 0;
NetEConst*rc = dynamic_cast<NetEConst*>(right_);
if (rc == 0) return 0;
verinum lval = lc->value();
verinum rval = rc->value();
return new NetEConst(lval * rval);
}
/*
* Evaluate the shift operator if possible. For this to work, both
* operands must be constant.
*/
NetEConst* NetEBShift::eval_tree()
{
eval_sub_tree_();
NetEConst*re = dynamic_cast<NetEConst*>(right_);
if (re == 0)
return 0;
NetEConst*le = dynamic_cast<NetEConst*>(left_);
if (le == 0)
return 0;
NetEConst*res;
verinum rv = re->value();
verinum lv = le->value();
/* Calculate the width of the result. If it is not fixed, then
get it from the left operand. */
unsigned wid = expr_width();
if (wid == 0)
wid = left_->expr_width();
if (rv.is_defined()) {
unsigned shift = rv.as_ulong();
assert(wid);
verinum nv (verinum::V0, wid);
if (op() == 'r') {
unsigned cnt = wid;
if (cnt > nv.len())
cnt = nv.len();
if (shift >= lv.len())
cnt = 0;
else if (cnt > (lv.len()-shift))
cnt = (lv.len()-shift);
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
nv.set(idx, lv[idx+shift]);
} else {
unsigned cnt = wid;
if (cnt > lv.len())
cnt = lv.len();
if (shift >= nv.len())
cnt = 0;
else if (cnt > (nv.len()-shift))
cnt = nv.len() - shift;
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
nv.set(idx+shift, lv[idx]);
}
res = new NetEConst(nv);
} else {
assert(wid);
verinum nv (verinum::Vx, wid);
res = new NetEConst(nv);
}
return res;
}
NetEConst* NetEConcat::eval_tree()
{
unsigned repeat_val = repeat();
unsigned local_errors = 0;
unsigned gap = 0;
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
// 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 this parameter is already a constant, all is well
// so go on.
if (dynamic_cast<NetEConst*>(parms_[idx])) {
gap += parms_[idx]->expr_width();
continue;
}
// Finally, try to evaluate the parameter expression
// that is here. If I succeed, reset the parameter to
// the evaluated value.
assert(parms_[idx]);
NetExpr*expr = parms_[idx]->eval_tree();
if (expr) {
delete parms_[idx];
parms_[idx] = expr;
if (! expr->has_width()) {
cerr << get_line() << ": error: concatenation "
<< "operand has indefinite width: "
<< *parms_[idx] << endl;
local_errors += 1;
}
gap += expr->expr_width();
}
}
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
// width.
if (repeat_val == 0) {
verinum val (verinum::V0, expr_width());
NetEConst*res = new NetEConst(val);
res->set_width(val.len());
return res;
}
// At this point, the "gap" is the width of a single repeat of
// the concatenation. The total width of the result is the gap
// times the repeat count.
verinum val (verinum::Vx, repeat_val * gap);
// build up the result from least significant to most.
unsigned cur = 0;
bool is_string_flag = true;
for (unsigned idx = parms_.count() ; idx > 0 ; idx -= 1) {
NetEConst*expr = dynamic_cast<NetEConst*>(parms_[idx-1]);
if (expr == 0)
return 0;
verinum tmp = expr->value();
for (unsigned bit = 0; bit < tmp.len(); bit += 1, cur += 1)
for (unsigned rep = 0 ; rep < repeat_val ; rep += 1)
val.set(rep*gap+cur, tmp[bit]);
is_string_flag = is_string_flag && tmp.is_string();
}
/* If all the values were strings, then re-stringify this
constant. This might be useful information in the code
generator or other optimizer steps. */
if (is_string_flag) {
val = verinum(val.as_string());
}
NetEConst*res = new NetEConst(val);
res->set_width(val.len());
return res;
}
/*
* There are limits to our ability to evaluate a memory reference
* expression, because the content of a memory is never
* constant. However, the index expression may be precalculated, and
* there are certain index values that do give us constant results.
*/
NetExpr* NetEMemory::eval_tree()
{
/* Attempt to evaluate the index expression to a constant, if
it is not already. */
if (idx_ && !dynamic_cast<NetEConst*>(idx_)) {
NetExpr* tmp = idx_->eval_tree();
if (tmp) {
delete idx_;
idx_ = tmp;
}
}
NetEConst*itmp = dynamic_cast<NetEConst*>(idx_);
if (itmp == 0)
return 0;
verinum ival = itmp->value();
/* If the index expression has any x or z bits, then we know
already that the expression result is a constant x. */
if (! ival.is_defined()) {
verinum xres (verinum::Vx, mem_->width(), false);
NetEConst*res = new NetEConst(xres);
return res;
}
/* If the index expression is outside the range of the memory,
then the result is a constant x. */
unsigned norm_idx = mem_->index_to_address(ival.as_long());
if (norm_idx >= mem_->count()) {
verinum xres (verinum::Vx, mem_->width(), false);
NetEConst*res = new NetEConst(xres);
return res;
}
return 0;
}
NetExpr* NetEParam::eval_tree()
{
if (des_ == 0)
return 0;
assert(scope_);
assert(name_.peek_name(1) == 0);
const NetExpr*expr = scope_->get_parameter(name_.peek_name(0));
if (expr == 0) {
cerr << get_line() << ": internal error: Unable to match "
<< "parameter " << name_ << " in scope "
<< scope_->name() << endl;
return 0;
}
assert(expr);
NetExpr*nexpr = expr->dup_expr();
assert(nexpr);
// If the parameter that I refer to is already evaluated, then
// return the constant value.
if (dynamic_cast<NetEConst*>(nexpr))
return nexpr;
// Try to evaluate the expression. If I cannot, then the
// expression is not a constant expression and I fail here.
NetExpr*res = nexpr->eval_tree();
if (res == 0) {
delete nexpr;
return 0;
}
// The result can be saved as the value of the parameter for
// future reference, and return a copy to the caller.
scope_->set_parameter(name_.peek_name(0), res);
return res->dup_expr();
}
/*
* A ternary expression evaluation is controlled by the condition
* expression. If the condition evaluates to true or false, then
* return the evaluated true or false expression. If the condition
* evaluates to x or z, then merge the constant bits of the true and
* false expressions.
*/
NetExpr* NetETernary::eval_tree()
{
NetExpr*tmp;
assert(cond_);
if (0 == dynamic_cast<NetEConst*>(cond_)) {
tmp = cond_->eval_tree();
if (tmp != 0) {
delete cond_;
cond_ = tmp;
}
}
assert(true_val_);
if (0 == dynamic_cast<NetEConst*>(true_val_)) {
tmp = true_val_->eval_tree();
if (tmp != 0) {
delete true_val_;
true_val_ = tmp;
}
}
assert(false_val_);
if (0 == dynamic_cast<NetEConst*>(false_val_)) {
tmp = false_val_->eval_tree();
if (tmp != 0) {
delete false_val_;
false_val_ = tmp;
}
}
NetEConst*c = dynamic_cast<NetEConst*>(cond_);
if (c == 0)
return 0;
/* 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 (c->value().get(0) == verinum::V1)
return true_val_->dup_expr();
if (c->value().get(0) == verinum::V0)
return false_val_->dup_expr();
/* 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. */
NetEConst*t = dynamic_cast<NetEConst*>(true_val_);
if (t == 0)
return 0;
NetEConst*f = dynamic_cast<NetEConst*>(false_val_);
if (f == 0)
return 0;
unsigned size = t->expr_width();
assert(size == f->expr_width());
verinum val (verinum::V0, size);
for (unsigned idx = 0 ; idx < size ; idx += 1) {
verinum::V tv = t->value().get(idx);
verinum::V fv = f->value().get(idx);
if (tv == fv)
val.set(idx, tv);
else
val.set(idx, verinum::Vx);
}
NetEConst*rc = new NetEConst(val);
rc->set_line(*this);
return rc;
}
void NetEUnary::eval_expr_()
{
assert(expr_);
if (dynamic_cast<NetEConst*>(expr_))
return;
NetExpr*oper = expr_->eval_tree();
if (oper == 0)
return;
delete expr_;
expr_ = oper;
}
NetEConst* NetEUnary::eval_tree()
{
eval_expr_();
NetEConst*rval = dynamic_cast<NetEConst*>(expr_);
if (rval == 0)
return 0;
verinum val = rval->value();
switch (op_) {
case '+':
/* Unary + is a no-op. */
return new NetEConst(val);
case '-': {
if (val.is_defined()) {
verinum tmp (verinum::V0, val.len());
tmp.has_sign(val.has_sign());
val = tmp - val;
} else {
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
val.set(idx, verinum::Vx);
}
return new NetEConst(val);
}
case '~': {
/* Bitwise not is even simpler then logical
not. Just invert all the bits of the operand and
make the new value with the same dimensions. */
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
switch (val.get(idx)) {
case verinum::V0:
val.set(idx, verinum::V1);
break;
case verinum::V1:
val.set(idx, verinum::V0);
break;
default:
val.set(idx, verinum::Vx);
}
return new NetEConst(val);
}
default:
return 0;
}
}
NetEConst* NetEUBits::eval_tree()
{
return NetEUnary::eval_tree();
}
NetEConst* NetEUReduce::eval_tree()
{
eval_expr_();
NetEConst*rval = dynamic_cast<NetEConst*>(expr_);
if (rval == 0)
return 0;
verinum val = rval->value();
verinum::V res;
switch (op_) {
case '!': {
/* Evaluate the unary logical not by first scanning
the operand value for V1 and Vx bits. If we find
any V1 bits we know that the value is TRUE, so
the result of ! is V0. If there are no V1 bits
but there are some Vx/Vz bits, the result is
unknown. Otherwise, the result is V1. */
unsigned v1 = 0, vx = 0;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1) {
switch (val.get(idx)) {
case verinum::V0:
break;
case verinum::V1:
v1 += 1;
break;
default:
vx += 1;
break;
}
}
res = v1? verinum::V0 : (vx? verinum::Vx : verinum::V1);
break;
}
case '&': {
res = verinum::V1;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
res = res & val.get(idx);
break;
}
case '|': {
res = verinum::V0;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
res = res | val.get(idx);
break;
}
case '^': {
/* Reduction XOR. */
unsigned ones = 0, unknown = 0;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
switch (val.get(idx)) {
case verinum::V0:
break;
case verinum::V1:
ones += 1;
break;
default:
unknown += 1;
break;
}
if (unknown)
return new NetEConst(verinum(verinum::Vx,1,true));
if (ones%2)
return new NetEConst(verinum(verinum::V1,1,true));
return new NetEConst(verinum(verinum::V0,1,true));
}
default:
return 0;
}
return new NetEConst(verinum(res, 1));
}
/*
* $Log: eval_tree.cc,v $
* Revision 1.40 2002/05/25 16:43:47 steve
* Eval ^ and &0 expressions.
*
* Revision 1.39 2002/05/06 02:30:27 steve
* Allow parameters in concatenation of widths are defined.
*
* Revision 1.38 2002/05/05 21:11:50 steve
* Put off evaluation of concatenation repeat expresions
* until after parameters are defined. This allows parms
* to be used in repeat expresions.
*
* Add the builtin $signed system function.
*
* Revision 1.37 2002/04/27 05:03:46 steve
* Preserve stringiness string part select and concatenation.
*
* Revision 1.36 2002/04/27 03:17:15 steve
* Fixup eval of signed constants.
*
* Revision 1.35 2002/04/24 02:47:02 steve
* Fix compile time eval of signed <= signed.
*
* Revision 1.34 2002/04/14 19:28:47 steve
* Fix constant evaluation of unary
*
* Revision 1.33 2002/02/01 05:09:14 steve
* Propagate sign in unary minus.
*
* Revision 1.32 2002/01/22 01:40:04 steve
* Precalculate constant results of memory index expressions.
*
* Revision 1.31 2001/12/30 00:39:25 steve
* Evaluate constant unary minus.
*
* Revision 1.30 2001/12/03 04:47:15 steve
* Parser and pform use hierarchical names as hname_t
* objects instead of encoded strings.
*
* Revision 1.29 2001/11/19 01:54:14 steve
* Port close cropping behavior from mcrgb
* Move window array reset to libmc.
*
* Revision 1.28 2001/10/22 15:31:21 steve
* fix constant overrun in | operands.
*
* Revision 1.27 2001/10/20 00:11:24 steve
* Evaluate constant == when operands differ in width.
*
* Revision 1.26 2001/07/25 03:10:49 steve
* Create a config.h.in file to hold all the config
* junk, and support gcc 3.0. (Stephan Boettcher)
*
* Revision 1.25 2001/07/16 20:41:41 steve
* Handle 0 counts in repeat concatenation.
*
* Revision 1.24 2001/02/10 20:29:39 steve
* In the context of range declarations, use elab_and_eval instead
* of the less robust eval_const methods.
*
* Revision 1.23 2001/02/09 05:44:23 steve
* support evaluation of constant < in expressions.
*
* Revision 1.22 2001/02/07 02:46:31 steve
* Support constant evaluation of / and % (PR#124)
*
* Revision 1.21 2001/01/14 23:04:56 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.20 2001/01/04 16:49:50 steve
* Evaluate constant === and !== expressions.
*
* Revision 1.19 2001/01/04 04:28:42 steve
* Evaluate constant logical && and ||.
*
* Revision 1.18 2001/01/02 04:21:14 steve
* Support a bunch of unary operators in parameter expressions.
*
* Revision 1.17 2001/01/02 03:23:40 steve
* Evaluate constant &, | and unary ~.
*
* Revision 1.16 2001/01/01 21:49:33 steve
* Fix shift and ternary operators in parameter expressions (PR#86)
*
* Revision 1.15 2000/12/16 20:00:17 steve
* Handle non-constant l-values.
*
* Revision 1.14 2000/12/16 19:03:30 steve
* Evaluate <= and ?: in parameter expressions (PR#81)
*
* Revision 1.13 2000/09/29 04:42:56 steve
* Cnstant evaluation of NE.
*
* Revision 1.12 2000/09/27 18:28:37 steve
* multiply in parameter expressions.
*
* Revision 1.11 2000/07/07 04:53:54 steve
* Add support for non-constant delays in delay statements,
* Support evaluating ! in constant expressions, and
* move some code from netlist.cc to net_proc.cc.
*
* Revision 1.10 2000/04/28 18:43:23 steve
* integer division in expressions properly get width.
*
* Revision 1.9 2000/03/08 04:36:53 steve
* Redesign the implementation of scopes and parameters.
* I now generate the scopes and notice the parameters
* in a separate pass over the pform. Once the scopes
* are generated, I can process overrides and evalutate
* paremeters before elaboration begins.
*
* Revision 1.8 2000/02/23 02:56:54 steve
* Macintosh compilers do not support ident.
*
* Revision 1.7 2000/01/13 03:35:35 steve
* Multiplication all the way to simulation.
*
* Revision 1.6 1999/10/22 23:57:53 steve
* do the <= in bits, not numbers.
*
* Revision 1.5 1999/10/10 23:29:37 steve
* Support evaluating + operator at compile time.
*
* Revision 1.4 1999/09/23 03:56:57 steve
* Support shift operators.
*
* Revision 1.3 1999/09/23 00:21:54 steve
* Move set_width methods into a single file,
* Add the NetEBLogic class for logic expressions,
* Fix error setting with of && in if statements.
*
* Revision 1.2 1999/09/21 00:13:40 steve
* Support parameters that reference other paramters.
*
* Revision 1.1 1999/09/20 02:21:10 steve
* Elaborate parameters in phases.
*
*/