diff --git a/PExpr.h b/PExpr.h index 5b014de8a..556dd3491 100644 --- a/PExpr.h +++ b/PExpr.h @@ -366,6 +366,11 @@ class PEIdent : public PExpr { // only applies to Ident expressions. NetNet* elaborate_subport(Design*des, NetScope*sc) const; + // Elaborate the identifier allowing for unpacked arrays. This + // method only applies to Ident expressions because only Ident + // expressions can can be unpacked arrays. + NetNet* elaborate_unpacked_net(Design*des, NetScope*sc) const; + verinum* eval_const(Design*des, NetScope*sc) const; virtual bool is_collapsible_net(Design*des, NetScope*scope) const; diff --git a/PGate.h b/PGate.h index 85ea1194b..7b726fa68 100644 --- a/PGate.h +++ b/PGate.h @@ -124,6 +124,7 @@ class PGAssign : public PGate { virtual bool elaborate_sig(Design*des, NetScope*scope) const; private: + void elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const; }; diff --git a/elab_expr.cc b/elab_expr.cc index bee085511..0d9b82847 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -2585,6 +2585,7 @@ bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, { list index; index = path_.back().index; + ivl_assert(*this, index.size() >= net->unpacked_dimensions()); for (size_t idx = 0 ; idx < net->unpacked_dimensions() ; idx += 1) index.pop_front(); diff --git a/elab_net.cc b/elab_net.cc index 1cad8de9c..ad7adf138 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -501,6 +501,10 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, unsigned midx = sig->vector_width()-1, lidx = 0; // The default word select is the first. long widx = 0; + // Set this to true if we calculate the word index. This is + // used to distinguish between unpacked array assignment and + // array word assignment. + bool widx_flag = false; list unpacked_indices_const; @@ -590,13 +594,31 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, midx = lidx + tmp_wid - 1; } + } else if (gn_system_verilog() && sig->unpacked_dimensions() > 0 && path_tail.index.size() == 0) { + + // In this case, we are doing a continuous assignment to + // an unpacked array. The NetNet representation is a + // NetNet with a pin for each array element, so there is + // nothing more needed here. + // + // This can come up from code like this: + // logic [...] data [0:3]; + // assign data = ...; + // In this case, "sig" is "data", and sig->pin_count() + // is 4 to account for the unpacked size. + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: " + << "Net assign to unpacked array \"" << sig->name() + << "\" with " << sig->pin_count() << " elements." << endl; + } + } else if (sig->unpacked_dimensions() > 0) { // Make sure there are enough indices to address an array element. if (path_tail.index.size() < sig->unpacked_dimensions()) { cerr << get_fileline() << ": error: Array " << path() << " needs " << sig->unpacked_dimensions() << " indices," - << " but got only " << path_tail.index.size() << "." << endl; + << " but got only " << path_tail.index.size() << ". (net)" << endl; des->errors += 1; return 0; } @@ -627,6 +649,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, << sig->name() << as_indices(unpacked_indices) << "." << endl; widx = -1; + widx_flag = true; } else { NetExpr*canon_index = 0; @@ -639,12 +662,14 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, << sig->name() << as_indices(unpacked_indices_const) << "." << endl; widx = -1; + widx_flag = true; } else { NetEConst*canon_const = dynamic_cast(canon_index); ivl_assert(*this, canon_const); widx = canon_const->value().as_long(); + widx_flag = true; delete canon_index; } } @@ -717,7 +742,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, return 0; } - if (sig->pin_count() > 1) { + if (sig->pin_count() > 1 && widx_flag) { if (widx < 0 || widx >= (long) sig->pin_count()) return 0; @@ -729,6 +754,13 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, tmp->local_flag(true); connect(sig->pin(widx), tmp->pin(0)); sig = tmp; + + } else if (sig->pin_count() > 1) { + + // If this turns out to be an l-value unpacked array, + // then let the caller handle it. It will probably be + // converted into an array of assignments. + return sig; } /* If the desired l-value vector is narrower than the @@ -851,6 +883,20 @@ NetNet* PEIdent::elaborate_subport(Design*des, NetScope*scope) const long midx; long lidx; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_subport: " + << "path_ = \"" << path_ + << "\", unpacked_dimensions=" << sig->unpacked_dimensions() + << ", port_type()=" << sig->port_type() << endl; + } + + if (sig->unpacked_dimensions()) { + cerr << get_fileline() << ": sorry: " + << "Don't know now to elaborate unpacked array ports." << endl; + des->errors += 1; + return 0; + } + /* Evaluate the part/bit select expressions, to get the part select of the signal that attaches to the port. Also handle range and direction checking here. */ @@ -912,6 +958,20 @@ NetNet* PEIdent::elaborate_subport(Design*des, NetScope*scope) const return sig; } +NetNet*PEIdent::elaborate_unpacked_net(Design*des, NetScope*scope) const +{ + NetNet* sig = 0; + const NetExpr*par = 0; + NetEvent* eve = 0; + perm_string method_name; + + symbol_search(this, des, scope, path_, sig, par, eve); + + ivl_assert(*this, sig); + + return sig; +} + bool PEIdent::is_collapsible_net(Design*des, NetScope*scope) const { assert(scope); diff --git a/elaborate.cc b/elaborate.cc index 66f346853..64925639e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -77,11 +77,19 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const return; } + // If this turns out to be an assignment to an unpacked array, + // then handle that special case elsewhere. + if (lval->pin_count() > 1) { + elaborate_unpacked_array_(des, scope, lval); + return; + } + ivl_assert(*this, lval->pin_count() == 1); if (debug_elaborate) { - cerr << get_fileline() << ": debug: PGAssign: elaborated l-value" - << " width=" << lval->vector_width() << endl; + cerr << get_fileline() << ": PGAssign::elaborate: elaborated l-value" + << " width=" << lval->vector_width() + << ", pin_count=" << lval->pin_count() << endl; } NetExpr*rval_expr = elaborate_rval_expr(des, scope, lval->net_type(), @@ -211,6 +219,26 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const } +void PGAssign::elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const +{ + PEIdent*rval_pident = dynamic_cast (pin(1)); + ivl_assert(*this, rval_pident); + + NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope); + + ivl_assert(*this, rval_net->pin_count() == lval->pin_count()); + + for (unsigned idx = 0 ; idx < lval->pin_count() ; idx += 1) { + NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(), + lval->vector_width(), false); + driver->set_line(*this); + des->add_node(driver); + + connect(lval->pin(idx), driver->pin(0)); + connect(driver->pin(1), rval_net->pin(idx)); + } +} + unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, long&high, long&low) const { diff --git a/parse.y b/parse.y index 02f697b17..3fdcdd6f6 100644 --- a/parse.y +++ b/parse.y @@ -3822,16 +3822,14 @@ port_declaration : attribute_list_opt K_input net_type_opt data_type_or_implicit IDENTIFIER dimensions_opt { Module::port_t*ptmp; perm_string name = lex_strings.make($5); + data_type_t*use_type = $4; + if ($6) use_type = new uarray_type_t(use_type, $6); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); - pform_module_define_port(@2, name, NetNet::PINPUT, $3, $4, $1); + pform_module_define_port(@2, name, NetNet::PINPUT, $3, use_type, $1); port_declaration_context.port_type = NetNet::PINPUT; port_declaration_context.port_net_type = $3; port_declaration_context.data_type = $4; delete[]$5; - if ($6) { - yyerror(@6, "sorry: Input ports with unpacked dimensions not supported."); - delete $6; - } $$ = ptmp; } | attribute_list_opt diff --git a/pform.cc b/pform.cc index ce50148b3..b8a408b86 100644 --- a/pform.cc +++ b/pform.cc @@ -2173,24 +2173,34 @@ void pform_module_define_port(const struct vlltype&li, return; } - list*range = 0; + // Packed ranges + list*prange = 0; + // Unpacked dimensions + list*urange = 0; + + // If this is an unpacked array, then split out the parts that + // we can send to the PWire object that we create. + if (uarray_type_t*uarr_type = dynamic_cast (vtype)) { + urange = uarr_type->dims.get(); + vtype = uarr_type->base_type; + } if (vector_type_t*vec_type = dynamic_cast (vtype)) { data_type = vec_type->base_type; signed_flag = vec_type->signed_flag; - range = vec_type->pdims.get(); + prange = vec_type->pdims.get(); if (vec_type->reg_flag) type = NetNet::REG; } else if (atom2_type_t*atype = dynamic_cast(vtype)) { data_type = IVL_VT_BOOL; signed_flag = atype->signed_flag; - range = make_range_from_width(atype->type_code); + prange = make_range_from_width(atype->type_code); } else if (real_type_t*rtype = dynamic_cast(vtype)) { data_type = IVL_VT_REAL; signed_flag = true; - range = 0; + prange = 0; if (rtype->type_code != real_type_t::REAL) { VLerror(li, "sorry: Only real (not shortreal) supported here (%s:%d).", @@ -2200,7 +2210,7 @@ void pform_module_define_port(const struct vlltype&li, } else if ((struct_type = dynamic_cast(vtype))) { data_type = struct_type->figure_packed_base_type(); signed_flag = false; - range = 0; + prange = 0; } else if (vtype) { VLerror(li, "sorry: Given type %s not supported here (%s:%d).", @@ -2220,11 +2230,15 @@ void pform_module_define_port(const struct vlltype&li, if (struct_type) { cur->set_data_type(struct_type); - } else if (range == 0) { + } else if (prange == 0) { cur->set_range_scalar((type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); } else { - cur->set_range(*range, (type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); + cur->set_range(*prange, (type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); + } + + if (urange) { + cur->set_unpacked_idx(*urange); } pform_bind_attributes(cur->attributes, attr);