diff --git a/PExpr.h b/PExpr.h index 04f7c1265..510963246 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.66 2004/10/04 01:10:51 steve Exp $" +#ident "$Id: PExpr.h,v 1.67 2004/12/29 23:55:43 steve Exp $" #endif # include @@ -83,8 +83,13 @@ class PExpr : public LineInfo { bool implicit_net_ok =false) const; // Expressions that can be in the l-value of procedural - // assignments can be elaborated with this method. - virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope) const; + // assignments can be elaborated with this method. If the + // is_force flag is true, then the set of valid l-value types + // is slightly modified to accomodate the Verilog force + // statement + virtual NetAssign_* elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const; // This attempts to evaluate a constant expression, and return // a verinum as a result. If the expression cannot be @@ -133,7 +138,9 @@ class PEConcat : public PExpr { virtual NetExpr*elaborate_expr(Design*des, NetScope*, bool sys_task_arg =false) const; virtual NetEConcat*elaborate_pexpr(Design*des, NetScope*) const; - virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope) const; + virtual NetAssign_* elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const; virtual bool is_constant(Module*) const; private: @@ -213,7 +220,9 @@ class PEIdent : public PExpr { bool implicit_net_ok =false) const; // Identifiers are also allowed as procedural assignment l-values. - virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope) const; + virtual NetAssign_* elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const; // Structural r-values are OK. virtual NetNet* elaborate_net(Design*des, NetScope*scope, @@ -288,7 +297,9 @@ class PENumber : public PExpr { virtual NetEConst*elaborate_expr(Design*des, NetScope*, bool sys_task_arg =false) const; virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const; - virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope) const; + virtual NetAssign_* elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const; virtual verinum* eval_const(const Design*des, const NetScope*sc) const; @@ -492,6 +503,14 @@ class PECallFunction : public PExpr { /* * $Log: PExpr.h,v $ + * Revision 1.67 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.66 2004/10/04 01:10:51 steve * Clean up spurious trailing white space. * diff --git a/design_dump.cc b/design_dump.cc index fc9607abe..c1912ac8e 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: design_dump.cc,v 1.150 2004/12/11 02:31:25 steve Exp $" +#ident "$Id: design_dump.cc,v 1.151 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -213,6 +213,16 @@ void NetCompare::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetConcat::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "NetConcat: " + << name() + << " scope=" << (scope()? scope()->name() : "") + << " width=" << width_ << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetDivide::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "NET_DIVIDE (NetDivide): " << name() << endl; @@ -1087,6 +1097,14 @@ void Design::dump(ostream&o) const /* * $Log: design_dump.cc,v $ + * Revision 1.151 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.150 2004/12/11 02:31:25 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/elab_lval.cc b/elab_lval.cc index 86092737d..7ef2b76ba 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: elab_lval.cc,v 1.30 2004/12/11 02:31:25 steve Exp $" +#ident "$Id: elab_lval.cc,v 1.31 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -66,7 +66,9 @@ * is to try to make a net elaboration, and see if the result is * suitable for assignment. */ -NetAssign_* PExpr::elaborate_lval(Design*des, NetScope*scope) const +NetAssign_* PExpr::elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const { NetNet*ll = 0; if (ll == 0) { @@ -92,7 +94,9 @@ NetAssign_* PExpr::elaborate_lval(Design*des, NetScope*scope) const * a is the MSB and b the LSB. Connect the LSB to the low pins of the * NetAssign_ object. */ -NetAssign_* PEConcat::elaborate_lval(Design*des, NetScope*scope) const +NetAssign_* PEConcat::elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const { if (repeat_) { cerr << get_line() << ": error: Repeat concatenations make " @@ -112,7 +116,7 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, NetScope*scope) const continue; } - NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope); + NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope, is_force); /* If the l-value doesn't elaborate, the error was already detected and printed. We just skip it and let @@ -140,7 +144,9 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, NetScope*scope) const * Handle the ident as an l-value. This includes bit and part selects * of that ident. */ -NetAssign_* PEIdent::elaborate_lval(Design*des, NetScope*scope) const +NetAssign_* PEIdent::elaborate_lval(Design*des, + NetScope*scope, + bool is_force) const { NetNet* reg = 0; NetMemory* mem = 0; @@ -151,6 +157,15 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, NetScope*scope) const symbol_search(des, scope, path_, reg, mem, var, par, eve); if (mem) { + if (is_force) { + cerr << get_line() << ": error: Memories " + << "(" << path_ << " in this case)" + << " are not allowed" + << " as l-values to force statements." << endl; + des->errors += 1; + return 0; + } + return elaborate_mem_lval_(des, scope, mem); } @@ -171,8 +186,9 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, NetScope*scope) const assert(reg); /* Get the signal referenced by the identifier, and make sure - it is a register. (Wires are not allows in this context. */ - if (reg->type() != NetNet::REG) { + it is a register. Wires are not allows in this context, + unless this is the l-value of a force. */ + if ((reg->type() != NetNet::REG) && !is_force) { cerr << get_line() << ": error: " << path_ << " is not a reg/integer/time in " << scope->name() << "." << endl; @@ -340,7 +356,7 @@ NetAssign_* PEIdent::elaborate_mem_lval_(Design*des, NetScope*scope, return lv; } -NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*) const +NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool) const { cerr << get_line() << ": error: Constant values not allowed " << "in l-value expressions." << endl; @@ -350,6 +366,14 @@ NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*) const /* * $Log: elab_lval.cc,v $ + * Revision 1.31 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.30 2004/12/11 02:31:25 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes @@ -363,95 +387,5 @@ NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*) const * * Revision 1.27 2003/09/19 03:30:05 steve * Fix name search in elab_lval. - * - * Revision 1.26 2003/01/27 05:09:17 steve - * Spelling fixes. - * - * Revision 1.25 2003/01/26 21:15:58 steve - * Rework expression parsing and elaboration to - * accommodate real/realtime values and expressions. - * - * Revision 1.24 2003/01/19 00:35:39 steve - * Detect null arguments to concatenation operator. - * - * Revision 1.23 2002/11/21 23:27:51 steve - * Precalculate indices to l-value arrays. - * - * Revision 1.22 2002/11/21 18:15:40 steve - * Fix const test of msb in assignment l-values. - * - * Revision 1.21 2002/11/02 01:10:49 steve - * Detect memories without work index in l-value. - * - * Revision 1.20 2002/08/12 01:34:58 steve - * conditional ident string using autoconfig. - * - * Revision 1.19 2002/06/04 05:38:44 steve - * Add support for memory words in l-value of - * blocking assignments, and remove the special - * NetAssignMem class. - * - * Revision 1.18 2002/03/09 04:02:26 steve - * Constant expressions are not l-values for task ports. - * - * Revision 1.17 2001/12/03 04:47:14 steve - * Parser and pform use hierarchical names as hname_t - * objects instead of encoded strings. - * - * Revision 1.16 2001/11/08 05:15:50 steve - * Remove string paths from PExpr elaboration. - * - * Revision 1.15 2001/11/07 04:01:59 steve - * eval_const uses scope instead of a string path. - * - * Revision 1.14 2001/08/25 23:50:02 steve - * Change the NetAssign_ class to refer to the signal - * instead of link into the netlist. This is faster - * and uses less space. Make the NetAssignNB carry - * the delays instead of the NetAssign_ lval objects. - * - * Change the vvp code generator to support multiple - * l-values, i.e. concatenations of part selects. - * - * Revision 1.13 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.12 2001/02/09 03:16:48 steve - * Report bit/part select out of range errors. (PR#133) - * - * Revision 1.11 2001/01/10 03:13:23 steve - * Build task outputs as lval instead of nets. (PR#98) - * - * Revision 1.10 2001/01/06 06:31:58 steve - * declaration initialization for time variables. - * - * Revision 1.9 2001/01/06 02:29:36 steve - * Support arrays of integers. - * - * Revision 1.8 2000/12/12 06:14:51 steve - * sorry for concatenated memories in l-values. (PR#76) - * - * Revision 1.7 2000/12/01 02:55:37 steve - * Detect part select errors on l-values. - * - * Revision 1.6 2000/10/31 17:49:02 steve - * Support time variables. - * - * Revision 1.5 2000/10/26 17:09:46 steve - * Fix handling of errors in behavioral lvalues. (PR#28) - * - * Revision 1.4 2000/09/10 15:43:59 steve - * Some error checking. - * - * Revision 1.3 2000/09/10 03:59:59 steve - * Agressively merge NetAssign_ within concatenations. - * - * Revision 1.2 2000/09/10 02:18:16 steve - * elaborate complex l-values - * - * Revision 1.1 2000/09/09 15:21:26 steve - * move lval elaboration to PExpr virtual methods. - * */ diff --git a/elaborate.cc b/elaborate.cc index 10ded5e49..e013346b5 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.311 2004/12/15 17:09:11 steve Exp $" +#ident "$Id: elaborate.cc,v 1.312 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -246,6 +246,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const void PGBuiltin::elaborate(Design*des, NetScope*scope) const { unsigned count = 1; + unsigned instance_width = 1; long low = 0, high = 0; string name = string(get_name()); @@ -289,8 +290,33 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const low = lsb.as_long(); high = msb.as_long(); + + if (debug_elaborate) { + cerr << get_line() << ": debug: PGBuiltin: Make arrray " + << "[" << high << ":" << low << "]" + << " of " << count << " gates for " << name << endl; + } } + /* Now we have a gate count. Elaborate the output expression + only. We do it early so that we can see if we can make a + wide gate instead of an array of gates. */ + + NetNet*lval_sig = pin(0)->elaborate_lnet(des, scope, true); + assert(lval_sig); + + /* Detect the special case that the l-value width exactly + matches the gate count. In this case, we will make a single + gate that has the desired vector width. */ + if (lval_sig->vector_width() == (long)count) { + instance_width = count; + count = 1; + + if (debug_elaborate) + cerr << get_line() << ": debug: PGBuiltin: " + "Collapsed gate array into single wide " + "(" << instance_width << ") instance." << endl; + } /* Allocate all the netlist nodes for the gates. */ NetLogic**cur = new NetLogic*[count]; @@ -334,75 +360,75 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const switch (type()) { case AND: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::AND, 1); + NetLogic::AND, instance_width); break; case BUF: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::BUF, 1); + NetLogic::BUF, instance_width); break; case BUFIF0: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::BUFIF0, 1); + NetLogic::BUFIF0, instance_width); break; case BUFIF1: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::BUFIF1, 1); + NetLogic::BUFIF1, instance_width); break; case NAND: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NAND, 1); + NetLogic::NAND, instance_width); break; case NMOS: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NMOS, 1); + NetLogic::NMOS, instance_width); break; case NOR: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NOR, 1); + NetLogic::NOR, instance_width); break; case NOT: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NOT, 1); + NetLogic::NOT, instance_width); break; case NOTIF0: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NOTIF0, 1); + NetLogic::NOTIF0, instance_width); break; case NOTIF1: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::NOTIF1, 1); + NetLogic::NOTIF1, instance_width); break; case OR: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::OR, 1); + NetLogic::OR, instance_width); break; case RNMOS: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::RNMOS, 1); + NetLogic::RNMOS, instance_width); break; case RPMOS: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::RPMOS, 1); + NetLogic::RPMOS, instance_width); break; case PMOS: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::PMOS, 1); + NetLogic::PMOS, instance_width); break; case PULLDOWN: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::PULLDOWN, 1); + NetLogic::PULLDOWN, instance_width); break; case PULLUP: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::PULLUP, 1); + NetLogic::PULLUP, instance_width); break; case XNOR: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::XNOR, 1); + NetLogic::XNOR, instance_width); break; case XOR: cur[idx] = new NetLogic(scope, inm, pin_count(), - NetLogic::XOR, 1); + NetLogic::XOR, instance_width); break; default: cerr << get_line() << ": internal error: unhandled " @@ -434,25 +460,67 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { const PExpr*ex = pin(idx); NetNet*sig = (idx == 0) - ? ex->elaborate_lnet(des, scope, true) + ? lval_sig : ex->elaborate_net(des, scope, 0, 0, 0, 0); if (sig == 0) continue; assert(sig); - if (sig->pin_count() == 1) + if (count == 1) { + /* Handle the case where there is one gate that + carries the whole vector width. */ + connect(cur[0]->pin(idx), sig->pin(0)); + + } else if (sig->vector_width() == 1) { + /* Handle the case where a single bit is connected + repetitively to all the instances. */ for (unsigned gdx = 0 ; gdx < count ; gdx += 1) connect(cur[gdx]->pin(idx), sig->pin(0)); - else if (sig->pin_count() == count) - for (unsigned gdx = 0 ; gdx < count ; gdx += 1) - connect(cur[gdx]->pin(idx), sig->pin(gdx)); + } else if (sig->vector_width() == (long)count) { - else { + /* Handle the general case that each bit of the + value is connected to a different instance. In + this case, the output is handled slightly + different from the inputs. */ + if (idx == 0) { + NetConcat*cc = new NetConcat(scope, + scope->local_symbol(), + sig->vector_width(), + count); + des->add_node(cc); + + /* Connect the concat to the signal. */ + connect(cc->pin(0), sig->pin(0)); + + /* Connect the outputs of the gates to the concat. */ + for (unsigned gdx = 0 ; gdx < count ; gdx += 1) { + connect(cur[gdx]->pin(0), cc->pin(gdx+1)); + + NetNet*tmp2 = new NetNet(scope, + scope->local_symbol(), + NetNet::WIRE, 1); + connect(cc->pin(gdx+1), tmp2->pin(0)); + } + + } else for (unsigned gdx = 0 ; gdx < count ; gdx += 1) { + /* Use part selects to get the bits + connected to the inputs of out gate. */ + NetPartSelect*tmp1 = new NetPartSelect(sig, gdx, 1); + tmp1->set_line(*this); + des->add_node(tmp1); + connect(tmp1->pin(1), sig->pin(0)); + NetNet*tmp2 = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, 1); + connect(tmp1->pin(0), tmp2->pin(0)); + connect(cur[gdx]->pin(idx), tmp1->pin(0)); + } + + } else { cerr << get_line() << ": error: Gate count of " << count << " does not match net width of " << - sig->pin_count() << " at pin " << idx << "." + sig->vector_width() << " at pin " << idx << "." << endl; des->errors += 1; } @@ -1031,7 +1099,7 @@ NetProc* Statement::elaborate(Design*des, NetScope*) const NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const { assert(lval_); - return lval_->elaborate_lval(des, scope); + return lval_->elaborate_lval(des, scope, false); } /* @@ -1667,7 +1735,7 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const detailed message. */ NetAssign_*lv; if (parms_[idx]) { - lv = parms_[idx]->elaborate_lval(des, scope); + lv = parms_[idx]->elaborate_lval(des, scope, false); if (lv == 0) { cerr << parms_[idx]->get_line() << ": error: " << "I give up on task port " << (idx+1) @@ -1702,7 +1770,7 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const NetCAssign*dev = 0; assert(scope); - NetAssign_*lval = lval_->elaborate_lval(des, scope); + NetAssign_*lval = lval_->elaborate_lval(des, scope, false); if (lval == 0) return 0; @@ -1733,7 +1801,7 @@ NetDeassign* PDeassign::elaborate(Design*des, NetScope*scope) const { assert(scope); - NetAssign_*lval = lval_->elaborate_lval(des, scope); + NetAssign_*lval = lval_->elaborate_lval(des, scope, false); if (lval == 0) return 0; @@ -2216,12 +2284,22 @@ NetProc* PForever::elaborate(Design*des, NetScope*scope) const return proc; } +/* + * Force is like a procedural assignment, most notably prodedural + * continuous assignment: + * + * force = + * + * The can be anything that a normal behavioral assignment can + * take, plus net signals. This is a little bit more lax then the + * other proceedural assignments. + */ NetForce* PForce::elaborate(Design*des, NetScope*scope) const { NetForce*dev = 0; assert(scope); - NetAssign_*lval = lval_->elaborate_lval(des, scope); + NetAssign_*lval = lval_->elaborate_lval(des, scope, true); if (lval == 0) return 0; @@ -2396,7 +2474,7 @@ NetProc* PRelease::elaborate(Design*des, NetScope*scope) const { assert(scope); - NetAssign_*lval = lval_->elaborate_lval(des, scope); + NetAssign_*lval = lval_->elaborate_lval(des, scope, true); if (lval == 0) return 0; @@ -2767,6 +2845,14 @@ Design* elaborate(listroots) /* * $Log: elaborate.cc,v $ + * Revision 1.312 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.311 2004/12/15 17:09:11 steve * Force r-value padded to width. * diff --git a/emit.cc b/emit.cc index 50a92d59c..0456a65f2 100644 --- a/emit.cc +++ b/emit.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: emit.cc,v 1.78 2004/12/11 02:31:26 steve Exp $" +#ident "$Id: emit.cc,v 1.79 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -75,6 +75,11 @@ bool NetCompare::emit_node(struct target_t*tgt) const return true; } +bool NetConcat::emit_node(struct target_t*tgt) const +{ + return tgt->concat(this); +} + bool NetConst::emit_node(struct target_t*tgt) const { return tgt->net_const(this); @@ -504,6 +509,14 @@ bool emit(const Design*des, const char*type) /* * $Log: emit.cc,v $ + * Revision 1.79 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.78 2004/12/11 02:31:26 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/ivl_target.h b/ivl_target.h index 3f2d46b7d..bad0cfbf0 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: ivl_target.h,v 1.129 2004/12/18 18:56:18 steve Exp $" +#ident "$Id: ivl_target.h,v 1.130 2004/12/29 23:55:43 steve Exp $" #endif #ifdef __cplusplus @@ -222,6 +222,7 @@ typedef enum ivl_logic_e { /* This is the type of an LPM object. */ typedef enum ivl_lpm_type_e { IVL_LPM_ADD = 0, + IVL_LPM_CONCAT = 16, IVL_LPM_CMP_EQ = 10, IVL_LPM_CMP_GE = 1, IVL_LPM_CMP_GT = 2, @@ -627,6 +628,12 @@ extern ivl_memory_t ivl_expr_memory(ivl_expr_t net); * These support iterating over logic attributes. The _cnt method * returns the number of attributes attached to the gate, and the * ivl_logic_attr_val returns the value of the attribute. + * + * SEMANTIC NOTES + * The ivl_logic_width and ivl_logic_pins are *not* related. A logic + * device has a number of pins that is the number of inputs to a logic + * array of identical gates, and the ivl_logic_width, is the width of + * the vector into each input pin and out of the output pin. */ extern const char* ivl_logic_name(ivl_net_logic_t net); @@ -637,6 +644,7 @@ extern ivl_nexus_t ivl_logic_pin(ivl_net_logic_t net, unsigned pin); extern unsigned ivl_logic_pins(ivl_net_logic_t net); extern ivl_udp_t ivl_logic_udp(ivl_net_logic_t net); extern unsigned ivl_logic_delay(ivl_net_logic_t net, unsigned transition); +extern unsigned ivl_logic_width(ivl_net_logic_t net); /* DEPRECATED */ extern const char* ivl_logic_attr(ivl_net_logic_t net, const char*key); @@ -659,7 +667,8 @@ extern const char* ivl_udp_name(ivl_udp_t net); /* LPM * These functions support access to the properties of LPM * devices. LPM devices are a variety of devices that handle more - * complex structural semantics. + * complex structural semantics. They are based on EIA LPM standard + * devices, but vary to suite the technical situation. * * These are the functions that apply to all LPM devices: * @@ -689,13 +698,12 @@ extern const char* ivl_udp_name(ivl_udp_t net); * of the part select. The ivl_lpm_width is the size of the part. * * ivl_lpm_data - * Return the input data nexus for device types that have a single - * input vector. This is also used to the get nexa of the first - * vector for devices that have more inputs. + * Return the input data nexus for device types that have input + * vectors. The "idx" parameter selects which data input is selected. * - * ivl_lpm_datab - * Return the input data nexus for device types that have a second - * input vector. For example, arithmetic devices are like this. + * ivl_lpm_datab (ANACHRONISM) + * This is the same as ivl_lpm_data(net,1), in other words the + * second data input. Use the ivl_lpm_data method instead. * * ivl_lpm_q * Return the output data nexus for device types that have a single @@ -713,6 +721,18 @@ extern const char* ivl_udp_name(ivl_udp_t net); * In addition to a width, some devices have a size. The size is * often the number of inputs per out, i.e., the number of inputs * per bit for a MUX. + * + * SEMANTIC NOTES + * + * - Concatenation (IVL_LPM_CONCAT) + * These devices take vectors in and combine them to form a single + * output the width specified by ivl_lpm_width. + * + * The ivl_lpm_q nexus is the output from the concatenation. + * + * The ivl_lpm_data function returns the connections for the inputs to + * the concatentation. The ivl_lpm_size function returns the number of + * inputs help by the device. */ extern const char* ivl_lpm_name(ivl_lpm_t net); /* (Obsolete) */ @@ -737,8 +757,8 @@ extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); /* IVL_LPM_FF IVL_LPM_RAM */ extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); - /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT IVL_LPM_RAM - IVL_LPM_SUB */ + /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT + IVL_LPM_RAM IVL_LPM_SUB */ extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_SUB */ /* IVL_LPM_MUX IVL_LPM_UFUNC */ @@ -753,7 +773,7 @@ extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx); extern unsigned ivl_lpm_selects(ivl_lpm_t net); /* IVL_LPM_MUX IVL_LPM_RAM */ extern ivl_nexus_t ivl_lpm_select(ivl_lpm_t net, unsigned idx); - /* IVL_LPM_MUX */ + /* IVL_LPM_CONCAT IVL_LPM_MUX */ extern unsigned ivl_lpm_size(ivl_lpm_t net); /* IVL_LPM_RAM */ extern ivl_memory_t ivl_lpm_memory(ivl_lpm_t net); @@ -1260,7 +1280,9 @@ extern ivl_statement_type_t ivl_statement_type(ivl_statement_t net); * * - IVL_ST_FORCE * This is very much like IVL_ST_CASSIGN, but adds that l-values can - * include nets (tri, wire, etc). + * include nets (tri, wire, etc). Memory words are restricted from + * force l-values, and also non-constant bit or part selects. The + * compiler will assure these constraints are met. * * - IVL_ST_TRIGGER * This represents the "-> name" statement that sends a trigger to a @@ -1371,6 +1393,14 @@ _END_DECL /* * $Log: ivl_target.h,v $ + * Revision 1.130 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.129 2004/12/18 18:56:18 steve * Add ivl_event_scope, and better document ivl_event_X methods. * diff --git a/netlist.cc b/netlist.cc index 75941f77f..edaa588d7 100644 --- a/netlist.cc +++ b/netlist.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: netlist.cc,v 1.227 2004/12/11 02:31:26 steve Exp $" +#ident "$Id: netlist.cc,v 1.228 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -532,6 +532,26 @@ const NetScope* NetProcTop::scope() const return scope_; } +NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt) +: NetNode(scope, n, cnt+1), width_(wid) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + for (unsigned idx = 1 ; idx < cnt+1 ; idx += 1) { + pin(idx).set_dir(Link::INPUT); + pin(idx).set_name(perm_string::literal("I"), idx-1); + } +} + +NetConcat::~NetConcat() +{ +} + +unsigned NetConcat::width() const +{ + return width_; +} + /* * The NetFF class represents an LPM_FF device. The pinout is assigned * like so: @@ -2306,6 +2326,14 @@ const NetProc*NetTaskDef::proc() const /* * $Log: netlist.cc,v $ + * Revision 1.228 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.227 2004/12/11 02:31:26 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/netlist.h b/netlist.h index 62417ae8a..0b8f474be 100644 --- a/netlist.h +++ b/netlist.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: netlist.h,v 1.322 2004/12/11 02:31:27 steve Exp $" +#ident "$Id: netlist.h,v 1.323 2004/12/29 23:55:43 steve Exp $" #endif /* @@ -582,6 +582,32 @@ class NetCompare : public NetNode { bool signed_flag_; }; + +/* + * This node is a means to connect net inputs together to form a wider + * vector. The output (pin 0) is a concatenation of the input vectors, + * with pin-1 at the LSB, pin-2 next, and so on. This node is most + * like the NetLogic node, as it has one output at pin 0 and the + * remaining pins are the input that are combined to make the + * output. It is seperated out because it it generally a special case + * for the code generators. + */ +class NetConcat : public NetNode { + + public: + NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt); + ~NetConcat(); + + unsigned width() const; + + void dump_node(ostream&, unsigned ind) const; + bool emit_node(struct target_t*) const; + + private: + unsigned width_; +}; + + /* * This class represents a theoretical (though not necessarily * practical) integer divider gate. This is not to represent any real @@ -1242,6 +1268,11 @@ class NetConst : public NetNode { * The pullup and pulldown gates have no inputs at all, and pin0 is * the output 1 or 0, depending on the gate type. It is the strength * of that value that is important. + * + * All these devices process vectors bitwise, so each bit can be + * logically seperated. The exception is the CONCAT gate, which is + * really an abstract gate that takes the inputs and turns it into a + * vector of bits. */ class NetLogic : public NetNode { @@ -3349,6 +3380,14 @@ extern ostream& operator << (ostream&, NetNet::Type); /* * $Log: netlist.h,v $ + * Revision 1.323 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.322 2004/12/11 02:31:27 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/t-dll-api.cc b/t-dll-api.cc index cecb740e6..fda6ee180 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: t-dll-api.cc,v 1.111 2004/12/18 18:56:18 steve Exp $" +#ident "$Id: t-dll-api.cc,v 1.112 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -766,8 +766,11 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_SUB: - assert(idx == 0); - return net->u_.arith.a; + assert(idx <= 1); + if (idx == 0) + return net->u_.arith.a; + else + return net->u_.arith.b; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: @@ -782,6 +785,10 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) else return net->u_.ff.d.pins[idx]; + case IVL_LPM_CONCAT: + assert(idx < net->u_.concat.inputs); + return net->u_.concat.pins[idx+1]; + case IVL_LPM_PART: assert(idx == 0); return net->u_.part.a; @@ -794,6 +801,7 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) extern "C" ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx) { + cerr << "ANACHRONISM: Call to anachronistic ivl_lpm_datab." << endl; assert(net); switch (net->type) { @@ -928,6 +936,9 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) assert(idx < net->u_.ufunc.port_wid[0]); return net->u_.ufunc.pins[idx]; + case IVL_LPM_CONCAT: + return net->u_.concat.pins[0]; + case IVL_LPM_PART: assert(idx == 0); return net->u_.part.q; @@ -982,6 +993,8 @@ extern "C" unsigned ivl_lpm_selects(ivl_lpm_t net) case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: return net->u_.shift.select; + case IVL_LPM_CONCAT: + return net->u_.concat.inputs; default: assert(0); return 0; @@ -1012,6 +1025,8 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_UFUNC: return 0; + case IVL_LPM_CONCAT: // Concatenations are always unsigned + return 0; case IVL_LPM_PART: return net->u_.part.signed_flag; default: @@ -1062,6 +1077,8 @@ extern "C" unsigned ivl_lpm_width(ivl_lpm_t net) return net->u_.shift.width; case IVL_LPM_UFUNC: return net->u_.ufunc.port_wid[0]; + case IVL_LPM_CONCAT: + return net->u_.concat.width; case IVL_LPM_PART: return net->u_.part.width; default: @@ -1946,6 +1963,14 @@ extern "C" ivl_variable_type_t ivl_variable_type(ivl_variable_t net) /* * $Log: t-dll-api.cc,v $ + * Revision 1.112 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.111 2004/12/18 18:56:18 steve * Add ivl_event_scope, and better document ivl_event_X methods. * diff --git a/t-dll.cc b/t-dll.cc index 368f10b88..25e33c268 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: t-dll.cc,v 1.132 2004/12/11 02:31:28 steve Exp $" +#ident "$Id: t-dll.cc,v 1.133 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -1867,6 +1867,34 @@ void dll_target::lpm_mux(const NetMux*net) } +bool dll_target::concat(const NetConcat*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CONCAT; + obj->name = net->name(); // NetConcat names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->u_.concat.width = net->width(); + + obj->u_.concat.inputs = net->pin_count() - 1; + obj->u_.concat.pins = new ivl_nexus_t[obj->u_.concat.inputs+1]; + + for (unsigned idx = 0 ; idx < obj->u_.concat.inputs+1 ; idx += 1) { + ivl_drive_t dr = idx == 0? IVL_DR_STRONG : IVL_DR_HiZ; + const Nexus*nex = net->pin(idx).nexus(); + assert(nex->t_cookie()); + + obj->u_.concat.pins[idx] = (ivl_nexus_t) nex->t_cookie(); + nexus_lpm_add(obj->u_.concat.pins[idx], obj, 0, dr, dr); + } + + scope_add_lpm(obj->scope, obj); + + return true; +} + bool dll_target::part_select(const NetPartSelect*net) { ivl_lpm_t obj = new struct ivl_lpm_s; @@ -2179,6 +2207,14 @@ extern const struct target tgt_dll = { "dll", &dll_target_obj }; /* * $Log: t-dll.cc,v $ + * Revision 1.133 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.132 2004/12/11 02:31:28 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/t-dll.h b/t-dll.h index 664a30a0b..1bb529869 100644 --- a/t-dll.h +++ b/t-dll.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: t-dll.h,v 1.116 2004/12/11 02:31:28 steve Exp $" +#ident "$Id: t-dll.h,v 1.117 2004/12/29 23:55:43 steve Exp $" #endif # include "target.h" @@ -82,6 +82,7 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_mult(const NetMult*); void lpm_mux(const NetMux*); void lpm_ram_dq(const NetRamDq*); + bool concat(const NetConcat*); bool part_select(const NetPartSelect*); void net_assign(const NetAssign_*); bool net_function(const NetUserFunc*); @@ -347,6 +348,12 @@ struct ivl_lpm_s { ivl_nexus_t q, a, b; } arith; + struct ivl_concat_s { + unsigned width; + unsigned inputs; + ivl_nexus_t*pins; + } concat; + struct ivl_part_s { unsigned width; unsigned base; @@ -680,6 +687,14 @@ struct ivl_variable_s { /* * $Log: t-dll.h,v $ + * Revision 1.117 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.116 2004/12/11 02:31:28 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/target.cc b/target.cc index e318300e3..3308ffe41 100644 --- a/target.cc +++ b/target.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: target.cc,v 1.70 2004/12/11 02:31:28 steve Exp $" +#ident "$Id: target.cc,v 1.71 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -137,6 +137,13 @@ void target_t::lpm_ram_dq(const NetRamDq*) "Unhandled NetRamDq." << endl; } +bool target_t::concat(const NetConcat*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetConcat." << endl; + return false; +} + bool target_t::part_select(const NetPartSelect*) { cerr << "target (" << typeid(*this).name() << "): " @@ -412,6 +419,14 @@ void expr_scan_t::expr_binary(const NetEBinary*ex) /* * $Log: target.cc,v $ + * Revision 1.71 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.70 2004/12/11 02:31:28 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/target.h b/target.h index 22b6bcb85..6be0280e0 100644 --- a/target.h +++ b/target.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: target.h,v 1.66 2004/12/11 02:31:28 steve Exp $" +#ident "$Id: target.h,v 1.67 2004/12/29 23:55:43 steve Exp $" #endif # include "netlist.h" @@ -86,6 +86,7 @@ struct target_t { virtual void lpm_mux(const NetMux*); virtual void lpm_ram_dq(const NetRamDq*); + virtual bool concat(const NetConcat*); virtual bool part_select(const NetPartSelect*); /* Output a gate (called for each gate) */ @@ -170,6 +171,14 @@ extern const struct target *target_table[]; /* * $Log: target.h,v $ + * Revision 1.67 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.66 2004/12/11 02:31:28 steve * Rework of internals to carry vectors through nexus instead * of single bits. Make the ivl, tgt-vvp and vvp initial changes diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 62e57a8d9..32407e429 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: stub.c,v 1.93 2004/12/18 18:55:08 steve Exp $" +#ident "$Id: stub.c,v 1.94 2004/12/29 23:55:43 steve Exp $" #endif # include "config.h" @@ -179,6 +179,50 @@ void show_expression(ivl_expr_t net, unsigned ind) } } + +/* IVL_LPM_CONCAT + * The concat device takes N inputs (N=ivl_lpm_selects) and generates + * a single output. The total output is known from the ivl_lpm_width + * function. The widths of all the inputs are inferred from the widths + * of the signals connected to the nexus of the inputs. The compiler + * makes sure the input widths add up to the output width. + */ +static void show_lpm_concat(ivl_lpm_t net) +{ + unsigned idx; + + unsigned width_sum = 0; + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CONCAT %s: \n", + ivl_lpm_basename(net), width, ivl_lpm_selects(net)); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + + for (idx = 0 ; idx < ivl_lpm_selects(net) ; idx += 1) { + unsigned ndx; + unsigned signal_width = 0; + ivl_nexus_t nex = ivl_lpm_data(net, idx); + + for (ndx = 0 ; ndx < ivl_nexus_ptrs(nex) ; ndx += 1) { + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, ndx); + ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); + if (sig != 0) { + signal_width = ivl_signal_width(sig); + break; + } + } + + fprintf(out, " I%u: %s (width=%u)\n", idx, + ivl_nexus_name(nex), signal_width); + width_sum += signal_width; + } + + if (width_sum != width) { + fprintf(out, " ERROR! Got %u bits input, expecting %u!\n", + width_sum, width); + } +} + static void show_lpm(ivl_lpm_t net) { unsigned idx; @@ -235,6 +279,10 @@ static void show_lpm(ivl_lpm_t net) break; } + case IVL_LPM_CONCAT: + show_lpm_concat(net); + break; + case IVL_LPM_SHIFTL: { fprintf(out, " LPM_SHIFTL %s: \n", ivl_lpm_basename(net), width, ivl_lpm_selects(net), @@ -762,6 +810,14 @@ int target_design(ivl_design_t des) /* * $Log: stub.c,v $ + * Revision 1.94 2004/12/29 23:55:43 steve + * Unify elaboration of l-values for all proceedural assignments, + * including assing, cassign and force. + * + * Generate NetConcat devices for gate outputs that feed into a + * vector results. Use this to hande gate arrays. Also let gate + * arrays handle vectors of gates when the outputs allow for it. + * * Revision 1.93 2004/12/18 18:55:08 steve * Better detail on event trigger and wait statements. *