From 6e8aef82626d174aba7e53666652cb5087e780b2 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 25 May 2012 15:58:29 -0700 Subject: [PATCH] Get unpacked arrays working. --- PWire.cc | 9 +- PWire.h | 7 +- design_dump.cc | 11 ++- elab_expr.cc | 115 +++++++++++------------ elab_lval.cc | 79 +++++++++------- elab_net.cc | 81 +++++++++-------- elab_sig.cc | 45 ++++----- net_nex_input.cc | 2 +- netlist.cc | 85 ++++++----------- netlist.h | 21 ++--- netmisc.cc | 231 +++++++++++++++++++++++++++++++++++++++-------- netmisc.h | 33 ++++++- parse.y | 22 +---- pform.cc | 5 +- pform.h | 3 +- pform_dump.cc | 9 +- t-dll.cc | 23 ++++- t-dll.h | 4 +- 18 files changed, 468 insertions(+), 317 deletions(-) diff --git a/PWire.cc b/PWire.cc index 4539bba17..6db15b071 100644 --- a/PWire.cc +++ b/PWire.cc @@ -29,7 +29,7 @@ PWire::PWire(perm_string n, : name_(n), type_(t), port_type_(pt), data_type_(dt), signed_(false), isint_(false), port_set_(false), net_set_(false), is_scalar_(false), - error_cnt_(0), lidx_(0), ridx_(0), enum_type_(0), struct_type_(0), + error_cnt_(0), enum_type_(0), struct_type_(0), discipline_(0) { if (t == NetNet::INTEGER) { @@ -242,15 +242,14 @@ void PWire::set_range(const list&rlist, PWSRType type) } } -void PWire::set_memory_idx(PExpr*ldx, PExpr*rdx) +void PWire::set_unpacked_idx(const list&ranges) { - if (lidx_ != 0 || ridx_ != 0) { + if (! unpacked_.empty()) { cerr << get_fileline() << ": error: Array ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } else { - lidx_ = ldx; - ridx_ = rdx; + unpacked_ = ranges; } } diff --git a/PWire.h b/PWire.h index 23c496ba5..52c761821 100644 --- a/PWire.h +++ b/PWire.h @@ -78,7 +78,7 @@ class PWire : public LineInfo { void set_range_scalar(PWSRType type); void set_range(const std::list&ranges, PWSRType type); - void set_memory_idx(PExpr*ldx, PExpr*rdx); + void set_unpacked_idx(const std::list&ranges); void set_enumeration(enum_type_t*enum_type); void set_struct_type(struct_type_t*type); @@ -115,9 +115,8 @@ class PWire : public LineInfo { unsigned error_cnt_; // If this wire is actually a memory, these indices will give - // me the size and address range of the memory. - PExpr*lidx_; - PExpr*ridx_; + // me the size and address ranges of the memory. + std::listunpacked_; enum_type_t*enum_type_; struct_type_t*struct_type_; diff --git a/design_dump.cc b/design_dump.cc index 2ded152a2..3e7e7bb07 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -196,11 +196,20 @@ ostream&operator<<(ostream&out, const list&rlist) return out; } +ostream&operator<<(ostream&out, const vector&rlist) +{ + for (vector::const_iterator cur = rlist.begin() + ; cur != rlist.end() ; ++cur) { + out << "[" << cur->msb << ":" << cur->lsb << "]"; + } + return out; +} + /* Dump a net. This can be a wire or register. */ void NetNet::dump_net(ostream&o, unsigned ind) const { o << setw(ind) << "" << type() << ": " << name() - << "[" << s0_ << ":" << e0_ << " count=" << pin_count() << "]"; + << unpacked_dims_ << " unpacked dims=" << unpacked_dimensions() << " count=" << pin_count(); if (local_flag_) o << " (local)"; o << " " << data_type_; diff --git a/elab_expr.cc b/elab_expr.cc index 213a580db..979d2b45c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1873,7 +1873,7 @@ bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, { list index; index = path_.back().index; - for (size_t idx = 0 ; idx < net->array_dimensions() ; idx += 1) + for (size_t idx = 0 ; idx < net->unpacked_dimensions() ; idx += 1) index.pop_front(); return evaluate_index_prefix(des, scope, prefix_indices, index); @@ -2073,7 +2073,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) if (!name_tail.index.empty()) { const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. - if (!net || (name_tail.index.size() > net->array_dimensions())) { + if (!net || (name_tail.index.size() > net->unpacked_dimensions())) { use_sel = index_tail.sel; } } @@ -3037,78 +3037,69 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, const name_component_t&name_tail = path_.back(); - if (name_tail.index.empty() && !(SYS_TASK_ARG & flags)) { + // Special case: This is the entire array, and we are a direct + // argument of a system task. + if (name_tail.index.empty() && (SYS_TASK_ARG & flags)) { + NetESignal*res = new NetESignal(net, 0); + res->set_line(*this); + return res; + } + + if (name_tail.index.empty()) { cerr << get_fileline() << ": error: Array " << path() << " Needs an array index here." << endl; des->errors += 1; return 0; } - index_component_t index_front; - if (! name_tail.index.empty()) { - index_front = name_tail.index.front(); - ivl_assert(*this, index_front.sel != index_component_t::SEL_NONE); - if (index_front.sel != index_component_t::SEL_BIT) { - cerr << get_fileline() << ": error: Array " << path_ - << " cannot be indexed by a range." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, index_front.msb); - ivl_assert(*this, !index_front.lsb); - } - - NetExpr*word_index = 0; - if (index_front.sel != index_component_t::SEL_NONE) - word_index = elab_and_eval(des, scope, index_front.msb, -1, - need_const); - - if (word_index == 0 && !(SYS_TASK_ARG & flags)) + // Make sure there are enough indices to address an array element. + if (name_tail.index.size() < net->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << path() + << " needs " << net->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; + des->errors += 1; return 0; - - if (NetEConst*word_addr = dynamic_cast(word_index)) { - long addr = word_addr->value().as_long(); - - // Special case: The index is out of range, so the value - // of this expression is a 'bx vector the width of a word. - if (!net->array_index_is_valid(addr)) { - cerr << get_fileline() << ": warning: returning 'bx for out " - "of bounds array access " << net->name() - << "[" << addr << "]." << endl; - NetEConst*resx = make_const_x(net->vector_width()); - resx->set_line(*this); - delete word_index; - return resx; - } - - // Recalculate the constant address with the adjusted base. - unsigned use_addr = net->array_index_to_address(addr); - if (addr < 0 || use_addr != (unsigned long)addr) { - verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr)); - NetEConst*tmp = new NetEConst(val); - tmp->set_line(*this); - delete word_index; - word_index = tmp; - } - - } else if (word_index) { - // If there is a non-zero base to the memory, then build an - // expression to calculate the canonical address. - if (long base = net->array_first()) { - - word_index = normalize_variable_array_base( - word_index, base, net->array_count()); - eval_expr(word_index); - } } - NetESignal*res = new NetESignal(net, word_index); + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + list unpacked_indices_const; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, net->unpacked_dimensions(), + need_const, + unpacked_indices, + unpacked_indices_const); + + NetExpr*canon_index = 0; + if (flag) { + ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices_const); + + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "returning 'bx for out of bounds array access " + << net->name() << as_indices(unpacked_indices_const) << "." << endl; + } + + } else { + ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices); + } + + if (canon_index == 0) { + NetEConst*xxx = make_const_x(net->vector_width()); + xxx->set_line(*this); + return xxx; + } + + NetESignal*res = new NetESignal(net, canon_index); res->set_line(*this); // Detect that the word has a bit/part select as well. index_component_t::ctype_t word_sel = index_component_t::SEL_NONE; - if (name_tail.index.size() > 1) + if (name_tail.index.size() > net->unpacked_dimensions()) word_sel = name_tail.index.back().sel; if (net->get_scalar() && @@ -3117,7 +3108,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, if (res->expr_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << net->name() - <<"[" << *word_index << "]" << endl; + << as_indices(unpacked_indices) << endl; des->errors += 1; delete res; return 0; @@ -3599,7 +3590,7 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { - if (net->array_dimensions() > 0) + if (net->unpacked_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, expr_wid, flags); diff --git a/elab_lval.cc b/elab_lval.cc index 2cfb98016..ba95061d5 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -202,7 +202,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, // slice. This is, in fact, an error in l-values. Detect the // situation by noting if the index count is less than the // array dimensions (unpacked). - if (reg->array_dimensions() > name_tail.index.size()) { + if (reg->unpacked_dimensions() > name_tail.index.size()) { cerr << get_fileline() << ": error: Cannot assign to array " << path_ << ". Did you forget a word index?" << endl; des->errors += 1; @@ -228,7 +228,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, return lv; } - if (reg->array_dimensions() > 0) + if (reg->unpacked_dimensions() > 0) return elaborate_lval_net_word_(des, scope, reg); // This must be after the array word elaboration above! @@ -278,6 +278,15 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); + if (name_tail.index.size() < reg->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << reg->name() + << " needs " << reg->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; + des->errors += 1; + return 0; + } + + // Make sure there are enough indices to address an array element. const index_component_t&index_head = name_tail.index.front(); if (index_head.sel == index_component_t::SEL_PART) { cerr << get_fileline() << ": error: cannot perform a part " @@ -286,47 +295,47 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, return 0; } - ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - ivl_assert(*this, index_head.msb != 0); - ivl_assert(*this, index_head.lsb == 0); - NetExpr*word = elab_and_eval(des, scope, index_head.msb, -1); + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + list unpacked_indices_const; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, reg->unpacked_dimensions(), + false, + unpacked_indices, + unpacked_indices_const); - // If there is a non-zero base to the memory, then build an - // expression to calculate the canonical address. - if (long base = reg->array_first()) { - - word = normalize_variable_array_base(word, base, - reg->array_count()); - eval_expr(word); + NetExpr*canon_index = 0; + if (flag) { + ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(reg, unpacked_indices_const); + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "ignoring out of bounds l-value array access " << reg->name(); + for (list::const_iterator cur = unpacked_indices_const.begin() + ; cur != unpacked_indices_const.end() ; ++cur) { + cerr << "[" << *cur << "]"; + } + cerr << "." << endl; + } + } else { + ivl_assert(*this, unpacked_indices.size() == reg->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(reg, unpacked_indices); } + NetAssign_*lv = new NetAssign_(reg); - lv->set_word(word); + lv->set_word(canon_index); if (debug_elaborate) - cerr << get_fileline() << ": debug: Set array word=" << *word << endl; + cerr << get_fileline() << ": debug: Set array word=" << *canon_index << endl; - // Test for the case that the index is a constant, and is out - // of bounds. The "word" expression is the word index already - // converted to canonical address, so this just needs to check - // that the address is not too big. - if (NetEConst*word_const = dynamic_cast(word)) { - verinum word_val = word_const->value(); - long index = word_val.as_long(); - - if (index < 0 || index >= (long) reg->array_count()) { - cerr << get_fileline() << ": warning: Constant array index " - << (index + reg->array_first()) - << " is out of range for array " - << reg->name() << "." << endl; - } - } /* An array word may also have part selects applied to them. */ index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; - if (name_tail.index.size() > 1) + if (name_tail.index.size() > reg->unpacked_dimensions()) use_sel = name_tail.index.back().sel; if (reg->get_scalar() && @@ -335,7 +344,7 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, if (reg->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << reg->name() - << "[" << *word << "]" << endl; + << "[" << *canon_index << "]" << endl; des->errors += 1; return 0; } @@ -563,7 +572,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, if (warn_ob_select) { if (rel_base < 0) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -574,7 +583,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, } if (rel_base + wid > reg->vector_width()) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -587,7 +596,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, } else { if (warn_ob_select) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx"; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; diff --git a/elab_net.cc b/elab_net.cc index db70c4069..81aaa55ea 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -208,7 +208,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, // Only treat as part/bit selects any index that is beyond the // word selects for an array. This is not an array, then // dimensions==0 and any index is treated as a select. - if (name_tail.index.size() <= sig->array_dimensions()) { + if (name_tail.index.size() <= sig->unpacked_dimensions()) { midx = sig->vector_width()-1; lidx = 0; return true; @@ -247,7 +247,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, if (warn_ob_select) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) cerr << "[]"; + if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx"; if (index_tail.sel == index_component_t::SEL_IDX_UP) { @@ -279,7 +279,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, /* Warn about an indexed part select that is out of range. */ if (warn_ob_select && (lidx < 0)) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) cerr << "[]"; + if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << midx_val; if (index_tail.sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -290,7 +290,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } if (warn_ob_select && (midx >= (long)sig->vector_width())) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) { + if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << midx_val; @@ -337,16 +337,11 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { cerr << get_fileline() << ": warning: Part select " << sig->name(); - if (sig->array_dimensions() > 0) { + if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << msb << ":" << lsb << "] is out of range." << endl; -#if 0 - midx_tmp = sig->vector_width() - 1; - lidx_tmp = 0; - des->errors += 1; -#endif } /* This is completely out side the signal so just skip it. */ if (lidx_tmp >= (long)sig->vector_width() || midx_tmp < 0) { @@ -359,7 +354,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } case index_component_t::SEL_BIT: - if (name_tail.index.size() > sig->array_dimensions()) { + if (name_tail.index.size() > sig->unpacked_dimensions()) { long msb; bool bit_defined_flag; /* bool flag = */ calculate_bits_(des, scope, msb, bit_defined_flag); @@ -460,11 +455,9 @@ 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; - // The widx_val is the word select as entered in the source - // code. It's used for error messages. - long widx_val = 0; const name_component_t&name_tail = path_.back(); + list unpacked_indices_const; netstruct_t*struct_type = 0; if ((struct_type = sig->struct_type()) && !method_name.nil()) { @@ -485,53 +478,64 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, lidx = member_off; midx = lidx + member->width() - 1; - } else if (sig->array_dimensions() > 0) { + } else if (sig->unpacked_dimensions() > 0) { - if (name_tail.index.empty()) { - cerr << get_fileline() << ": error: array " << sig->name() - << " must be used with an index." << endl; + // Make sure there are enough indices to address an array element. + if (name_tail.index.size() < sig->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << path() + << " needs " << sig->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; des->errors += 1; return 0; } - const index_component_t&index_head = name_tail.index.front(); - if (index_head.sel == index_component_t::SEL_PART) { - cerr << get_fileline() << ": error: cannot perform a part " - << "select on array " << sig->name() << "." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - - NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1, true); - NetEConst*tmp = dynamic_cast(tmp_ex); - if (!tmp) { + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, sig->unpacked_dimensions(), + true, + unpacked_indices, + unpacked_indices_const); + // Note that !flag includes that there were any other + // elaboration errors generating the unpacked_indices list. + if (!flag) { cerr << get_fileline() << ": error: array " << sig->name() << " index must be a constant in this context." << endl; des->errors += 1; return 0; } - widx_val = tmp->value().as_long(); - if (sig->array_index_is_valid(widx_val)) - widx = sig->array_index_to_address(widx_val); - else + NetExpr*canon_index = 0; + ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(sig, unpacked_indices_const); + if (canon_index == 0) { + // Normalize detected an out-of-bounds + // index. Indicate that by setting the generated + // widx to -1. widx = -1; - delete tmp_ex; + + } else { + NetEConst*canon_const = dynamic_cast(canon_index); + ivl_assert(*this, canon_const); + + widx = canon_const->value().as_long(); + delete canon_index; + } if (debug_elaborate) cerr << get_fileline() << ": debug: Use [" << widx << "]" << " to index l-value array." << endl; /* The array has a part/bit select at the end. */ - if (name_tail.index.size() > sig->array_dimensions()) { + if (name_tail.index.size() > sig->unpacked_dimensions()) { if (sig->get_scalar()) { cerr << get_fileline() << ": error: " << "can not select part of "; if (sig->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << sig->name() - << "[" << widx_val << "]" << endl; + << as_indices(unpacked_indices_const) << endl; des->errors += 1; return 0; } @@ -550,6 +554,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, midx = midx_tmp; lidx = lidx_tmp; } + } else if (!name_tail.index.empty()) { if (sig->get_scalar()) { cerr << get_fileline() << ": error: " @@ -590,7 +595,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, if (widx < 0 || widx >= (long) sig->pin_count()) { cerr << get_fileline() << ": warning: ignoring out of " "bounds l-value array access " - << sig->name() << "[" << widx_val << "]." << endl; + << sig->name() << as_indices(unpacked_indices_const) << "." << endl; return 0; } diff --git a/elab_sig.cc b/elab_sig.cc index f5ba9d4ca..8d6ed3486 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1057,19 +1057,17 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib, des, scope); - long array_s0 = 0; - long array_e0 = 0; - unsigned array_dimensions = 0; - /* If the ident has idx expressions, then this is a - memory. It can only have the idx registers after the msb - and lsb expressions are filled. And, if it has one index, - it has both. */ - if (lidx_ || ridx_) { - assert(lidx_ && ridx_); + listunpacked_dimensions; - NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true); - NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true); + for (list::const_iterator cur = unpacked_.begin() + ; cur != unpacked_.end() ; ++cur) { + PExpr*use_lidx = cur->first; + PExpr*use_ridx = cur->second; + assert(use_lidx && use_ridx); + + NetExpr*lexp = elab_and_eval(des, scope, use_lidx, -1, true); + NetExpr*rexp = elab_and_eval(des, scope, use_ridx, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " @@ -1086,19 +1084,21 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete rexp; delete lexp; - if (!const_flag) { + long index_l, index_r; + if (! const_flag) { cerr << get_fileline() << ": error: The indices " << "are not constant for array ``" << name_ << "''." << endl; des->errors += 1; /* Attempt to recover from error, */ - array_s0 = 0; - array_e0 = 0; + index_l = 0; + index_r = 0; } else { - array_s0 = lval.as_long(); - array_e0 = rval.as_long(); - } - array_dimensions = 1; + index_l = lval.as_long(); + index_r = rval.as_long(); + } + + unpacked_dimensions.push_back(NetNet::range_t(index_l, index_r)); } if (data_type_ == IVL_VT_REAL && !packed_dimensions.empty()) { @@ -1173,16 +1173,11 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (!get_scalar()) { cerr << " " << packed_dimensions; } - cerr << " " << name_; - if (array_dimensions > 0) { - cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl; - } + cerr << " " << name_ << unpacked_dimensions; cerr << " in scope " << scope_path(scope) << endl; } - sig = array_dimensions > 0 - ? new NetNet(scope, name_, wtype, packed_dimensions, array_s0, array_e0) - : new NetNet(scope, name_, wtype, packed_dimensions); + sig = new NetNet(scope, name_, wtype, packed_dimensions, unpacked_dimensions); } // If this is an enumeration, then set the enumeration set for diff --git a/net_nex_input.cc b/net_nex_input.cc index 712ef37b0..5642e6164 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -159,7 +159,7 @@ NexusSet* NetESignal::nex_input(bool rem_out) delete tmp; if (warn_sens_entire_arr) { cerr << get_fileline() << ": warning: @* is sensitive to all " - << net_->array_count() << " words in array '" + << net_->unpacked_count() << " words in array '" << name() << "'." << endl; } } diff --git a/netlist.cc b/netlist.cc index ef0b6c1a6..200d3fd35 100644 --- a/netlist.cc +++ b/netlist.cc @@ -452,7 +452,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins) type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) + eref_count_(0), lref_count_(0) { assert(s); assert(npins>0); @@ -501,7 +501,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) { packed_dims_ = packed; @@ -529,34 +528,41 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, s->add_signal(this); } -static unsigned calculate_count(long s, long e) +static unsigned calculate_count(const list&unpacked) { - unsigned long r; - if (s >= e) { - r = s - e; - } else { - r = e - s; + unsigned long sum = 1; + for (list::const_iterator cur = unpacked.begin() + ; cur != unpacked.end() ; ++cur) { + sum *= cur->width(); } - if (r >= UINT_MAX) { + + if (sum >= UINT_MAX) return 0; - } - return r + 1; + + return sum; } NetNet::NetNet(NetScope*s, perm_string n, Type t, - const list&packed, long array_s, long array_e) -: NetObj(s, n, calculate_count(array_s, array_e)), + const list&packed, + const list&unpacked) +: NetObj(s, n, calculate_count(unpacked)), type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), - discipline_(0), - dimensions_(1), s0_(array_s), e0_(array_e), + discipline_(0), unpacked_dims_(unpacked.size()), eref_count_(0), lref_count_(0) { packed_dims_ = packed; + size_t idx = 0; + for (list::const_iterator cur = unpacked.begin() + ; cur != unpacked.end() ; ++cur, idx += 1) { + unpacked_dims_[idx] = *cur; + } + assert(idx == unpacked_dims_.size()); + ivl_assert(*this, s); if (pin_count() == 0) { - cerr << "Array too big [" << array_s << ":" << array_e << "]" << endl; + cerr << "Array too big " << unpacked << endl; ivl_assert(*this, 0); } @@ -602,7 +608,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty) data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(ty), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) { packed_dims_.push_back(range_t(calculate_count(ty)-1, 0)); @@ -878,52 +883,16 @@ bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned return true; } - -unsigned NetNet::array_dimensions() const +unsigned NetNet::unpacked_count() const { - return dimensions_; -} + unsigned c = 1; + for (size_t idx = 0 ; idx < unpacked_dims_.size() ; idx += 1) { + c *= unpacked_dims_[idx].width(); + } -long NetNet::array_first() const -{ - if (s0_ <= e0_) - return s0_; - else - return e0_; -} - -bool NetNet::array_addr_swapped() const -{ - if (s0_ <= e0_) - return false; - else - return true; -} - -unsigned NetNet::array_count() const -{ - unsigned c = calculate_count(s0_, e0_); - ivl_assert(*this, c > 0); return c; } -bool NetNet::array_index_is_valid(long sb) const -{ - if (sb < s0_ && sb < e0_) - return false; - if (sb > e0_ && sb > s0_) - return false; - return true; -} - -unsigned NetNet::array_index_to_address(long sb) const -{ - if (s0_ <= e0_) - return sb - s0_; - else - return sb - e0_; -} - void NetNet::incr_eref() { eref_count_ += 1; diff --git a/netlist.h b/netlist.h index ba9ff80e3..27a8cd829 100644 --- a/netlist.h +++ b/netlist.h @@ -591,7 +591,8 @@ class NetNet : public NetObj { explicit NetNet(NetScope*s, perm_string n, Type t, const std::list&packed); explicit NetNet(NetScope*s, perm_string n, Type t, - const std::list&packed, long s0, long e0); + const std::list&packed, + const std::list&unpacked); // This form builds a NetNet from its record definition. explicit NetNet(NetScope*s, perm_string n, Type t, netstruct_t*type); @@ -635,6 +636,8 @@ class NetNet : public NetObj { the verilog declaration. */ const std::list& packed_dims() const { return packed_dims_; } + const std::vector& unpacked_dims() const { return unpacked_dims_; } + /* The vector_width returns the bit width of the packed array, vector or scaler that is this NetNet object. The static method is also a convenient way to convert a range list to @@ -670,18 +673,10 @@ class NetNet : public NetObj { /* This method returns 0 for scalars and vectors, and greater for arrays. The value is the number of array indices. (Currently only one array index is supported.) */ - unsigned array_dimensions() const; - long array_first() const; - bool array_addr_swapped() const; + inline unsigned unpacked_dimensions() const { return unpacked_dims_.size(); } // This is the number of array elements. - unsigned array_count() const; - - // This method returns a 0 based address of an array entry as - // indexed by idx. The Verilog source may give index ranges - // that are not zero based. - bool array_index_is_valid(long idx) const; - unsigned array_index_to_address(long idx) const; + unsigned unpacked_count() const; bool local_flag() const { return local_flag_; } void local_flag(bool f) { local_flag_ = f; } @@ -723,11 +718,11 @@ class NetNet : public NetObj { ivl_discipline_t discipline_; std::list packed_dims_; - const unsigned dimensions_; - long s0_, e0_; + std::vector unpacked_dims_; unsigned eref_count_; unsigned lref_count_; + // When the signal is an unresolved wire, we need more detail // which bits are assigned. This mask is true for each bit // that is known to be driven. diff --git a/netmisc.cc b/netmisc.cc index 9ce4ebf00..475b22b91 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -384,51 +384,202 @@ NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, return base; } -/* - * This routine generates the normalization expression needed for a variable - * array word select. - */ -NetExpr *normalize_variable_array_base(NetExpr *base, long offset, - unsigned count) +ostream& operator << (ostream&o, __IndicesManip val) { - assert(offset != 0); - /* Calculate the space needed for the offset. */ - unsigned min_wid = num_bits(-offset); - /* 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(); - /* Now that we have the minimum needed width increase it by one - * to make room for the normalization calculation. */ - min_wid += 1; - /* Pad the base expression to the correct width. */ - base = pad_to_width(base, min_wid, *base); - /* If the offset is greater than zero then we need to do signed - * math to get the location value correct. */ - if (offset > 0 && ! base->has_sign()) { - /* We need this extra select to hide the signed property - * from the padding above. It will be removed automatically - * during code generation. */ - NetESelect *tmp = new NetESelect(base, 0 , min_wid); - tmp->set_line(*base); - tmp->cast_signed(true); - base = tmp; + for (list::const_iterator cur = val.val.begin() + ; cur != val.val.end() ; ++cur) { + o << "[" << *cur << "]"; } - /* Normalize the expression. */ - base = make_add_expr(base, -offset); + return o; +} - /* We should not need to do this, but .array/port does not - * handle a small signed index correctly and it is a major - * effort to fix it. For now we will just pad the expression - * enough so that any negative value when converted to - * unsigned is larger than the maximum array word. */ - if (base->has_sign()) { - unsigned range_wid = num_bits(count-1) + 1; - if (min_wid < range_wid) { - base = pad_to_width(base, range_wid, *base); +ostream& operator << (ostream&o, __IndicesManip val) +{ + for (list::const_iterator cur = val.val.begin() + ; cur != val.val.end() ; ++cur) { + o << "[" << *(*cur) << "]"; + } + return o; +} + +/* + * The src is the input index expression list from the expression, and + * the count is the number that are to be elaborated into the indices + * list. At the same time, create a indices_const list that contains + * the evaluated values for the expression, if they can be + * evaluated. This function will return "true" if all the constants + * can be evaluated. + */ +bool indices_to_expressions(Design*des, NetScope*scope, + // loc is for error messages. + const LineInfo*loc, + // src is the index list, and count is + // the number of items in the list to use. + const list&src, unsigned count, + // True if the expression MUST be constant. + bool need_const, + // These are the outputs. + list&indices, list&indices_const) +{ + ivl_assert(*loc, count <= src.size()); + + bool flag = true; + for (list::const_iterator cur = src.begin() + ; count > 0 ; ++cur, --count) { + ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE); + + if (cur->sel != index_component_t::SEL_BIT) { + cerr << loc->get_fileline() << ": error: " + << "Array cannot be indexed by a range." << endl; + des->errors += 1; + } + ivl_assert(*loc, cur->msb); + + NetExpr*word_index = elab_and_eval(des, scope, cur->msb, -1, need_const); + + // If the elaboration failed, then it is most certainly + // not constant, either. + if (word_index == 0) + flag = false; + + // Track if we detect any non-constant expressions + // here. This may allow for a special case. + if (flag) { + NetEConst*word_const = dynamic_cast (word_index); + if (word_const) + indices_const.push_back(word_const->value().as_long()); + else + flag = false; + } + + indices.push_back(word_index); + } + + return flag; +} + +static void make_strides(const vector&dims, + vector&stride) +{ + stride[dims.size()-1] = 1; + for (size_t idx = stride.size()-1 ; idx > 0 ; --idx) { + long tmp = dims[idx].width(); + if (idx < stride.size()) + tmp *= stride[idx]; + stride[idx-1] = tmp; + } +} + +/* + * Take in a vector of constant indices and convert them to a single + * number that is the canonical address (zero based, 1-d) of the + * word. If any of the indices are out of bounds, return nil instead + * of an expression. + */ +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +{ + const vector&dims = net->unpacked_dims(); + + // Make strides for each index. The stride is the distance (in + // words) to the next element in the canonical array. + vector stride (dims.size()); + make_strides(dims, stride); + + int64_t canonical_addr = 0; + + int idx = 0; + for (list::const_iterator cur = indices.begin() + ; cur != indices.end() ; ++cur, ++idx) { + long tmp = *cur; + + if (dims[idx].lsb <= dims[idx].msb) + tmp -= dims[idx].lsb; + else + tmp -= dims[idx].msb; + + // Notice of this index is out of range. + if (tmp < 0 || tmp >= dims[idx].width()) { + return 0; + } + + canonical_addr += tmp * stride[idx]; + } + + NetEConst*canonical_expr = new NetEConst(verinum(canonical_addr)); + return canonical_expr; +} + +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +{ + const vector&dims = net->unpacked_dims(); + + // Make strides for each index. The stride is the distance (in + // words) to the next element in the canonical array. + vector stride (dims.size()); + make_strides(dims, stride); + + NetExpr*canonical_expr = 0; + + int idx = 0; + for (list::const_iterator cur = indices.begin() + ; cur != indices.end() ; ++cur, ++idx) { + NetExpr*tmp = *cur; + // If the expression elaboration generated errors, then + // give up. Presumably, the error during expression + // elaboration already generated the error message. + if (tmp == 0) + return 0; + + int64_t use_base; + if (dims[idx].lsb <= dims[idx].msb) + use_base = dims[idx].lsb; + else + use_base = dims[idx].msb; + + int64_t use_stride = stride[idx]; + + // Account for that we are doing arithmatic and should + // have a proper width to make sure there ar no + // losses. So calculate a min_wid width. + unsigned tmp_wid; + unsigned min_wid = tmp->expr_width(); + if (use_stride != 1 && ((tmp_wid = num_bits(use_stride)) >= min_wid)) + min_wid = tmp_wid + 1; + if (use_base != 0 && ((tmp_wid = num_bits(use_base)) >= min_wid)) + min_wid = tmp_wid + 1; + if ((tmp_wid = num_bits(dims[idx].width()+1)) >= min_wid) + min_wid = tmp_wid + 1; + + tmp = pad_to_width(tmp, min_wid, *net); + + // Now generate the math to calculate the canonical address. + NetExpr*tmp_scaled = 0; + if (NetEConst*tmp_const = dynamic_cast (tmp)) { + // Special case: the index is constant, so this + // iteration can be replaced with a constant + // expression. + int64_t val = tmp_const->value().as_long(); + val -= use_base; + val *= use_stride; + tmp_scaled = new NetEConst(verinum(val)); + + } else { + tmp_scaled = tmp; + if (use_base != 0) + tmp_scaled = make_add_expr(tmp_scaled, -use_base); + if (use_stride != 1) + tmp_scaled = make_mult_expr(tmp_scaled, use_stride); + } + + if (canonical_expr == 0) { + canonical_expr = tmp_scaled; + } else { + canonical_expr = new NetEBAdd('+', canonical_expr, tmp_scaled, + canonical_expr->expr_width()+1, false); } } - return base; + return canonical_expr; } NetEConst* make_const_x(unsigned long wid) diff --git a/netmisc.h b/netmisc.h index 0504db472..c64a5a996 100644 --- a/netmisc.h +++ b/netmisc.h @@ -142,8 +142,37 @@ extern NetExpr *normalize_variable_part_base(const list&indices, NetExpr*b 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); +/* + * The as_indices() manipulator is a convenient way to emit a list of + * index values in the form [<>][<>].... + */ +template struct __IndicesManip { + inline __IndicesManip(const std::list&v) : val(v) { } + const std::list&val; +}; +template inline __IndicesManip as_indices(const std::list&indices) +{ return __IndicesManip(indices); } + +extern ostream& operator << (ostream&o, __IndicesManip); +extern ostream& operator << (ostream&o, __IndicesManip); + +/* + * Given a list of index expressions, generate elaborated expressions + * and constant values, if possible. + */ +extern bool indices_to_expressions(Design*des, NetScope*scope, + // loc is for error messages. + const LineInfo*loc, + // src is the index list, and count is + // the number of items in the list to use. + const list&src, unsigned count, + // True if the expression MUST be constant. + bool need_const, + // These are the outputs. + list&indices, list&indices_const); + +extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); +extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); /* * This function takes as input a NetNet signal and adds a constant diff --git a/parse.y b/parse.y index 71a17b01a..6fcfdffd8 100644 --- a/parse.y +++ b/parse.y @@ -4947,16 +4947,7 @@ register_variable { perm_string ident_name = lex_strings.make($1); pform_makewire(@1, ident_name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); - if ($2 != 0) { - pform_range_t index; - if ($2->size() > 1) { - yyerror(@2, "sorry: only 1 dimensional arrays " - "are currently supported."); - } - index = $2->front(); - pform_set_reg_idx(ident_name, index.first, index.second); - delete $2; - } + pform_set_reg_idx(ident_name, $2); $$ = $1; } | IDENTIFIER '=' expression @@ -4988,16 +4979,7 @@ net_variable { perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::IMPLICIT, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); - if ($2 != 0) { - pform_range_t index; - if ($2->size() > 1) { - yyerror(@2, "sorry: only 1 dimensional arrays " - "are currently supported."); - } - index = $2->front(); - pform_set_reg_idx(name, index.first, index.second); - delete $2; - } + pform_set_reg_idx(name, $2); $$ = $1; } ; diff --git a/pform.cc b/pform.cc index a21ae1182..1ba1e2feb 100644 --- a/pform.cc +++ b/pform.cc @@ -2273,7 +2273,7 @@ void pform_set_type_attrib(perm_string name, const string&key, * This function attaches a memory index range to an existing * register. (The named wire must be a register. */ -void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r) +void pform_set_reg_idx(perm_string name, list*indices) { PWire*cur = lexical_scope->wires_find(name); if (cur == 0) { @@ -2281,7 +2281,8 @@ void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r) return; } - cur->set_memory_idx(l, r); + if (indices && !indices->empty()) + cur->set_unpacked_idx(*indices); } LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, diff --git a/pform.h b/pform.h index c17a2e2b6..d88237c8e 100644 --- a/pform.h +++ b/pform.h @@ -294,7 +294,8 @@ extern void pform_set_net_range(list*names, bool signed_flag, ivl_variable_type_t, std::list*attr); -extern void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r); +extern void pform_set_reg_idx(perm_string name, + std::list*indices); extern void pform_set_reg_integer(list*names, list*attr); extern void pform_set_reg_time(list*names, list*attr); diff --git a/pform_dump.cc b/pform_dump.cc index 5adb1b407..364a26b67 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -376,11 +376,12 @@ void PWire::dump(ostream&out, unsigned ind) const out << " " << name_; - // If the wire has indices, dump them. - if (lidx_ || ridx_) { + // If the wire has unpacked indices, dump them. + for (list::const_iterator cur = unpacked_.begin() + ; cur != unpacked_.end() ; ++cur) { out << "["; - if (lidx_) out << *lidx_; - if (ridx_) out << ":" << *ridx_; + if (cur->first) out << *cur->first; + if (cur->second) out << ":" << *cur->second; out << "]"; } diff --git a/t-dll.cc b/t-dll.cc index e16f8477f..a8c5f12f5 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2403,7 +2403,8 @@ void dll_target::signal(const NetNet*net) (net->peek_lref() > 0) ? 1 : 0; obj->discipline = net->get_discipline(); - obj->array_dimensions_ = net->array_dimensions(); + obj->array_dimensions_ = net->unpacked_dimensions(); + assert(obj->array_dimensions_ == net->unpacked_dimensions()); switch (net->port_type()) { @@ -2493,9 +2494,23 @@ void dll_target::signal(const NetNet*net) t_cookie of the Nexus object so that I find it again when I next encounter the nexus. */ - obj->array_base = net->array_first(); - obj->array_words = net->array_count(); - obj->array_addr_swapped = net->array_addr_swapped() ? 1 : 0; + if (obj->array_dimensions_ == 1) { + const vector& dims = net->unpacked_dims(); + if (dims[0].msb < dims[0].lsb) { + obj->array_base = dims[0].msb; + obj->array_addr_swapped = false; + } else { + obj->array_base = dims[0].lsb; + obj->array_addr_swapped = true; + } + obj->array_words = net->unpacked_count(); + } else { + // The back-end API doesn't yet support multi-dimension + // unpacked arrays, so just report the canonical dimensions. + obj->array_base = 0; + obj->array_words = net->unpacked_count(); + obj->array_addr_swapped = 0; + } ivl_assert(*net, obj->array_words == net->pin_count()); if (debug_optimizer && obj->array_words > 1000) cerr << "debug: " diff --git a/t-dll.h b/t-dll.h index 2bdae36bb..54065e7d5 100644 --- a/t-dll.h +++ b/t-dll.h @@ -1,7 +1,7 @@ #ifndef __t_dll_H #define __t_dll_H /* - * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -685,7 +685,7 @@ struct ivl_signal_s { unsigned forced_net_ : 1; /* For now, support only 0 or 1 array dimensions. */ - unsigned array_dimensions_ : 1; + unsigned array_dimensions_ : 8; unsigned array_addr_swapped : 1; /* These encode the declared packed dimensions for the