diff --git a/PExpr.h b/PExpr.h index 17ca6f989..7691b44d1 100644 --- a/PExpr.h +++ b/PExpr.h @@ -343,6 +343,18 @@ class PEIdent : public PExpr { bool calculate_up_do_width_(Design*, NetScope*, unsigned long&wid) const; + // Evaluate the prefix indices. All but the final index in a + // chain of indices must be a single value and must evaluate + // to constants at compile time. For example: + // [x] - OK + // [1][2][x] - OK + // [1][x:y] - OK + // [2:0][x] - BAD + // [y][x] - BAD + // Leave the last index for special handling. + bool calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, + std::list&prefix_indices) const; + private: NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*) const; bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*) const; diff --git a/design_dump.cc b/design_dump.cc index bf046c2e8..5da7f9047 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1518,7 +1518,7 @@ void NetESignal::dump(ostream&o) const o << "+"; o << name(); if (word_) o << "[word=" << *word_ << "]"; - o << "[" << msi()<<":"<packed_dims(); } void NetETernary::dump(ostream&o) const diff --git a/elab_expr.cc b/elab_expr.cc index a75636c74..f886e7197 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1854,6 +1854,17 @@ NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const return tmp; } +bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, + list&prefix_indices) const +{ + list index; + index = path_.back().index; + for (size_t idx = 0 ; idx < net->array_dimensions() ; idx += 1) + index.pop_front(); + + return evaluate_index_prefix(des, scope, prefix_indices, index); +} + /* * Given that the msb_ and lsb_ are part select expressions, this * function calculates their values. Note that this method does *not* @@ -3101,6 +3112,10 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, NetESignal*net, NetScope*, unsigned expr_wid) const { + list prefix_indices; + bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); + ivl_assert(*this, rc); + long msv, lsv; bool parts_defined_flag; bool flag = calculate_parts_(des, scope, msv, lsv, parts_defined_flag); @@ -3133,9 +3148,8 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, tmp->set_line(*this); return tmp; } - - long sb_lsb = net->sig()->sb_to_idx(lsv); - long sb_msb = net->sig()->sb_to_idx(msv); + long sb_lsb = net->sig()->sb_to_idx(prefix_indices, lsv); + long sb_msb = net->sig()->sb_to_idx(prefix_indices, msv); if (sb_msb < sb_lsb) { cerr << get_fileline() << ": error: part select " << net->name(); @@ -3207,6 +3221,10 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); + ivl_assert(*this, rc); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; @@ -3224,7 +3242,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself. - if (net->sig()->sb_to_idx(lsv) == 0 && + if (net->sig()->sb_to_idx(prefix_indices, lsv) == 0 && wid == net->vector_width()) { delete base; net->cast_signed(false); @@ -3237,10 +3255,9 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, } // Otherwise, make a part select that covers the right // range. - ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) + - offset)); + ex = new NetEConst(verinum(net->sig()->sb_to_idx(prefix_indices,lsv) + offset)); if (warn_ob_select) { - long rel_base = net->sig()->sb_to_idx(lsv) + offset; + long rel_base = net->sig()->sb_to_idx(prefix_indices,lsv) + offset; if (rel_base < 0) { cerr << get_fileline() << ": warning: " << net->name(); @@ -3296,6 +3313,10 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); + ivl_assert(*this, rc); + NetExpr*base = calculate_up_do_base_(des, scope, need_const); unsigned long wid = 0; @@ -3312,7 +3333,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself. - if (net->sig()->sb_to_idx(lsv) == (signed) (wid-1) && + if (net->sig()->sb_to_idx(prefix_indices,lsv) == (signed) (wid-1) && wid == net->vector_width()) { delete base; net->cast_signed(false); @@ -3325,10 +3346,9 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } // Otherwise, make a part select that covers the right // range. - ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) + - offset)); + ex = new NetEConst(verinum(net->sig()->sb_to_idx(prefix_indices,lsv) + offset)); if (warn_ob_select) { - long rel_base = net->sig()->sb_to_idx(lsv) + offset; + long rel_base = net->sig()->sb_to_idx(prefix_indices,lsv) + offset; if (rel_base < 0) { cerr << get_fileline() << ": warning: " << net->name(); @@ -3381,6 +3401,10 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetESignal*net, NetScope*, bool need_const) const { + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, net->sig(), prefix_indices); + ivl_assert(*this, rc); + const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3388,12 +3412,12 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); - NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1, need_const); + NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1, need_const); // 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 (NetEConst*msc = dynamic_cast (ex)) { + if (NetEConst*msc = dynamic_cast (mux)) { // Special case: The bit select expression is constant // x/z. The result of the expression is 1'bx. @@ -3414,12 +3438,12 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetEConst*tmp = make_const_x(1); tmp->set_line(*this); - delete ex; + delete mux; return tmp; } long msv = msc->value().as_long(); - long idx = net->sig()->sb_to_idx(msv); + long idx = net->sig()->sb_to_idx(prefix_indices,msv); if (idx >= (long)net->vector_width() || idx < 0) { /* The bit select is out of range of the @@ -3444,7 +3468,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetEConst*tmp = make_const_x(1); tmp->set_line(*this); - delete ex; + delete mux; return tmp; } @@ -3464,16 +3488,36 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, return res; } + const list& sig_packed = net->sig()->packed_dims(); + if (prefix_indices.size()+2 <= sig_packed.size()) { + // Special case: this is a slice of a multi-dimensional + // packed array. For example: + // reg [3:0][7:0] x; + // x[2] = ... + // This shows up as the prefix_indices being too short + // for the packed dimensions of the vector. What we do + // here is convert to a "slice" of the vector. + unsigned long lwid; + mux = normalize_variable_slice_base(prefix_indices, mux, + net->sig(), lwid); + mux->set_line(*net); + + // Make a PART select with the canonical index + NetESelect*res = new NetESelect(net, mux, lwid); + res->set_line(*net); + + return res; + } + // 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. - const list& sig_packed = net->sig()->packed_dims(); assert(sig_packed.size() == 1); - ex = normalize_variable_base(ex, sig_packed, 1, true); + mux = normalize_variable_base(mux, sig_packed, 1, true); - NetESelect*ss = new NetESelect(net, ex, 1); + NetESelect*ss = new NetESelect(net, mux, 1); ss->set_line(*this); return ss; } @@ -3506,6 +3550,10 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, return 0; } + list prefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, path_.back().index); + ivl_assert(*this, rc); + // 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 diff --git a/elab_lval.cc b/elab_lval.cc index 70c0f8b2c..ce887fff6 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -183,16 +183,26 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, } ivl_assert(*this, reg); - + // We are processing the tail of a string of names. For + // example, the verilog may be "a.b.c", so we are processing + // "c" at this point. const name_component_t&name_tail = path_.back(); + // Use the last index to determine what kind of select + // (bit/part/etc) we are processing. For example, the verilog + // may be "a.b.c[1][2][]". All but the last index must + // be simple expressions, only the may be a part + // select etc., so look at it to determine how we will be + // proceeding. index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; - // This is the special case that the l-value is an entire - // memory. This is, in fact, an error. - if (reg->array_dimensions() > 0 && name_tail.index.empty()) { + // Special case: The l-value is an entire memory, or array + // slice. This is, in fact, an error in l-values. Detect the + // situation by noting if the index count is less then the + // array dimensions (unpacked). + if (reg->array_dimensions() > name_tail.index.size()) { cerr << get_fileline() << ": error: Cannot assign to array " << path_ << ". Did you forget a word index?" << endl; des->errors += 1; @@ -200,7 +210,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, } /* Get the signal referenced by the identifier, and make sure - it is a register. Wires are not allows in this context, + it is a register. Wires are not allowed in this context, unless this is the l-value of a force. */ if ((reg->type() != NetNet::REG) && !is_force) { cerr << get_fileline() << ": error: " << path_ << @@ -347,7 +357,13 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, NetScope*scope, NetAssign_*lv) const { + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); + ivl_assert(*this, rc); + const name_component_t&name_tail = path_.back(); + ivl_assert(*this, !name_tail.index.empty()); + const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb != 0); ivl_assert(*this, index_tail.lsb == 0); @@ -365,26 +381,43 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, mux = 0; } - // For now, we only understand 1-dim packed arrays. - const list&packed = reg->packed_dims(); - ivl_assert(*this, packed.size() == 1); - const NetNet::range_t rng = packed.back(); - if (mux) { + if (prefix_indices.size()+2 <= reg->packed_dims().size()) { + // Special case: this is a slice of a multi-dimensional + // packed array. For example: + // reg [3:0][7:0] x; + // x[2] = ... + // This shows up as the prefix_indices being too short + // for the packed dimensions of the vector. What we do + // here is convert to a "slice" of the vector. + if (mux == 0) { + long loff; + unsigned long lwid; + bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid); + ivl_assert(*this, rcl); + + lv->set_part(new NetEConst(verinum(loff)), lwid); + } else { + unsigned long lwid; + mux = normalize_variable_slice_base(prefix_indices, mux, + reg, lwid); + lv->set_part(mux, lwid); + } + + } else if (mux) { // Non-constant bit mux. Correct the mux for the range // of the vector, then set the l-value part select // expression. mux = normalize_variable_base(mux, reg->packed_dims(), 1, true); - lv->set_part(mux, 1); - } else if (lsb == rng.msb && lsb == rng.lsb) { + } else if (reg->vector_width() == 1 && reg->sb_is_valid(prefix_indices,lsb)) { // Constant bit mux that happens to select the only bit // of the l-value. Don't bother with any select at all. } else { // Constant bit select that does something useful. - long loff = reg->sb_to_idx(lsb); + long loff = reg->sb_to_idx(prefix_indices,lsb); if (loff < 0 || loff >= (long)reg->vector_width()) { cerr << get_fileline() << ": error: bit select " @@ -404,6 +437,10 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, NetScope*scope, NetAssign_*lv) const { + list prefix_indices; + bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); + ivl_assert(*this, rc); + // The range expressions of a part select must be // constant. The calculate_parts_ function calculates the // values into msb and lsb. @@ -429,8 +466,8 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, } else { /* Get the canonical offsets into the vector. */ - long loff = reg->sb_to_idx(lsb); - long moff = reg->sb_to_idx(msb); + long loff = reg->sb_to_idx(prefix_indices,lsb); + long moff = reg->sb_to_idx(prefix_indices,msb); long wid = moff - loff + 1; if (moff < loff) { @@ -463,6 +500,10 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, NetAssign_*lv, index_component_t::ctype_t use_sel) const { + listprefix_indices; + bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices); + ivl_assert(*this, rc); + const name_component_t&name_tail = path_.back();; ivl_assert(*this, !name_tail.index.empty()); @@ -509,7 +550,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, offset = -wid + 1; } delete base; - long rel_base = reg->sb_to_idx(lsv) + offset; + long rel_base = reg->sb_to_idx(prefix_indices,lsv) + offset; /* If we cover the entire lvalue just skip the select. */ if (rel_base == 0 && wid == reg->vector_width()) return true; base = new NetEConst(verinum(rel_base)); diff --git a/elab_net.cc b/elab_net.cc index 6f1aca6a7..98180dab5 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -200,6 +200,10 @@ bool PEConcat::is_collapsible_net(Design*des, NetScope*scope) const bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, long&midx, long&lidx) const { + list prefix_indices; + bool rc = calculate_packed_indices_(des, scope, sig, prefix_indices); + ivl_assert(*this, rc); + const name_component_t&name_tail = path_.back(); // Only treat as part/bit selects any index that is beyond the // word selects for an array. This is not an array, then @@ -258,13 +262,13 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } long midx_val = tmp->value().as_long(); - midx = sig->sb_to_idx(midx_val); + midx = sig->sb_to_idx(prefix_indices, midx_val); delete tmp_ex; if (index_tail.sel == index_component_t::SEL_IDX_UP) - lidx = sig->sb_to_idx(midx_val+wid-1); + lidx = sig->sb_to_idx(prefix_indices, midx_val+wid-1); else - lidx = sig->sb_to_idx(midx_val-wid+1); + lidx = sig->sb_to_idx(prefix_indices, midx_val-wid+1); if (midx < lidx) { long tmpx = midx; @@ -313,8 +317,8 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, /* bool flag = */ calculate_parts_(des, scope, msb, lsb, part_defined_flag); ivl_assert(*this, part_defined_flag); - long lidx_tmp = sig->sb_to_idx(lsb); - long midx_tmp = sig->sb_to_idx(msb); + long lidx_tmp = sig->sb_to_idx(prefix_indices, lsb); + long midx_tmp = sig->sb_to_idx(prefix_indices, msb); /* Detect reversed indices of a part select. */ if (lidx_tmp > midx_tmp) { cerr << get_fileline() << ": error: Part select " @@ -370,7 +374,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } assert(mval); - midx = sig->sb_to_idx(mval->as_long()); + midx = sig->sb_to_idx(prefix_indices, mval->as_long()); if (midx >= (long)sig->vector_width()) { cerr << get_fileline() << ": error: Index " << sig->name() << "[" << mval->as_long() << "] is out of range." diff --git a/elab_sig.cc b/elab_sig.cc index 7848c5540..336a8e97e 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1149,12 +1149,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const cerr << " in scope " << scope_path(scope) << endl; } - if (packed_dimensions.size() > 1) { - cerr << get_fileline() << ": sorry: Multi-dimension " - << "packed arrays not supported." << endl; - des->errors += 1; - } - sig = array_dimensions > 0 ? new NetNet(scope, name_, wtype, packed_dimensions, array_s0, array_e0) : new NetNet(scope, name_, wtype, packed_dimensions); diff --git a/ivl_target.h b/ivl_target.h index 3a1c39b70..eda6861f9 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1785,13 +1785,23 @@ extern int ivl_scope_time_units(ivl_scope_t net); * If the signal has been declared with a domain (Verilog-AMS) then * this function will return a non-nil ivl_discipline_t. * - * ivl_signal_msb - * ivl_signal_lsb + * ivl_signal_msb (deprecated) + * ivl_signal_lsb (deprecated) + * ivl_signal_packed_dimensions + * ivl_signal_packed_msb + * ivl_signal_packed_lsb * ivl_signal_width - * These functions return the left and right indices, respectively, - * of the signal. If the signal is a scalar, both return 0. However, - * it doesn't mean that the signal is a scalar if both return 0, one - * can have a vector with 0 as both indices. + * These functions return the msb and lsb packed indices. The + * packed dimensions are declared differently from array + * dimensions, like so: + * reg [4:1][7:0] sig... + * which has two packed dimensions. The [4:1] dimension is the + * first, and so forth. If the signal is a scalar, it has 0 + * dimension. + * + * The ivl_signal_msb/ivl_signal_lsb functions are deprecated + * versions that only work with variables that have less then two + * dimensions. They will return msb==lsb==0 for scalars. * * ivl_signal_port * If the signal is a port to a module, this function returns the @@ -1859,8 +1869,11 @@ extern unsigned ivl_signal_array_count(ivl_signal_t net); extern unsigned ivl_signal_array_addr_swapped(ivl_signal_t net); extern unsigned ivl_signal_dimensions(ivl_signal_t net); extern ivl_discipline_t ivl_signal_discipline(ivl_signal_t net); -extern int ivl_signal_msb(ivl_signal_t net); -extern int ivl_signal_lsb(ivl_signal_t net); +extern unsigned ivl_signal_packed_dimensions(ivl_signal_t net); +extern int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim); +extern int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim); +extern int ivl_signal_msb(ivl_signal_t net) __attribute__((deprecated)); +extern int ivl_signal_lsb(ivl_signal_t net) __attribute__((deprecated)); extern unsigned ivl_signal_width(ivl_signal_t net); extern ivl_signal_port_t ivl_signal_port(ivl_signal_t net); extern int ivl_signal_signed(ivl_signal_t net); diff --git a/netlist.cc b/netlist.cc index 10c4668f7..922f6f2bb 100644 --- a/netlist.cc +++ b/netlist.cc @@ -780,8 +780,9 @@ unsigned long NetNet::vector_width(const list&packed) return wid; } -bool NetNet::sb_is_valid(long sb) const +bool NetNet::sb_is_valid(const list&indices, long sb) const { + ivl_assert(*this, indices.size()+1 == packed_dims_.size()); assert(packed_dims_.size() == 1); const range_t&rng = packed_dims_.back(); if (rng.msb >= rng.lsb) @@ -790,8 +791,9 @@ bool NetNet::sb_is_valid(long sb) const return (sb <= rng.lsb) && (sb >= rng.msb); } -long NetNet::sb_to_idx(long sb) const +long NetNet::sb_to_idx(const list&indices, long sb) const { + ivl_assert(*this, indices.size()+1 == packed_dims_.size()); assert(packed_dims_.size() == 1); const range_t&rng = packed_dims_.back(); if (rng.msb >= rng.lsb) @@ -800,6 +802,56 @@ long NetNet::sb_to_idx(long sb) const return rng.lsb - sb; } +bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned long&lwid) const +{ + ivl_assert(*this, indices.size() < packed_dims_.size()); + + size_t acc_wid = 1; + list::const_iterator pcur = packed_dims_.end(); + for (size_t idx = indices.size()+1 ; idx < packed_dims_.size() ; idx += 1) { + -- pcur; + acc_wid *= pcur->width(); + } + + lwid = acc_wid; + + -- pcur; + if (sb < pcur->msb && sb < pcur->lsb) + return false; + if (sb > pcur->msb && sb > pcur->lsb) + return false; + + long acc_off = 0; + if (pcur->msb >= pcur->lsb) + acc_off += (sb - pcur->lsb) * acc_wid; + else + acc_off += (sb - pcur->msb) * acc_wid; + + if (indices.size() == 0) { + loff = acc_off; + return true; + } + + lwid *= pcur->width(); + + list::const_iterator icur = indices.end(); + do { + -- pcur; + -- icur; + acc_wid *= pcur->width(); + if (pcur->msb >= pcur->lsb) + acc_off += (*icur - pcur->lsb) * acc_wid; + else + acc_off += (*icur - pcur->msb) * acc_wid; + + } while (icur != indices.begin()); + + loff = acc_off; + + return true; +} + + unsigned NetNet::array_dimensions() const { return dimensions_; diff --git a/netlist.h b/netlist.h index 658523c44..42f31050e 100644 --- a/netlist.h +++ b/netlist.h @@ -550,7 +550,7 @@ class NetDelaySrc : public NetObj { * * NetNet objects are located by searching NetScope objects. * - * The pin of a NetNet object are PASSIVE: they do not drive + * The pins of a NetNet object are PASSIVE: they do not drive * anything and they are not a data sink, per se. The pins follow the * values on the nexus. */ @@ -573,6 +573,9 @@ class NetNet : public NetObj { long msb; long lsb; + + inline unsigned long width()const + { if (msb >= lsb) return msb-lsb+1; else return lsb-msb+1; } }; public: @@ -639,15 +642,30 @@ class NetNet : public NetObj { static unsigned long vector_width(const std::list&); unsigned long vector_width() const { return vector_width(packed_dims_); } + /* Given a prefix of indices, figure out how wide the + resulting slice would be. This is a generalization of the + vector_width(), where the depth would be 0. */ + unsigned long slice_width(size_t depth) const; + /* This method converts a signed index (the type that might be - found in the Verilog source) to a pin number. It accounts - for variation in the definition of the reg/wire/whatever. */ - long sb_to_idx(long sb) const; + found in the Verilog source) to canonical. It accounts + for variation in the definition of the + reg/wire/whatever. Note that a canonical index of a + multi-dimensioned packed array is a single dimension. For + example, "reg [4:1][3:0]..." has the canonical dimension + [15:0] and the sb_to_idx) method will convert [2][2] to + the canonical index [6]. */ + long sb_to_idx(const std::list&prefix, long sb) const; + + /* This method converts a partial packed indices list and a + tail index, and generates a canonical slice offset and + width. */ + bool sb_to_slice(const std::list&prefix, long sb, long&off, unsigned long&wid) const; /* This method checks that the signed index is valid for this signal. If it is, the above sb_to_idx can be used to get the pin# from the index. */ - bool sb_is_valid(long sb) const; + bool sb_is_valid(const std::list&prefix, long sb) const; /* This method returns 0 for scalars and vectors, and greater for arrays. The value is the number of array @@ -2348,6 +2366,8 @@ class NetAssign_ { ivl_select_type_t select_type() const; void set_word(NetExpr*); + // Set a part select expression for the l-value vector. Note + // that the expression calculates a CANONICAL bit address. void set_part(NetExpr* loff, unsigned wid, ivl_select_type_t = IVL_SEL_OTHER); diff --git a/netmisc.cc b/netmisc.cc index 09853f8f0..c6e94e881 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -198,6 +198,21 @@ static NetExpr* make_sub_expr(long val, NetExpr*expr) return res; } +static NetExpr* make_mult_expr(NetExpr*expr, unsigned long val) +{ + verinum val_v (val, expr->expr_width()); + val_v.has_sign(true); + + NetEConst*val_c = new NetEConst(val_v); + val_c->set_line(*expr); + + NetEBMult*res = new NetEBMult('*', expr, val_c, expr->expr_width(), + expr->has_sign()); + res->set_line(*expr); + + return res; +} + /* * This routine is used to calculate the number of bits needed to * contain the given number. @@ -310,6 +325,31 @@ NetExpr *normalize_variable_base(NetExpr *base, return normalize_variable_base(base, rng.msb, rng.lsb, wid, is_up); } +NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, + const NetNet*reg, unsigned long&lwid) +{ + const list&packed_dims = reg->packed_dims(); + ivl_assert(*base, indices.size() < packed_dims.size()); + + list::const_iterator pcur = packed_dims.end(); + for (size_t idx = indices.size() ; idx < packed_dims.size(); idx += 1) { + -- pcur; + } + + long sb; + if (pcur->msb >= pcur->lsb) + sb = pcur->lsb; + else + sb = pcur->msb; + + long loff; + reg->sb_to_slice(indices, sb, loff, lwid); + + base = make_mult_expr(base, lwid); + base = make_add_expr(base, loff); + return base; +} + /* * This routine generates the normalization expression needed for a variable * array word select. @@ -858,3 +898,39 @@ void collapse_partselect_pv_to_concat(Design*des, NetNet*sig) delete ps_obj; } } + +/* + * Evaluate the prefix indices. All but the final index in a + * chain of indices must be a single value and must evaluate + * to constants at compile time. For example: + * [x] - OK + * [1][2][x] - OK + * [1][x:y] - OK + * [2:0][x] - BAD + * [y][x] - BAD + * Leave the last index for special handling. + */ +bool evaluate_index_prefix(Design*des, NetScope*scope, + list&prefix_indices, + const list&indices) +{ + list::const_iterator icur = indices.begin(); + for (size_t idx = 0 ; (idx+1) < indices.size() ; idx += 1, ++icur) { + assert(icur != indices.end()); + assert(icur->sel == index_component_t::SEL_BIT); + NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, true); + ivl_assert(*icur->msb, texpr); + long tmp; + if (!eval_as_long(tmp, texpr)) { + cerr << icur->msb->get_fileline() << ": error: " + "Array index expressions must be constant." << endl; + des->errors += 1; + return 0; + } + + prefix_indices .push_back(tmp); + delete texpr; + } + + return true; +} diff --git a/netmisc.h b/netmisc.h index b0db81d32..91d1fcfd9 100644 --- a/netmisc.h +++ b/netmisc.h @@ -103,6 +103,8 @@ extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb, extern NetExpr*normalize_variable_base(NetExpr *base, const list&dims, unsigned long wid, bool is_up); +extern NetExpr*normalize_variable_slice_base(const list&indices, NetExpr *base, + const NetNet*reg, unsigned long&lwid); extern NetExpr*normalize_variable_array_base(NetExpr *base, long offset, unsigned count); @@ -238,4 +240,7 @@ extern uint64_t get_scaled_time_from_real(Design*des, extern void collapse_partselect_pv_to_concat(Design*des, NetNet*sig); +extern bool evaluate_index_prefix(Design*des, NetScope*scope, + list&prefix_indices, + const list&indices); #endif diff --git a/t-dll-api.cc b/t-dll-api.cc index b45da977e..008847127 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -2147,15 +2147,39 @@ extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word) } } +extern "C" unsigned ivl_signal_packed_dimensions(ivl_signal_t net) +{ + return net->packed_dims.size(); +} + +extern "C" int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim) +{ + assert(dim < net->packed_dims.size()); + return net->packed_dims[dim].msb; +} + +extern "C" int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim) +{ + assert(dim < net->packed_dims.size()); + return net->packed_dims[dim].lsb; +} + extern "C" int ivl_signal_msb(ivl_signal_t net) { - assert(net->lsb_dist == 1 || net->lsb_dist == -1); - return net->lsb_index + net->lsb_dist * (net->width_ - 1); + if (net->packed_dims.size() == 0) + return 0; + + assert(net->packed_dims.size() == 1); + return net->packed_dims[0].msb; } extern "C" int ivl_signal_lsb(ivl_signal_t net) { - return net->lsb_index; + if (net->packed_dims.size() == 0) + return 0; + + assert(net->packed_dims.size() == 1); + return net->packed_dims[0].lsb; } extern "C" ivl_scope_t ivl_signal_scope(ivl_signal_t net) diff --git a/t-dll.cc b/t-dll.cc index 50ab72724..e6b44c75f 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2399,16 +2399,17 @@ void dll_target::signal(const NetNet*net) /* Save the primitive properties of the signal in the ivl_signal_t object. */ - // FIX ME: This is a temporary workaround until the ivl_target - // API gets a way to represent multiple packed dimensions. - const list&packed = net->packed_dims(); - ivl_assert(*net, packed.size() <= 1); - const NetNet::range_t rng = packed.empty()? NetNet::range_t(0,0) : packed.back(); + { size_t idx = 0; + list::const_iterator cur; + obj->packed_dims.resize(net->packed_dims().size()); + for (cur = net->packed_dims().begin(), idx = 0 + ; cur != net->packed_dims().end() ; ++cur, idx += 1) { + obj->packed_dims[idx] = *cur; + } + } obj->width_ = net->vector_width(); obj->signed_= net->get_signed()? 1 : 0; - obj->lsb_index = rng.lsb; - obj->lsb_dist = rng.msb >= rng.lsb ? 1 : -1; obj->isint_ = false; obj->local_ = net->local_flag()? 1 : 0; obj->forced_net_ = (net->type() != NetNet::REG) && diff --git a/t-dll.h b/t-dll.h index 62f2df303..2bdae36bb 100644 --- a/t-dll.h +++ b/t-dll.h @@ -688,10 +688,9 @@ struct ivl_signal_s { unsigned array_dimensions_ : 1; unsigned array_addr_swapped : 1; - /* These encode the run-time index for the least significant - bit, and the distance to the second bit. */ - signed lsb_index; - signed lsb_dist; + /* These encode the declared packed dimensions for the + signal, in case they are needed by the run-time */ + std::vector packed_dims; perm_string name_; ivl_scope_t scope_;