Get unpacked arrays working.

This commit is contained in:
Stephen Williams 2012-05-25 15:58:29 -07:00
parent f7ba954ef7
commit 6e8aef8262
18 changed files with 468 additions and 317 deletions

View File

@ -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<pform_range_t>&rlist, PWSRType type)
}
}
void PWire::set_memory_idx(PExpr*ldx, PExpr*rdx)
void PWire::set_unpacked_idx(const list<pform_range_t>&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;
}
}

View File

@ -78,7 +78,7 @@ class PWire : public LineInfo {
void set_range_scalar(PWSRType type);
void set_range(const std::list<pform_range_t>&ranges, PWSRType type);
void set_memory_idx(PExpr*ldx, PExpr*rdx);
void set_unpacked_idx(const std::list<pform_range_t>&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::list<pform_range_t>unpacked_;
enum_type_t*enum_type_;
struct_type_t*struct_type_;

View File

@ -196,11 +196,20 @@ ostream&operator<<(ostream&out, const list<NetNet::range_t>&rlist)
return out;
}
ostream&operator<<(ostream&out, const vector<NetNet::range_t>&rlist)
{
for (vector<NetNet::range_t>::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_;

View File

@ -1873,7 +1873,7 @@ bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net,
{
list<index_component_t> 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<NetEConst*>(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.
list<NetExpr*>unpacked_indices;
list<long> 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);

View File

@ -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.
list<NetExpr*>unpacked_indices;
list<long> 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<long>::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<NetEConst*>(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 << "+:";

View File

@ -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<long> 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<NetEConst*>(tmp_ex);
if (!tmp) {
// Evaluate all the index expressions into an
// "unpacked_indices" array.
list<NetExpr*>unpacked_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<NetEConst*>(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;
}

View File

@ -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_);
list<NetNet::range_t>unpacked_dimensions;
NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true);
NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true);
for (list<pform_range_t>::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

View File

@ -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;
}
}

View File

@ -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<NetNet::range_t>&unpacked)
{
unsigned long r;
if (s >= e) {
r = s - e;
} else {
r = e - s;
unsigned long sum = 1;
for (list<NetNet::range_t>::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<NetNet::range_t>&packed, long array_s, long array_e)
: NetObj(s, n, calculate_count(array_s, array_e)),
const list<NetNet::range_t>&packed,
const list<NetNet::range_t>&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<NetNet::range_t>::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<long>&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;

View File

@ -591,7 +591,8 @@ class NetNet : public NetObj {
explicit NetNet(NetScope*s, perm_string n, Type t,
const std::list<range_t>&packed);
explicit NetNet(NetScope*s, perm_string n, Type t,
const std::list<range_t>&packed, long s0, long e0);
const std::list<range_t>&packed,
const std::list<range_t>&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<range_t>& packed_dims() const { return packed_dims_; }
const std::vector<range_t>& 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<range_t> packed_dims_;
const unsigned dimensions_;
long s0_, e0_;
std::vector<range_t> 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.

View File

@ -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<long>&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<long> 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<long>::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<NetExpr*> val)
{
for (list<NetExpr*>::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<index_component_t>&src, unsigned count,
// True if the expression MUST be constant.
bool need_const,
// These are the outputs.
list<NetExpr*>&indices, list<long>&indices_const)
{
ivl_assert(*loc, count <= src.size());
bool flag = true;
for (list<index_component_t>::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<NetEConst*> (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<NetNet::range_t>&dims,
vector<long>&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<long>&indices)
{
const vector<NetNet::range_t>&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<long> stride (dims.size());
make_strides(dims, stride);
int64_t canonical_addr = 0;
int idx = 0;
for (list<long>::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<NetExpr*>&indices)
{
const vector<NetNet::range_t>&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<long> stride (dims.size());
make_strides(dims, stride);
NetExpr*canonical_expr = 0;
int idx = 0;
for (list<NetExpr*>::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<NetEConst*> (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)

View File

@ -142,8 +142,37 @@ extern NetExpr *normalize_variable_part_base(const list<long>&indices, NetExpr*b
extern NetExpr*normalize_variable_slice_base(const list<long>&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 <class TYPE> struct __IndicesManip {
inline __IndicesManip(const std::list<TYPE>&v) : val(v) { }
const std::list<TYPE>&val;
};
template <class TYPE> inline __IndicesManip<TYPE> as_indices(const std::list<TYPE>&indices)
{ return __IndicesManip<TYPE>(indices); }
extern ostream& operator << (ostream&o, __IndicesManip<long>);
extern ostream& operator << (ostream&o, __IndicesManip<NetExpr*>);
/*
* 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<index_component_t>&src, unsigned count,
// True if the expression MUST be constant.
bool need_const,
// These are the outputs.
list<NetExpr*>&indices, list<long>&indices_const);
extern NetExpr*normalize_variable_unpacked(const NetNet*net, list<long>&indices);
extern NetExpr*normalize_variable_unpacked(const NetNet*net, list<NetExpr*>&indices);
/*
* This function takes as input a NetNet signal and adds a constant

22
parse.y
View File

@ -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;
}
;

View File

@ -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<pform_range_t>*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,

View File

@ -294,7 +294,8 @@ extern void pform_set_net_range(list<perm_string>*names,
bool signed_flag,
ivl_variable_type_t,
std::list<named_pexpr_t>*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<pform_range_t>*indices);
extern void pform_set_reg_integer(list<perm_string>*names, list<named_pexpr_t>*attr);
extern void pform_set_reg_time(list<perm_string>*names, list<named_pexpr_t>*attr);

View File

@ -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<pform_range_t>::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 << "]";
}

View File

@ -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<NetNet::range_t>& 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: "

View File

@ -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