diff --git a/PExpr.h b/PExpr.h index 67440005a6..5dfc9d2a35 100644 --- a/PExpr.h +++ b/PExpr.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: PExpr.h,v 1.71 2005/10/04 04:09:25 steve Exp $" +#ident "$Id: PExpr.h,v 1.72 2005/11/10 13:28:11 steve Exp $" #endif # include @@ -256,6 +256,26 @@ class PEIdent : public PExpr { NetScope*scope, const NetExpr*par, NetScope*found) const; + NetExpr*elaborate_expr_net(Design*des, + NetScope*scope, + NetNet*net, + NetScope*found) const; + NetExpr*elaborate_expr_net_part_(Design*des, + NetScope*scope, + NetNet*net, + NetScope*found) const; + NetExpr*elaborate_expr_net_idx_up_(Design*des, + NetScope*scope, + NetNet*net, + NetScope*found) const; + NetExpr*elaborate_expr_net_idx_do_(Design*des, + NetScope*scope, + NetNet*net, + NetScope*found) const; + NetExpr*elaborate_expr_net_bit_(Design*des, + NetScope*scope, + NetNet*net, + NetScope*found) const; hname_t path_; public: @@ -523,6 +543,13 @@ class PECallFunction : public PExpr { /* * $Log: PExpr.h,v $ + * Revision 1.72 2005/11/10 13:28:11 steve + * Reorganize signal part select handling, and add support for + * indexed part selects. + * + * Expand expression constant propagation to eliminate extra + * sums in certain cases. + * * Revision 1.71 2005/10/04 04:09:25 steve * Add support for indexed select attached to parameters. * diff --git a/elab_expr.cc b/elab_expr.cc index 9f735cfe80..93f2e7e778 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2003 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2005 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 @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: elab_expr.cc,v 1.98 2005/10/04 04:09:25 steve Exp $" +#ident "$Id: elab_expr.cc,v 1.99 2005/11/10 13:28:11 steve Exp $" #endif # include "config.h" @@ -504,157 +504,9 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. - if (net != 0) { + if (net != 0) + return elaborate_expr_net(des, scope, net, found_in); - // If this is a part select of a signal, then make a new - // temporary signal that is connected to just the - // selected bits. The lsb_ and msb_ expressions are from - // the foo[msb:lsb] expression in the original. - if (lsb_) { - assert(msb_); - assert(sel_ == SEL_PART); - verinum*lsn = lsb_->eval_const(des, scope); - verinum*msn = msb_->eval_const(des, scope); - if ((lsn == 0) || (msn == 0)) { - cerr << get_line() << ": error: " - "Part select expressions must be " - "constant expressions." << endl; - des->errors += 1; - return 0; - } - - assert(lsn); - assert(msn); - - /* The indices of part selects are signed - integers, so allow negative values. However, - the width that they represent is - unsigned. Remember that any order is possible, - i.e., [1:0], [-4,6], etc. */ - - long lsv = lsn->as_long(); - long msv = msn->as_long(); - unsigned long wid = 1 + ((msv>lsv)? (msv-lsv) : (lsv-msv)); - if (wid > net->vector_width()) { - cerr << get_line() << ": error: part select [" - << msv << ":" << lsv << "] out of range." - << endl; - des->errors += 1; - delete lsn; - delete msn; - return 0; - } - assert(wid <= net->vector_width()); - - if (net->sb_to_idx(msv) < net->sb_to_idx(lsv)) { - cerr << get_line() << ": error: part select [" - << msv << ":" << lsv << "] out of order." - << endl; - des->errors += 1; - delete lsn; - delete msn; - return 0; - } - - - if (net->sb_to_idx(msv) >= net->vector_width()) { - cerr << get_line() << ": error: part select [" - << msv << ":" << lsv << "] out of range." - << endl; - des->errors += 1; - delete lsn; - delete msn; - return 0; - } - - NetESignal*tmp = new NetESignal(net); - tmp->set_line(*this); - - // If the part select convers exactly the entire - // vector, then do not bother with it. Return the - // signal itself. - if (net->sb_to_idx(lsv) == 0 && wid == net->vector_width()) - return tmp; - - NetExpr*ex = new NetEConst(verinum(net->sb_to_idx(lsv))); - NetESelect*ss = new NetESelect(tmp, ex, wid); - ss->set_line(*this); - - return ss; - } - - // If the bit select is constant, then treat it similar - // to the part select, so that I save the effort of - // making a mux part in the netlist. - verinum*msn; - if (msb_ && (msn = msb_->eval_const(des, scope))) { - assert(idx_ == 0); - long msv = msn->as_long(); - unsigned idx = net->sb_to_idx(msv); - - if (idx >= net->vector_width()) { - /* The bit select is out of range of the - vector. This is legal, but returns a - constant 1'bx value. */ - verinum x (verinum::Vx); - NetEConst*tmp = new NetEConst(x); - tmp->set_line(*this); - - cerr << get_line() << ": warning: Bit select [" - << msv << "] out of range of vector " - << net->name() << "[" << net->msb() - << ":" << net->lsb() << "]." << endl; - cerr << get_line() << ": : Replacing " - << "expression with a constant 1'bx." << endl; - delete msn; - return tmp; - } - - NetESignal*tmp = new NetESignal(net); - tmp->set_line(*this); - // If the vector is only one bit, we are done. The - // bit select will return the scaler itself. - if (net->vector_width() == 1) - return tmp; - - // Make an expression out of the index - NetEConst*idx_c = new NetEConst(verinum(idx)); - idx_c->set_line(*net); - - // Make a bit select with the canonical index - NetESelect*res = new NetESelect(tmp, idx_c, 1); - res->set_line(*net); - - return res; - } - - NetESignal*node = new NetESignal(net); - assert(idx_ == 0); - - // Non-constant bit select? punt and make a subsignal - // device to mux the bit in the net. This is a fairly - // complicated task because we need to generate - // expressions to convert calculated bit select - // values to canonical values that are used internally. - if (msb_) { - NetExpr*ex = msb_->elaborate_expr(des, scope); - - if (net->msb() < net->lsb()) { - ex = make_sub_expr(net->lsb(), ex); - } else { - ex = make_add_expr(ex, - net->lsb()); - } - - NetESelect*ss = new NetESelect(node, ex, 1); - ss->set_line(*this); - return ss; - } - - // All else fails, return the signal itself as the - // expression. - assert(msb_ == 0); - return node; - } // If the identifier names a memory, then this is a // memory reference and I must generate a NetEMemory @@ -915,6 +767,291 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des, return tmp; } +/* + * Handle part selects of NetNet identifiers. + */ +NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, + NetNet*net, NetScope*found_in)const +{ + assert(lsb_ != 0); + assert(msb_ != 0); + assert(idx_ == 0); + + verinum*lsn = lsb_->eval_const(des, scope); + verinum*msn = msb_->eval_const(des, scope); + if ((lsn == 0) || (msn == 0)) { + cerr << get_line() << ": error: " + "Part select expressions must be " + "constant expressions." << endl; + des->errors += 1; + return 0; + } + + assert(lsn); + assert(msn); + + /* The indices of part selects are signed integers, so allow + negative values. However, the width that they represent is + unsigned. Remember that any order is possible, + i.e., [1:0], [-4:6], etc. */ + + long lsv = lsn->as_long(); + long msv = msn->as_long(); + unsigned long wid = 1 + ((msv>lsv)? (msv-lsv) : (lsv-msv)); + if (wid > net->vector_width()) { + cerr << get_line() << ": error: part select [" + << msv << ":" << lsv << "] out of range." << endl; + des->errors += 1; + delete lsn; + delete msn; + return 0; + } + assert(wid <= net->vector_width()); + + if (net->sb_to_idx(msv) < net->sb_to_idx(lsv)) { + cerr << get_line() << ": error: part select [" + << msv << ":" << lsv << "] out of order." << endl; + des->errors += 1; + delete lsn; + delete msn; + return 0; + } + + + if (net->sb_to_idx(msv) >= net->vector_width()) { + cerr << get_line() << ": error: part select [" + << msv << ":" << lsv << "] out of range." << endl; + des->errors += 1; + delete lsn; + delete msn; + return 0; + } + + NetESignal*tmp = new NetESignal(net); + tmp->set_line(*this); + + // If the part select convers exactly the entire + // vector, then do not bother with it. Return the + // signal itself. + if (net->sb_to_idx(lsv) == 0 && wid == net->vector_width()) + return tmp; + + NetExpr*ex = new NetEConst(verinum(net->sb_to_idx(lsv))); + NetESelect*ss = new NetESelect(tmp, ex, wid); + ss->set_line(*this); + + return ss; +} + +/* + * Part select indexed up, i.e. net[ +: ] + */ +NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, + NetNet*net, NetScope*found_in)const +{ + assert(lsb_ != 0); + assert(msb_ != 0); + assert(idx_ == 0); + + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + + NetExpr*base = elab_and_eval(des, scope, msb_); + + NetExpr*wid_e = elab_and_eval(des, scope, lsb_); + NetEConst*wid_c = dynamic_cast (wid_e); + if (wid_c == 0) { + cerr << get_line() << ": error: Width of indexed part select " + << "must be constant." << endl; + cerr << get_line() << ": : Width expression is: " + << *wid_e << endl; + des->errors += 1; + return 0; + } + + assert(wid_c != 0); + long wid = wid_c->value().as_long(); + + // Handle the special case that the base is constant as + // well. In this case it can be converted to a conventional + // part select. + if (NetEConst*base_c = dynamic_cast (base)) { + long lsv = base_c->value().as_long(); + + // If the part select convers exactly the entire + // vector, then do not bother with it. Return the + // signal itself. + if (net->sb_to_idx(lsv) == 0 && wid == net->vector_width()) + return sig; + } + + NetESelect*ss = new NetESelect(sig, base, wid); + ss->set_line(*this); + + if (debug_elaborate) { + cerr << get_line() << ": debug: Elaborate part " + << "select base="<< *base << ", wid="<< wid << endl; + } + + return ss; +} + +/* + * Part select up, i.e. net[ +: ] + */ +NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, + NetNet*net, NetScope*found_in)const +{ + assert(lsb_ != 0); + assert(msb_ != 0); + assert(idx_ == 0); + + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + + NetExpr*base = elab_and_eval(des, scope, msb_); + + NetExpr*wid_e = elab_and_eval(des, scope, lsb_); + NetEConst*wid_c = dynamic_cast (wid_e); + if (wid_c == 0) { + cerr << get_line() << ": error: Width of indexed part select " + << "must be constant." << endl; + cerr << get_line() << ": : Width expression is: " + << *wid_e << endl; + des->errors += 1; + return 0; + } + + assert(wid_c != 0); + long wid = wid_c->value().as_long(); + + // Handle the special case that the base is constant as + // well. In this case it can be converted to a conventional + // part select. + if (NetEConst*base_c = dynamic_cast (base)) { + long lsv = base_c->value().as_long(); + + // If the part select convers exactly the entire + // vector, then do not bother with it. Return the + // signal itself. + if (net->sb_to_idx(lsv) == (wid-1) && wid == net->vector_width()) + return sig; + } + + NetExpr*base_adjusted = wid > 1? make_add_expr(base,1-wid) : base; + NetESelect*ss = new NetESelect(sig, base_adjusted, wid); + ss->set_line(*this); + + if (debug_elaborate) { + cerr << get_line() << ": debug: Elaborate part " + << "select base="<< *base << ", wid="<< wid << endl; + } + + return ss; +} + +NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, + NetNet*net, NetScope*found_in) const +{ + assert(msb_ != 0); + assert(lsb_ == 0); + assert(idx_ == 0); + + // If the bit select is constant, then treat it similar + // to the part select, so that I save the effort of + // making a mux part in the netlist. + if (verinum*msn = msb_->eval_const(des, scope)) { + long msv = msn->as_long(); + unsigned idx = net->sb_to_idx(msv); + + if (idx >= net->vector_width()) { + /* The bit select is out of range of the + vector. This is legal, but returns a + constant 1'bx value. */ + verinum x (verinum::Vx); + NetEConst*tmp = new NetEConst(x); + tmp->set_line(*this); + + cerr << get_line() << ": warning: Bit select [" + << msv << "] out of range of vector " + << net->name() << "[" << net->msb() + << ":" << net->lsb() << "]." << endl; + cerr << get_line() << ": : Replacing " + << "expression with a constant 1'bx." << endl; + delete msn; + return tmp; + } + + NetESignal*tmp = new NetESignal(net); + tmp->set_line(*this); + // If the vector is only one bit, we are done. The + // bit select will return the scaler itself. + if (net->vector_width() == 1) + return tmp; + + // Make an expression out of the index + NetEConst*idx_c = new NetEConst(verinum(idx)); + idx_c->set_line(*net); + + // Make a bit select with the canonical index + NetESelect*res = new NetESelect(tmp, idx_c, 1); + res->set_line(*net); + + return res; + } + + NetESignal*node = new NetESignal(net); + + // Non-constant bit select? punt and make a subsignal + // device to mux the bit in the net. This is a fairly + // complicated task because we need to generate + // expressions to convert calculated bit select + // values to canonical values that are used internally. + NetExpr*ex = msb_->elaborate_expr(des, scope); + + if (net->msb() < net->lsb()) { + ex = make_sub_expr(net->lsb(), ex); + } else { + ex = make_add_expr(ex, - net->lsb()); + } + + NetESelect*ss = new NetESelect(node, ex, 1); + ss->set_line(*this); + return ss; +} + +NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, + NetNet*net, NetScope*found_in) const +{ + // If this is a part select of a signal, then make a new + // temporary signal that is connected to just the + // selected bits. The lsb_ and msb_ expressions are from + // the foo[msb:lsb] expression in the original. + if (sel_ == SEL_PART) + return elaborate_expr_net_part_(des, scope, net, found_in); + + if (sel_ == SEL_BIT) + return elaborate_expr_net_bit_(des, scope, net, found_in); + + if (sel_ == SEL_IDX_UP) + return elaborate_expr_net_idx_up_(des, scope, net, found_in); + + if (sel_ == SEL_IDX_DO) + return elaborate_expr_net_idx_do_(des, scope, net, found_in); + + // It's not anything else, so this must be a simple identifier + // expression with no part or bit select. Return the signal + // itself as the expression. + assert(sel_ == SEL_NONE); + assert(msb_ == 0); + assert(lsb_ == 0); + assert(idx_ == 0); + + NetESignal*node = new NetESignal(net); + node->set_line(*this); + return node; +} + NetEConst* PENumber::elaborate_expr(Design*des, NetScope*, bool) const { assert(value_); @@ -1091,6 +1228,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, bool) const /* * $Log: elab_expr.cc,v $ + * Revision 1.99 2005/11/10 13:28:11 steve + * Reorganize signal part select handling, and add support for + * indexed part selects. + * + * Expand expression constant propagation to eliminate extra + * sums in certain cases. + * * Revision 1.98 2005/10/04 04:09:25 steve * Add support for indexed select attached to parameters. * diff --git a/elaborate.cc b/elaborate.cc index 3cfcc11da3..3fb7249ff3 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: elaborate.cc,v 1.330 2005/09/27 04:51:37 steve Exp $" +#ident "$Id: elaborate.cc,v 1.331 2005/11/10 13:28:11 steve Exp $" #endif # include "config.h" @@ -587,7 +587,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (debug_elaborate) { cerr << get_line() << ": debug: Instantiate module " - << rmod->mod_name() << " With instance name " + << rmod->mod_name() << " with instance name " << get_name() << " in scope " << scope->name() << endl; } @@ -3028,6 +3028,13 @@ Design* elaborate(listroots) /* * $Log: elaborate.cc,v $ + * Revision 1.331 2005/11/10 13:28:11 steve + * Reorganize signal part select handling, and add support for + * indexed part selects. + * + * Expand expression constant propagation to eliminate extra + * sums in certain cases. + * * Revision 1.330 2005/09/27 04:51:37 steve * Error message for invalid for-loop index variable. * diff --git a/eval_tree.cc b/eval_tree.cc index 5c98815d36..d2ed0cd027 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: eval_tree.cc,v 1.65 2005/09/14 02:53:14 steve Exp $" +#ident "$Id: eval_tree.cc,v 1.66 2005/11/10 13:28:12 steve Exp $" #endif # include "config.h" @@ -55,26 +55,64 @@ NetEConst* NetEBAdd::eval_tree() { eval_sub_tree_(); NetEConst*lc = dynamic_cast(left_); - if (lc == 0) return 0; NetEConst*rc = dynamic_cast(right_); - if (rc == 0) return 0; - verinum lval = lc->value(); - verinum rval = rc->value(); + /* If both operands are constant, then replace the entire + expression with a constant value. */ + if (lc != 0 && rc != 0) { + verinum lval = lc->value(); + verinum rval = rc->value(); - verinum val; - switch (op_) { - case '+': - val = lval + rval; - break; - case '-': - val = lval - rval; - break; - default: + verinum val; + switch (op_) { + case '+': + val = lval + rval; + break; + case '-': + val = lval - rval; + break; + default: + return 0; + } + + return new NetEConst(val); + } + + /* Try to combine a right constant value with the right + constant value of a sub-expression add. For example, the + expression (a + 2) - 1 can be rewritten as a + 1. */ + + NetEBAdd*se = dynamic_cast(left_); + lc = se? dynamic_cast(se->right_) : 0; + + if (lc != 0 && rc != 0) { + assert(se != 0); + verinum lval = lc->value(); + verinum rval = rc->value(); + + verinum val; + if (op_ == se->op_) { + /* (a + lval) + rval --> a + (rval+lval) */ + /* (a - lval) - rval --> a - (rval+lval) */ + val = rval + lval; + } else { + /* (a - lval) + rval --> a + (rval-lval) */ + /* (a + lval) - rval --> a - (rval-lval) */ + val = rval - lval; + } + + NetEConst*tmp = new NetEConst(val); + left_ = se->left_->dup_expr(); + delete se; + delete right_; + right_ = tmp; + /* We've changed the subexpression, but the result is + still not constant, so return nil here anyhow. */ return 0; } - return new NetEConst(val); + /* Nothing more to be done, the value is not constant. */ + return 0; } NetEConst* NetEBBits::eval_tree() @@ -1561,6 +1599,13 @@ NetEConst* NetEUReduce::eval_tree() /* * $Log: eval_tree.cc,v $ + * Revision 1.66 2005/11/10 13:28:12 steve + * Reorganize signal part select handling, and add support for + * indexed part selects. + * + * Expand expression constant propagation to eliminate extra + * sums in certain cases. + * * Revision 1.65 2005/09/14 02:53:14 steve * Support bool expressions and compares handle them optimally. * diff --git a/parse.y b/parse.y index 8f1127a9da..d051b5073b 100644 --- a/parse.y +++ b/parse.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2004 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2005 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 @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: parse.y,v 1.206 2005/10/04 04:09:25 steve Exp $" +#ident "$Id: parse.y,v 1.207 2005/11/10 13:28:12 steve Exp $" #endif # include "config.h" @@ -985,6 +985,7 @@ expr_primary tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); tmp->msb_ = $3; + tmp->sel_ = PEIdent::SEL_BIT; delete $1; $$ = tmp; } @@ -1455,6 +1456,7 @@ lavalue } else { tmp->msb_ = sel; } + tmp->sel_ = PEIdent::SEL_BIT; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; @@ -1465,6 +1467,7 @@ lavalue assert($2->count() == 2); tmp->msb_ = (*$2)[0]; tmp->lsb_ = (*$2)[1]; + tmp->sel_ = PEIdent::SEL_PART; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1;