diff --git a/elab_expr.cc b/elab_expr.cc index 6b5d48ade..6ad2f7cda 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1553,7 +1553,9 @@ bool calculate_part(const LineInfo*li, Design*des, NetScope*scope, */ static NetExpr* check_for_struct_members(const LineInfo*li, Design*des, NetScope*scope, - NetNet*net, const name_component_t&comp) + NetNet*net, + const list&base_index, + const name_component_t&comp) { unsigned long off; const netstruct_t::member_t*mem = get_struct_member(li, des, 0, net, @@ -1563,12 +1565,14 @@ static NetExpr* check_for_struct_members(const LineInfo*li, unsigned use_width = mem->width(); if (debug_elaborate) { - cerr << li->get_fileline() << ": check_for_struct_members: " + cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Found struct member " << mem->name << " At offset " << off << ", member width = " << use_width << endl; } + // The struct member may be a packed array. Process index + // expression that address the member element. if ( ! comp.index.empty() ) { // Evaluate all but the last index expression, into prefix_indices. listprefix_indices; @@ -1597,7 +1601,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li, prefix_to_slice(mem->packed_dims, prefix_indices, poff, loff, lwid); if (debug_elaborate) { - cerr << li->get_fileline() << ": check_for_struct_members: " + cerr << li->get_fileline() << ": debug: check_for_struct_members: " << "Evaluate prefix gives slice loff=" << loff << ", lwid=" << lwid << ", part select pwid=" << pwid << endl; } @@ -1609,8 +1613,44 @@ static NetExpr* check_for_struct_members(const LineInfo*li, use_width = lwid; } + // If the base symbol has dimensions, then this is a packed + // array of structures. Convert an array of indices to a + // single part select. For example, "net" is a packed array + // of struct, and "mem" is the struct member. In Verilog it + // looks something like "net[idx].mem". We've already + // converted "mem" to an offset into the packed struct, so now + // we just canonicalize "[idx]" and add the ".mem" offset to + // get a collapsed index. + NetExpr*packed_base = 0; + if(net->packed_dimensions() > 1) { + listtmp_index = base_index; + index_component_t member_select; + member_select.sel = index_component_t::SEL_BIT; + member_select.msb = new PENumber(new verinum(off)); + tmp_index.push_back(member_select); + packed_base = collapse_array_exprs(des, scope, li, net, tmp_index); + ivl_assert(*li, packed_base); + if (debug_elaborate) { + cerr << li->get_fileline() << ": debug: check_for_struct_members: " + << "Got collapsed array expr: " << *packed_base << endl; + } + } + + long tmp; + if (packed_base && eval_as_long(tmp, packed_base)) { + off = tmp; + delete packed_base; + packed_base = 0; + } + NetESignal*sig = new NetESignal(net); - NetEConst*base = make_const_val(off); + NetExpr *base = packed_base? packed_base : make_const_val(off); + + if (debug_elaborate) { + cerr << li->get_fileline() << ": debug: check_for_struct_members: " + << "Convert packed indices/member select into part select: " << *base << endl; + } + NetESelect*sel = new NetESelect(sig, base, use_width); return sel; } @@ -2352,7 +2392,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) ivl_assert(*this, use_enum != 0); expr_type_ = use_enum->base_type(); - expr_width_ = use_enum->base_width(); + expr_width_ = use_enum->packed_width(); min_width_ = expr_width_; signed_flag_ = par_enum->has_sign(); @@ -2400,9 +2440,17 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) // Check to see if we have a net and if so is it a structure? if (net != 0) { // If this net is a struct, the method name may be - // a struct member. + // a struct member. If it is, then we know the + // width of this identifier my knowing the width + // of the member. We don't even need to know + // anything about positions in containing arrays. if (net->struct_type() != 0) { - ivl_assert(*this, use_path.back().index.empty()); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: PEIdent::test_width: " + << "Net " << use_path << " is a struct, " + << "checking width of member " << method_name << endl; + } const netstruct_t::member_t*mem; unsigned long unused; @@ -2603,10 +2651,19 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // If this net is a struct, the method name may be // a struct member. if (net->struct_type() != 0) { - ivl_assert(*this, use_path.back().index.empty()); + if (debug_elaborate) { + cerr << get_fileline() << ": debug: " + << "PEIdent::elaborate_expr: " + << "Ident " << use_path + << " is a struct." + << " Expecting " << net->packed_dims().size() + << "-1 dimensions, " + << "got " << use_path.back().index.size() << "." << endl; + } return check_for_struct_members(this, des, scope, - net, member_comp); + net, use_path.back().index, + member_comp); } } diff --git a/elab_lval.cc b/elab_lval.cc index 7d0546ba9..8a36c23f7 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -713,20 +713,9 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, ++ name_idx; const name_component_t&name_base = *name_idx; - // The dimenions in the expression must match the packed - // dimensions that are declared for the variable. For example, - // if foo is a packed array of struct, then this expression - // must be "b[n][m]" with the right number of dimensions to - // match the declaration of "b". - ivl_assert(*this, name_base.index.size() == reg->packed_dimensions()); - - // Generate an expression that takes the input array of - // expressions and generates a canonical offset into the - // packed array. - NetExpr*packed_base = 0; - if (reg->packed_dimensions() > 0) - packed_base = collapse_array_indices(des, scope, reg, name_base.index); - + // Calculate the offset within the packed structure of the + // member, and any indices. We will add in the offset of the + // struct into the packed array later. unsigned long off; const netstruct_t::member_t* member = struct_type->packed_member(member_name, off); @@ -781,9 +770,31 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, use_width = lwid; } + // The dimenions in the expression must match the packed + // dimensions that are declared for the variable. For example, + // if foo is a packed array of struct, then this expression + // must be "b[n][m]" with the right number of dimensions to + // match the declaration of "b". + // Note that one of the packed dimensions is the packed struct + // itself. + ivl_assert(*this, name_base.index.size()+1 == reg->packed_dimensions()); + + // Generate an expression that takes the input array of + // expressions and generates a canonical offset into the + // packed array. + NetExpr*packed_base = 0; + if (reg->packed_dimensions() > 1) { + listtmp_index = name_base.index; + index_component_t member_select; + member_select.sel = index_component_t::SEL_BIT; + member_select.msb = new PENumber(new verinum(off)); + tmp_index.push_back(member_select); + packed_base = collapse_array_indices(des, scope, reg, tmp_index); + } + long tmp; - if (eval_as_long(tmp, packed_base)) { - off += tmp * struct_type->packed_width(); + if (packed_base && eval_as_long(tmp, packed_base)) { + off = tmp; delete packed_base; packed_base = 0; } @@ -793,6 +804,10 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, return true; } + // Oops, packed_base is not fully evaluated, so I don't know + // yet what to do with it. + cerr << get_fileline() << ": internal error: " + << "I don't know how to handle this index expression? " << *packed_base << endl; ivl_assert(*this, 0); return false; } diff --git a/elab_scope.cc b/elab_scope.cc index 0a9a5c81c..140cb060d 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -181,12 +181,12 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, verinum max_value (0); if (enum_type->signed_flag) { min_value = v_not((pow(verinum(2), - verinum(use_enum->base_width()-1)))) + + verinum(use_enum->packed_width()-1)))) + one_value; - max_value = pow(verinum(2), verinum(use_enum->base_width()-1)) - + max_value = pow(verinum(2), verinum(use_enum->packed_width()-1)) - one_value; } else { - max_value = pow(verinum(2), verinum(use_enum->base_width())) - + max_value = pow(verinum(2), verinum(use_enum->packed_width())) - one_value; } min_value.has_sign(true); @@ -249,15 +249,15 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, // The values are explicitly sized to the width of the // base type of the enumeration. verinum tmp_val (0); - if (cur_value.len() < use_enum->base_width()) { + if (cur_value.len() < use_enum->packed_width()) { // Pad the current value if it is narrower than the final // width of the enum. - tmp_val = pad_to_width (cur_value, use_enum->base_width()); + tmp_val = pad_to_width (cur_value, use_enum->packed_width()); tmp_val.has_len(true); } else { // Truncate an oversized value. We report out of bound // values above. This may create duplicates. - tmp_val = verinum(cur_value, use_enum->base_width()); + tmp_val = verinum(cur_value, use_enum->packed_width()); } tmp_val.has_sign(enum_type->signed_flag); diff --git a/elab_sig.cc b/elab_sig.cc index fd7277e94..5eb3915a2 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1213,7 +1213,9 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype << " enumeration " - << name_ << " in scope " << scope_path(scope) << endl; + << name_ << " in scope " << scope_path(scope) + << " with packed_dimensions=" << packed_dimensions + << " and packed_width=" << use_enum->packed_width() << endl; } sig = new NetNet(scope, name_, wtype, packed_dimensions, unpacked_dimensions, use_enum); diff --git a/net_expr.cc b/net_expr.cc index 2ae1da07a..76d1fba0e 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -334,7 +334,7 @@ NetESFunc::NetESFunc(const char*n, netenum_t*enum_type, unsigned np) : name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np) { name_ = lex_strings.add(n); - expr_width(enum_type->base_width()); + expr_width(enum_type->packed_width()); } NetESFunc::~NetESFunc() diff --git a/netenum.cc b/netenum.cc index 8a70cd0d7..238e00261 100644 --- a/netenum.cc +++ b/netenum.cc @@ -32,6 +32,14 @@ netenum_t::~netenum_t() { } +long netenum_t::packed_width() const +{ + if (msb_ >= lsb_) + return msb_ - lsb_ + 1; + else + return lsb_ - msb_ + 1; +} + bool netenum_t::insert_name(size_t name_idx, perm_string name, const verinum&val) { std::pair::iterator, bool> res; diff --git a/netenum.h b/netenum.h index d5caab46a..c254ae95a 100644 --- a/netenum.h +++ b/netenum.h @@ -37,7 +37,7 @@ class netenum_t : public LineInfo, public nettype_base_t { ~netenum_t(); ivl_variable_type_t base_type() const; - unsigned base_width() const; + long packed_width() const; bool has_sign() const; // The size() is the number of enumeration literals. @@ -73,14 +73,6 @@ class netenum_t : public LineInfo, public nettype_base_t { inline ivl_variable_type_t netenum_t::base_type() const { return base_type_; } -inline unsigned netenum_t::base_width() const -{ - if (msb_ >= lsb_) - return msb_ - lsb_ + 1; - else - return lsb_ - msb_ + 1; -} - inline size_t netenum_t::size() const { return names_.size(); } inline bool netenum_t::has_sign() const { return signed_flag_; } diff --git a/netlist.cc b/netlist.cc index a86e9d881..3aa48920b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -476,6 +476,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins) // Synthesize a single range to describe this canonical vector. packed_dims_.push_back(netrange_t(npins-1, 0)); + calculate_slice_widths_from_packed_dims_(); Link::DIR dir = Link::PASSIVE; @@ -521,6 +522,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, eref_count_(0), lref_count_(0) { packed_dims_ = packed; + calculate_slice_widths_from_packed_dims_(); assert(s); Link::DIR dir = Link::PASSIVE; @@ -564,6 +566,32 @@ static unsigned calculate_count(const list&unpacked) return sum; } +template static unsigned calculate_count(T*type) +{ + long wid = type->packed_width(); + if (wid >= 0) + return wid; + else + return 1; +} + +void NetNet::calculate_slice_widths_from_packed_dims_(void) +{ + if (packed_dims_.empty()) { + slice_wids_.clear(); + return; + } + + slice_wids_.resize(packed_dims_.size()); + + slice_wids_[0] = netrange_width(packed_dims_); + list::const_iterator cur = packed_dims_.begin(); + for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1) { + slice_wids_[idx] = slice_wids_[idx-1] / cur->width(); + } + +} + NetNet::NetNet(NetScope*s, perm_string n, Type t, const list&packed, const list&unpacked, @@ -576,6 +604,9 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, eref_count_(0), lref_count_(0) { packed_dims_ = packed; + if (net_type) + packed_dims_.push_back(netrange_t(calculate_count(net_type)-1, 0)); + calculate_slice_widths_from_packed_dims_(); size_t idx = 0; for (list::const_iterator cur = unpacked.begin() ; cur != unpacked.end() ; ++cur, idx += 1) { @@ -611,15 +642,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, s->add_signal(this); } -template static unsigned calculate_count(T*type) -{ - long wid = type->packed_width(); - if (wid >= 0) - return wid; - else - return 1; -} - /* * When we create a netnet for a packed struct, create a single * vector with the msb_/lsb_ chosen to name enough bits for the entire @@ -634,6 +656,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty) eref_count_(0), lref_count_(0) { packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0)); + calculate_slice_widths_from_packed_dims_(); Link::DIR dir = Link::PASSIVE; switch (t) { @@ -820,15 +843,23 @@ netdarray_t* NetNet::darray_type(void) const return dynamic_cast (net_type_); } -unsigned long NetNet::vector_width() const +/* + * "depth" is the number of index expressions that the user is using + * to index this identifer. So consider if Net was declared like so: + * + * reg [5:0][3:0] foo; + * + * In this case, slice_width(2) == 1 (slice_width(N) where N is the + * number of dimensions will always be 1.) and represents + * $bits(foo[a][b]). Then, slice_width(1)==4 ($bits(foo[a]) and slice_width(0)==24. + */ +unsigned long NetNet::slice_width(size_t depth) const { - unsigned long wid = netrange_width(packed_dims_); - if (net_type_) { - long tmp = net_type_->packed_width(); - if (tmp > 0) wid *= tmp; - } - - return wid; + if (depth > slice_wids_.size()) + return 0; + if (depth == slice_wids_.size()) + return 1; + return slice_wids_[depth]; } ivl_discipline_t NetNet::get_discipline() const diff --git a/netlist.h b/netlist.h index d07f58f0e..e092eedf4 100644 --- a/netlist.h +++ b/netlist.h @@ -664,7 +664,7 @@ class NetNet : public NetObj, public PortType { /* The vector_width returns the bit width of the packed array, vector or scaler that is this NetNet object. */ - unsigned long vector_width() const; + inline unsigned long vector_width() const { return slice_width(0); } /* Given a prefix of indices, figure out how wide the resulting slice would be. This is a generalization of the @@ -744,6 +744,14 @@ class NetNet : public NetObj, public PortType { std::list packed_dims_; std::vector unpacked_dims_; + // These are the widths of the various slice depths. There is + // one entry in this vector for each packed dimension. The + // value at N is the slice width if N indices are provided. + // + // For example: slice_wids_[0] is vector_width(). + void calculate_slice_widths_from_packed_dims_(void); + std::vector slice_wids_; + unsigned eref_count_; unsigned lref_count_; diff --git a/netmisc.cc b/netmisc.cc index aeb6479cb..d02d4e959 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -180,6 +180,21 @@ static NetExpr* make_add_expr(NetExpr*expr, long val) return res; } +static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2) +{ + bool use_signed = expr1->has_sign() && expr2->has_sign(); + unsigned use_wid = expr1->expr_width(); + + if (expr2->expr_width() > use_wid) + use_wid = expr2->expr_width(); + + expr1 = pad_to_width(expr1, use_wid, *loc); + expr2 = pad_to_width(expr2, use_wid, *loc); + + NetEBAdd*tmp = new NetEBAdd('+', expr1, expr2, use_wid, use_signed); + return tmp; +} + /* * Subtract an existing expression from a signed constant. */ @@ -254,6 +269,8 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb, if (is_up) offset -= wid - 1; /* Calculate the space needed for the offset. */ unsigned min_wid = num_bits(offset); + if (num_bits(soff) > min_wid) + min_wid = num_bits(soff); /* We need enough space for the larger of the offset or the * base expression. */ if (min_wid < base->expr_width()) min_wid = base->expr_width(); @@ -287,6 +304,8 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb, if ((soff-offset) == 0) return base; /* Calculate the space needed for the offset. */ unsigned min_wid = num_bits(-offset); + if (num_bits(soff) > min_wid) + min_wid = num_bits(soff); /* We need enough space for the larger of the offset or the * base expression. */ if (min_wid < base->expr_width()) min_wid = base->expr_width(); @@ -834,8 +853,9 @@ bool eval_as_double(double&value, NetExpr*expr) * returns the path component name. It will evaluate the index * expression if it is present. */ -hname_t eval_path_component(Design*des, NetScope*scope, - const name_component_t&comp) +static hname_t eval_path_component(Design*des, NetScope*scope, + const name_component_t&comp, + bool&error_flag) { // No index expression, so the path component is an undecorated // name, for example "foo". @@ -873,6 +893,7 @@ hname_t eval_path_component(Design*des, NetScope*scope, return res; } +#if 1 // Darn, the expression doesn't evaluate to a constant. That's // an error to be reported. And make up a fake index value to // return to the caller. @@ -880,6 +901,8 @@ hname_t eval_path_component(Design*des, NetScope*scope, << "Scope index expression is not constant: " << *index.msb << endl; des->errors += 1; +#endif + error_flag = true; delete tmp; return hname_t (comp.name, 0); @@ -888,15 +911,20 @@ hname_t eval_path_component(Design*des, NetScope*scope, std::list eval_scope_path(Design*des, NetScope*scope, const pform_name_t&path) { + bool path_error_flag = false; list res; typedef pform_name_t::const_iterator pform_path_it; for (pform_path_it cur = path.begin() ; cur != path.end(); ++ cur ) { const name_component_t&comp = *cur; - res.push_back( eval_path_component(des,scope,comp) ); + res.push_back( eval_path_component(des,scope,comp,path_error_flag) ); } - +#if 0 + if (path_error_flag) { + cerr << "XXXXX: Errors evaluating path " << path << endl; + } +#endif return res; } @@ -1128,6 +1156,64 @@ bool evaluate_index_prefix(Design*des, NetScope*scope, return true; } +/* + * Evaluate the indices. The chain of indices are applied to the + * packed indices of a NetNet to generate a canonical expression to + * replace the exprs. + */ +NetExpr*collapse_array_exprs(Design*des, NetScope*scope, + const LineInfo*loc, NetNet*net, + const list&indices) +{ + // First elaborate all the expressions as far as possible. + list exprs; + list exprs_const; + bool flag = indices_to_expressions(des, scope, loc, indices, + net->packed_dimensions(), + false, exprs, exprs_const); + ivl_assert(*loc, exprs.size() == net->packed_dimensions()); + + // Special Case: there is only 1 packed dimension, so the + // single expression should already be naturally canonical. + if (net->slice_width(1) == 1) { + return *exprs.begin(); + } + + const std::list&pdims = net->packed_dims(); + std::list::const_iterator pcur = pdims.begin(); + + list::iterator ecur = exprs.begin(); + NetExpr* base = 0; + for (size_t idx = 0 ; idx < net->packed_dimensions() ; idx += 1, ++pcur, ++ecur) { + unsigned cur_slice_width = net->slice_width(idx+1); + // This normalizes the expression of this index based on + // the msb/lsb values. + NetExpr*tmp = normalize_variable_base(*ecur, pcur->get_msb(), + pcur->get_lsb(), + cur_slice_width, true); + + // If this slice has width, then scale it. + if (net->slice_width(idx+1) != 1) { + unsigned min_wid = tmp->expr_width(); + if (num_bits(cur_slice_width) >= min_wid) { + min_wid = num_bits(cur_slice_width)+1; + tmp = pad_to_width(tmp, min_wid, *loc); + } + + tmp = make_mult_expr(tmp, cur_slice_width); + } + + // Now add it to the position we've accumulated so far. + if (base) { + base = make_add_expr(loc, base, tmp); + } else { + base = tmp; + } + } + + return base; +} + /* * Given a list of indices, treat them as packed indices and convert * them to an expression that normalizes the list to a single index @@ -1147,5 +1233,7 @@ NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net, NetExpr*base = elab_and_eval(des, scope, back_index.msb, -1, true); NetExpr*res = normalize_variable_bit_base(prefix_indices, base, net); + + eval_expr(res, -1); return res; } diff --git a/netmisc.h b/netmisc.h index 1972e652b..4815c002c 100644 --- a/netmisc.h +++ b/netmisc.h @@ -256,13 +256,6 @@ void eval_expr(NetExpr*&expr, int context_width =-1); bool eval_as_long(long&value, NetExpr*expr); bool eval_as_double(double&value, NetExpr*expr); -/* - * Evaluate the component of a scope path to get an hname_t value. Do - * the evaluation in the context of the given scope. - */ -extern hname_t eval_path_component(Design*des, NetScope*scope, - const name_component_t&comp); - /* * Evaluate an entire scope path in the context of the given scope. */ @@ -314,4 +307,8 @@ extern bool evaluate_index_prefix(Design*des, NetScope*scope, extern NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net, const std::list&indices); +extern NetExpr*collapse_array_exprs(Design*des, NetScope*scope, + const LineInfo*loc, NetNet*net, + const list&indices); + #endif diff --git a/pform.cc b/pform.cc index fb6f0012b..657f6049f 100644 --- a/pform.cc +++ b/pform.cc @@ -2872,7 +2872,7 @@ static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, assert(enum_type->range.get() != 0); assert(enum_type->range->size() == 1); - cur->set_range(*enum_type->range, SR_NET); + //XXXXcur->set_range(*enum_type->range, SR_NET); cur->set_packed_type(enum_type); pform_bind_attributes(cur->attributes, attr, true); } diff --git a/t-dll-api.cc b/t-dll-api.cc index c1f813871..a2444f81b 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -259,7 +259,7 @@ extern "C" ivl_variable_type_t ivl_enum_type(ivl_enumtype_t net) extern "C" unsigned ivl_enum_width(ivl_enumtype_t net) { assert(net); - return net->base_width(); + return net->packed_width(); } extern "C" int ivl_enum_signed(ivl_enumtype_t net)