Consolidate unpacked array type elaboration

There are currently two implementations for elaborating unpacked array
types. One that is used when elaborating a signal with an unpacked array
type and one that is used everywhere else using the elaborate_type()
infrastructure.

The elaborate_type() implementation is less complete and for example does
not support bounded queue types.

Consolidate both into a single implementation to reduce duplicated code and
get consistent behavior. This for example makes sure that the maximum queue
size is respected when used as a function return type.

Nested data structures of arrays, dynamic arrays or queues are not yet
supported. In the current implementation when encountering such a type an
assert will be triggered and the application crashes. In the new
implementation an error message will be printed without crashing the
application.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-03-26 16:40:32 +01:00
parent 66a5cfe660
commit 724d7d4282
7 changed files with 175 additions and 173 deletions

View File

@ -965,28 +965,6 @@ ivl_type_t PWire::elaborate_type(Design*des, NetScope*scope,
return vec;
}
ivl_type_t PWire::elaborate_darray_type(Design*des, NetScope*scope,
const char *darray_type,
const std::vector<netrange_t>&packed_dimensions)
const
{
ivl_type_t type = elaborate_type(des, scope, packed_dimensions);
if (dynamic_cast<const netvector_t*>(type) ||
dynamic_cast<const netparray_t*>(type) ||
dynamic_cast<const netreal_t*>(type) ||
dynamic_cast<const netstring_t*>(type))
return type;
cerr << get_fileline() << ": Sorry: "
<< darray_type << " of type `" << *type
<< "` is not yet supported." << endl;
des->errors++;
// Return something to recover
return new netvector_t(IVL_VT_LOGIC);
}
/*
* Elaborate a source wire. The "wire" is the declaration of wires,
* registers, ports and memories. The parser has already merged the
@ -1136,86 +1114,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib,
des, scope);
list<netrange_t>unpacked_dimensions;
netdarray_t*netdarray = 0;
for (list<pform_range_t>::const_iterator cur = unpacked_.begin()
; cur != unpacked_.end() ; ++cur) {
PExpr*use_lidx = cur->first;
PExpr*use_ridx = cur->second;
// Special case: If we encounter an undefined
// dimensions, then turn this into a dynamic array and
// put all the packed dimensions there.
if (use_lidx==0 && use_ridx==0) {
ivl_type_t base_type = elaborate_darray_type(des,
array_type_scope,
"Dynamic array",
packed_dimensions);
packed_dimensions.clear();
ivl_assert(*this, netdarray==0);
netdarray = new netdarray_t(base_type);
continue;
}
// Special case: Detect the mark for a QUEUE
// declaration, which is the dimensions [null:max_idx].
if (dynamic_cast<PENull*>(use_lidx)) {
ivl_type_t base_type = elaborate_darray_type(des,
array_type_scope,
"Queue",
packed_dimensions);
packed_dimensions.clear();
ivl_assert(*this, netdarray==0);
long max_idx;
if (use_ridx) {
NetExpr*tmp = elab_and_eval(des, array_type_scope, use_ridx,
-1, true);
NetEConst*cv = dynamic_cast<NetEConst*>(tmp);
if (cv == 0) {
cerr << get_fileline() << ": error: queue '" << name_
<< "' bound must be a constant!" << endl;
des->errors += 1;
max_idx = -1;
} else {
verinum res = cv->value();
if (res.is_defined()) {
max_idx = res.as_long();
if (max_idx < 0) {
cerr << get_fileline() << ": error: queue '"
<< name_ << "' bound must be positive ("
<< max_idx << ")!" << endl;
des->errors += 1;
max_idx = -1;
}
} else {
cerr << get_fileline() << ": error: queue '" << name_
<< "' bound is undefined!" << endl;
des->errors += 1;
max_idx = -1;
}
}
} else max_idx = -1;
netdarray = new netqueue_t(base_type, max_idx);
continue;
}
// Cannot handle dynamic arrays/queues of arrays yet.
ivl_assert(*this, netdarray==0);
long index_l, index_r;
evaluate_range(des, array_type_scope, this, *cur, index_l, index_r);
if (abs(index_r - index_l) > warn_dimension_size) {
cerr << get_fileline() << ": warning: Array dimension "
"is greater than " << warn_dimension_size
<< "." << endl;
}
unpacked_dimensions.push_back(netrange_t(index_l, index_r));
}
if (data_type_ == IVL_VT_REAL && !packed_dimensions.empty()) {
cerr << get_fileline() << ": error: real ";
if (wtype == NetNet::REG) cerr << "variable";
@ -1263,37 +1161,31 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
wtype = NetNet::WIRE;
}
ivl_type_t type = elaborate_type(des, array_type_scope, packed_dimensions);
// Create the type for the unpacked dimensions. If the
// unpacked_dimensions are empty this will just return the base type.
type = elaborate_array_type(des, array_type_scope, *this, type, unpacked_);
NetNet*sig = 0;
if (netdarray) {
if (debug_elaborate) {
cerr << get_fileline() << ": PWire::elaborate_sig: "
<< "Create signal wtype=" << wtype
<< " name=" << name_
<< " netdarray=" << *netdarray
<< " in scope " << scope_path(scope) << endl;
}
ivl_assert(*this, packed_dimensions.empty());
ivl_assert(*this, unpacked_dimensions.empty());
sig = new NetNet(scope, name_, wtype, netdarray);
} else {
ivl_type_t use_type = elaborate_type(des, array_type_scope,
packed_dimensions);
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Create signal "
<< wtype << " " << *set_data_type_
<< " " << name_ << unpacked_dimensions
<< " in scope " << scope_path(scope) << endl;
}
sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type);
list<netrange_t> unpacked_dimensions;
// If this is an unpacked array extract the base type and unpacked
// dimensions as these are separate properties of the NetNet.
if (const netuarray_t *atype = dynamic_cast<const netuarray_t*>(type)) {
unpacked_dimensions.insert(unpacked_dimensions.begin(),
atype->static_dimensions().begin(),
atype->static_dimensions().end());
type = atype->element_type();
}
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Create signal " << wtype;
if (set_data_type_)
cout << " " << *set_data_type_;
cout << name_ << unpacked_dimensions << " in scope "
<< scope_path(scope) << endl;
}
NetNet*sig = new NetNet(scope, name_, wtype, unpacked_dimensions, type);
if (wtype == NetNet::WIRE) sig->devirtualize_pins();
sig->set_line(*this);
sig->port_type(port_type_);

View File

@ -222,39 +222,145 @@ ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
return res;
}
static ivl_type_t elaborate_darray_check_type(Design *des, const LineInfo &li,
ivl_type_t type,
const char *darray_type)
{
if (dynamic_cast<const netvector_t*>(type) ||
dynamic_cast<const netparray_t*>(type) ||
dynamic_cast<const netreal_t*>(type) ||
dynamic_cast<const netstring_t*>(type))
return type;
cerr << li.get_fileline() << ": Sorry: "
<< darray_type << " of type `" << *type
<< "` is not yet supported." << endl;
des->errors++;
// Return something to recover
return new netvector_t(IVL_VT_LOGIC);
}
static ivl_type_t elaborate_queue_type(Design *des, NetScope *scope,
const LineInfo &li, ivl_type_t base_type,
PExpr *ridx)
{
base_type = elaborate_darray_check_type(des, li, base_type, "Queue");
long max_idx = -1;
if (ridx) {
NetExpr*tmp = elab_and_eval(des, scope, ridx, -1, true);
NetEConst*cv = dynamic_cast<NetEConst*>(tmp);
if (cv == 0) {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be constant."
<< endl;
des->errors++;
} else {
verinum res = cv->value();
if (res.is_defined()) {
max_idx = res.as_long();
if (max_idx < 0) {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be positive ("
<< max_idx << ")." << endl;
des->errors++;
max_idx = -1;
}
} else {
cerr << li.get_fileline() << ": error: "
<< "queue bound must be defined."
<< endl;
des->errors++;
}
}
delete cv;
}
return new netqueue_t(base_type, max_idx);
}
// If dims is not empty create a unpacked array type and clear dims, otherwise
// return the base type. Also check that we actually support the base type.
static ivl_type_t elaborate_static_array_type(Design *des, const LineInfo &li,
ivl_type_t base_type,
std::vector<netrange_t> &dims)
{
if (dims.empty())
return base_type;
if (dynamic_cast<const netqueue_t*>(base_type)) {
cerr << li.get_fileline() << ": sorry: "
<< "array of queue type is not yet supported."
<< endl;
des->errors++;
// Recover
base_type = new netvector_t(IVL_VT_LOGIC);
} else if (dynamic_cast<const netdarray_t*>(base_type)) {
cerr << li.get_fileline() << ": sorry: "
<< "array of dynamic array type is not yet supported."
<< endl;
des->errors++;
// Recover
base_type = new netvector_t(IVL_VT_LOGIC);
}
ivl_type_t type = new netuarray_t(dims, base_type);
dims.clear();
return type;
}
ivl_type_t elaborate_array_type(Design *des, NetScope *scope,
const LineInfo &li, ivl_type_t base_type,
const list<pform_range_t> &dims)
{
const long warn_dimension_size = 1 << 30;
std::vector<netrange_t> dimensions;
dimensions.reserve(dims.size());
ivl_type_t type = base_type;
for (list<pform_range_t>::const_iterator cur = dims.begin();
cur != dims.end() ; ++cur) {
PExpr *lidx = cur->first;
PExpr *ridx = cur->second;
if (lidx == 0 && ridx == 0) {
// Special case: If we encounter an undefined dimensions,
// then turn this into a dynamic array and put all the
// packed dimensions there.
type = elaborate_static_array_type(des, li, type, dimensions);
type = elaborate_darray_check_type(des, li, type, "Dynamic array");
type = new netdarray_t(type);
continue;
} else if (dynamic_cast<PENull*>(lidx)) {
// Special case: Detect the mark for a QUEUE declaration,
// which is the dimensions [null:max_idx].
type = elaborate_static_array_type(des, li, type, dimensions);
type = elaborate_queue_type(des, scope, li, type, ridx);
continue;
}
long index_l, index_r;
evaluate_range(des, scope, &li, *cur, index_l, index_r);
if (abs(index_r - index_l) > warn_dimension_size) {
cerr << li.get_fileline() << ": warning: "
<< "Array dimension is greater than "
<< warn_dimension_size << "."
<< endl;
}
dimensions.push_back(netrange_t(index_l, index_r));
}
return elaborate_static_array_type(des, li, type, dimensions);
}
ivl_type_t uarray_type_t::elaborate_type_raw(Design*des, NetScope*scope) const
{
ivl_type_t btype = base_type->elaborate_type(des, scope);
assert(dims->size() >= 1);
list<pform_range_t>::const_iterator cur = dims->begin();
// Special case: if the dimension is nil:nil, this is a
// dynamic array. Note that we only know how to handle dynamic
// arrays with 1 dimension at a time.
if (cur->first==0 && cur->second==0) {
assert(dims->size()==1);
ivl_type_s*res = new netdarray_t(btype);
return res;
}
// Special case: if the dimension is null:nil. this is a queue.
if (dynamic_cast<PENull*>(cur->first)) {
// FIXME: Need to set the max size if cur->second is defined
ivl_type_s*res = new netqueue_t(btype, -1);
return res;
}
vector<netrange_t> dimensions;
bool dimensions_ok = evaluate_ranges(des, scope, this, dimensions, *dims);
if (!dimensions_ok) {
cerr << get_fileline() << " : warning: "
<< "Bad dimensions for type here." << endl;
}
ivl_assert(*this, btype);
ivl_type_s*res = new netuarray_t(dimensions, btype);
return res;
return elaborate_array_type(des, scope, *this, btype, *dims.get());
}

View File

@ -1,7 +1,7 @@
./ivltests/sv_queue_parray_fail.v:7: error: queue 'q_vec1' bound must be positive (-1)!
./ivltests/sv_queue_parray_fail.v:8: error: queue 'q_vec2' bound is undefined!
./ivltests/sv_queue_parray_fail.v:7: error: queue bound must be positive (-1).
./ivltests/sv_queue_parray_fail.v:8: error: queue bound must be defined.
./ivltests/sv_queue_parray_fail.v:9: error: A reference to a wire or reg (`bound') is not allowed in a constant expression.
./ivltests/sv_queue_parray_fail.v:9: error: queue 'q_vec3' bound must be a constant!
./ivltests/sv_queue_parray_fail.v:9: error: queue bound must be constant.
./ivltests/sv_queue_parray_fail.v:12: error: size() method takes no arguments
./ivltests/sv_queue_parray_fail.v:13: error: pop_front() method takes no arguments
./ivltests/sv_queue_parray_fail.v:14: error: pop_back() method takes no arguments

View File

@ -1,7 +1,7 @@
./ivltests/sv_queue_real_fail.v:4: error: queue 'q_real1' bound must be positive (-1)!
./ivltests/sv_queue_real_fail.v:5: error: queue 'q_real2' bound is undefined!
./ivltests/sv_queue_real_fail.v:4: error: queue bound must be positive (-1).
./ivltests/sv_queue_real_fail.v:5: error: queue bound must be defined.
./ivltests/sv_queue_real_fail.v:6: error: A reference to a wire or reg (`bound') is not allowed in a constant expression.
./ivltests/sv_queue_real_fail.v:6: error: queue 'q_real3' bound must be a constant!
./ivltests/sv_queue_real_fail.v:6: error: queue bound must be constant.
./ivltests/sv_queue_real_fail.v:9: error: size() method takes no arguments
./ivltests/sv_queue_real_fail.v:10: error: pop_front() method takes no arguments
./ivltests/sv_queue_real_fail.v:11: error: pop_back() method takes no arguments

View File

@ -1,7 +1,7 @@
./ivltests/sv_queue_string_fail.v:4: error: queue 'q_str1' bound must be positive (-1)!
./ivltests/sv_queue_string_fail.v:5: error: queue 'q_str2' bound is undefined!
./ivltests/sv_queue_string_fail.v:4: error: queue bound must be positive (-1).
./ivltests/sv_queue_string_fail.v:5: error: queue bound must be defined.
./ivltests/sv_queue_string_fail.v:6: error: A reference to a wire or reg (`bound') is not allowed in a constant expression.
./ivltests/sv_queue_string_fail.v:6: error: queue 'q_str3' bound must be a constant!
./ivltests/sv_queue_string_fail.v:6: error: queue bound must be constant.
./ivltests/sv_queue_string_fail.v:9: error: size() method takes no arguments
./ivltests/sv_queue_string_fail.v:10: error: pop_front() method takes no arguments
./ivltests/sv_queue_string_fail.v:11: error: pop_back() method takes no arguments

View File

@ -1,7 +1,7 @@
./ivltests/sv_queue_vec_fail.v:4: error: queue 'q_vec1' bound must be positive (-1)!
./ivltests/sv_queue_vec_fail.v:5: error: queue 'q_vec2' bound is undefined!
./ivltests/sv_queue_vec_fail.v:4: error: queue bound must be positive (-1).
./ivltests/sv_queue_vec_fail.v:5: error: queue bound must be defined.
./ivltests/sv_queue_vec_fail.v:6: error: A reference to a wire or reg (`bound') is not allowed in a constant expression.
./ivltests/sv_queue_vec_fail.v:6: error: queue 'q_vec3' bound must be a constant!
./ivltests/sv_queue_vec_fail.v:6: error: queue bound must be constant.
./ivltests/sv_queue_vec_fail.v:9: error: size() method takes no arguments
./ivltests/sv_queue_vec_fail.v:10: error: pop_front() method takes no arguments
./ivltests/sv_queue_vec_fail.v:11: error: pop_back() method takes no arguments

View File

@ -364,6 +364,10 @@ struct class_type_t : public data_type_t {
virtual SymbolType symbol_type() const;
};
ivl_type_t elaborate_array_type(Design *des, NetScope *scope,
const LineInfo &li, ivl_type_t base_type,
const std::list<pform_range_t> &dims);
/*
* The pform_name_t is the general form for a hierarchical
* identifier. It is an ordered list of name components. Each name