Support nested struct l-values.
This commit is contained in:
parent
b639c4c9aa
commit
1c281c2d77
7
PExpr.h
7
PExpr.h
|
|
@ -433,10 +433,10 @@ class PEIdent : public PExpr {
|
|||
bool need_const_idx) const;
|
||||
NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*,
|
||||
NetNet*,
|
||||
const perm_string&) const;
|
||||
pform_name_t) const;
|
||||
bool elaborate_lval_net_packed_member_(Design*, NetScope*,
|
||||
NetAssign_*,
|
||||
const name_component_t&) const;
|
||||
pform_name_t member_path) const;
|
||||
bool elaborate_lval_darray_bit_(Design*, NetScope*,
|
||||
NetAssign_*) const;
|
||||
|
||||
|
|
@ -530,8 +530,7 @@ class PEIdent : public PExpr {
|
|||
NetNet* elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||
bool bidirectional_flag) const;
|
||||
|
||||
NetAssign_*scan_lname_for_nested_members_(Design*des, NetScope*scope,
|
||||
const pform_name_t&path) const;
|
||||
|
||||
bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig,
|
||||
long&midx, long&lidx) const;
|
||||
};
|
||||
|
|
|
|||
497
elab_lval.cc
497
elab_lval.cc
|
|
@ -146,57 +146,6 @@ NetAssign_* PEConcat::elaborate_lval(Design*des,
|
|||
return res;
|
||||
}
|
||||
|
||||
NetAssign_*PEIdent::scan_lname_for_nested_members_(Design*des, NetScope*scope,
|
||||
const pform_name_t&cur_path) const
|
||||
{
|
||||
if (cur_path.size() == 1)
|
||||
return 0;
|
||||
|
||||
pform_name_t use_path = cur_path;
|
||||
name_component_t tail = use_path.back();
|
||||
use_path.pop_back();
|
||||
|
||||
NetNet* reg = 0;
|
||||
const NetExpr*par = 0;
|
||||
NetEvent* eve = 0;
|
||||
symbol_search(this, des, scope, use_path, reg, par, eve);
|
||||
|
||||
if (reg == 0) {
|
||||
NetAssign_*tmp = scan_lname_for_nested_members_(des, scope, use_path);
|
||||
if (tmp == 0)
|
||||
return 0;
|
||||
|
||||
tmp = new NetAssign_(tmp);
|
||||
tmp->set_property(tail.name);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
if (reg->struct_type() && reg->struct_type()->packed()) {
|
||||
NetAssign_*tmp = new NetAssign_(reg);
|
||||
elaborate_lval_net_packed_member_(des, scope, tmp, tail);
|
||||
return tmp;
|
||||
}
|
||||
#if 0
|
||||
if (reg->struct_type() && reg->struct_type()->packed()) {
|
||||
cerr << get_fileline() << ": sorry: "
|
||||
<< "I don't know what to do with packed struct " << use_path
|
||||
<< " with member " << tail << "." << endl;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (reg->struct_type() && !reg->struct_type()->packed()) {
|
||||
cerr << get_fileline() << ": sorry: "
|
||||
<< "I don't know what to do with unpacked struct " << use_path
|
||||
<< " with member " << tail << "." << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (reg->class_type()) {
|
||||
return elaborate_lval_net_class_member_(des, scope, reg, tail.name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the ident as an l-value. This includes bit and part selects
|
||||
|
|
@ -210,7 +159,6 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
NetNet* reg = 0;
|
||||
const NetExpr*par = 0;
|
||||
NetEvent* eve = 0;
|
||||
perm_string method_name;
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval: "
|
||||
|
|
@ -231,32 +179,28 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
ivl_assert(*this, use_scope);
|
||||
}
|
||||
|
||||
symbol_search(this, des, use_scope, path_, reg, par, eve);
|
||||
|
||||
/* If the signal is not found, check to see if this is a
|
||||
member of a struct. Take the name of the form "a.b.member",
|
||||
remove the member and store it into method_name, and retry
|
||||
the search with "a.b". */
|
||||
if (reg == 0 && path_.size() >= 2) {
|
||||
pform_name_t use_path = path_;
|
||||
perm_string tmp_name = peek_tail_name(use_path);
|
||||
use_path.pop_back();
|
||||
symbol_search(this, des, use_scope, use_path, reg, par, eve);
|
||||
|
||||
if (reg && reg->struct_type()) {
|
||||
method_name = tmp_name;
|
||||
|
||||
} else if (reg && reg->class_type()) {
|
||||
method_name = tmp_name;
|
||||
|
||||
} else if (NetAssign_*subl = scan_lname_for_nested_members_(des, use_scope, path_)) {
|
||||
return subl;
|
||||
|
||||
} else {
|
||||
reg = 0;
|
||||
}
|
||||
/* Try to find the base part of the path that names the
|
||||
variable. The remainer is the member path. For example, if
|
||||
the path is a.b.c.d, and a.b is the path to a variable,
|
||||
then a.b becomes the base_path and c.d becomes the
|
||||
member_path. If we cannot find the variable with any
|
||||
prefix, then the base_path will be empty after this loop
|
||||
and reg will remain nil. */
|
||||
pform_name_t base_path = path_;
|
||||
pform_name_t member_path;
|
||||
while (reg == 0 && base_path.size() > 0) {
|
||||
symbol_search(this, des, use_scope, base_path, reg, par, eve);
|
||||
// Found it!
|
||||
if (reg != 0) break;
|
||||
// Not found. Try to pop another name off the base_path
|
||||
// and push it to the front of the member_path.
|
||||
member_path.push_front( base_path.back() );
|
||||
base_path.pop_back();
|
||||
}
|
||||
|
||||
|
||||
/* The l-value must be a variable. If not, then give up and
|
||||
print a useful error message. */
|
||||
if (reg == 0) {
|
||||
if (use_scope->type()==NetScope::FUNC
|
||||
&& use_scope->func_def()->return_sig()==0
|
||||
|
|
@ -278,7 +222,10 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval: "
|
||||
<< "Found l-value as reg."
|
||||
<< "Found l-value path_=" << path_
|
||||
<< " as reg=" << reg->name()
|
||||
<< " base_path=" << base_path
|
||||
<< ", member_path=" << member_path
|
||||
<< " unpacked_dimensions()=" << reg->unpacked_dimensions() << endl;
|
||||
}
|
||||
|
||||
|
|
@ -324,21 +271,27 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (reg->struct_type() && !method_name.nil()) {
|
||||
|
||||
// If we find that the matched variable is a packed struct,
|
||||
// then we can handled it with the net_packed_member_ method.
|
||||
if (reg->struct_type() && member_path.size() > 0) {
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
name_component_t tmp_name (method_name);
|
||||
elaborate_lval_net_packed_member_(des, use_scope, lv, tmp_name);
|
||||
elaborate_lval_net_packed_member_(des, use_scope, lv, member_path);
|
||||
return lv;
|
||||
}
|
||||
|
||||
if (reg->class_type() && !method_name.nil() && gn_system_verilog()) {
|
||||
NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, method_name);
|
||||
// If the variable is a class object, then handle it with the
|
||||
// net_class_member_ method.
|
||||
if (reg->class_type() && member_path.size() > 0 && gn_system_verilog()) {
|
||||
NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, member_path);
|
||||
return lv;
|
||||
}
|
||||
|
||||
|
||||
// Past this point, we should have taken care of the cases
|
||||
// where the name is a member/method of a struct/class.
|
||||
ivl_assert(*this, method_name.nil());
|
||||
// XXXX ivl_assert(*this, method_name.nil());
|
||||
ivl_assert(*this, member_path.size() == 0);
|
||||
|
||||
bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE);
|
||||
|
||||
|
|
@ -1063,89 +1016,137 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* When the l-value turns out to be a class object, this method is
|
||||
* called with the bound variable, and the method path. For example,
|
||||
* if path_=a.b.c and a.b binds to the variable, then sig is b, and
|
||||
* member_path=c. if path_=obj.base.x, and base_path=obj, then sig is
|
||||
* obj, and member_path=base.x.
|
||||
*/
|
||||
NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope,
|
||||
NetNet*sig, const perm_string&method_name) const
|
||||
NetNet*sig, pform_name_t member_path) const
|
||||
{
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": elaborate_lval_net_class_member_: "
|
||||
<< "l-value is property " << method_name
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
|
||||
<< "l-value is property " << member_path
|
||||
<< " of " << sig->name() << "." << endl;
|
||||
}
|
||||
|
||||
const netclass_t*class_type = sig->class_type();
|
||||
ivl_assert(*this, class_type);
|
||||
|
||||
/* Make sure the property is really present in the class. If
|
||||
not, then generate an error message and return an error. */
|
||||
int pidx = class_type->property_idx_from_name(method_name);
|
||||
if (pidx < 0) {
|
||||
cerr << get_fileline() << ": error: Class " << class_type->get_name()
|
||||
<< " does not have a property " << method_name << "." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
// Iterate over the member_path. This handles nested class
|
||||
// object, by generating nested NetAssign_ object. We start
|
||||
// with lv==0, so the front of the member_path is the member
|
||||
// of the outermost class. This generates an lv from sig. Then
|
||||
// iterate over the remaining of the member_path, replacing
|
||||
// the outer lv with an lv that nests the lv from the previous
|
||||
// iteration.
|
||||
NetAssign_*lv = 0;
|
||||
do {
|
||||
// Start with the first component of the member path...
|
||||
perm_string method_name = peek_head_name(member_path);
|
||||
// Pull that component from the member_path. We need to
|
||||
// know the current member being worked on, and will
|
||||
// need to know if there are more members to be worked on.
|
||||
name_component_t member_cur = member_path.front();
|
||||
member_path.pop_front();
|
||||
|
||||
property_qualifier_t qual = class_type->get_prop_qual(pidx);
|
||||
if (qual.test_local() && ! class_type->test_scope_is_method(scope)) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Local property " << class_type->get_prop_name(pidx)
|
||||
<< " is not accessible (l-value) in this context."
|
||||
<< " (scope=" << scope_path(scope) << ")" << endl;
|
||||
des->errors += 1;
|
||||
|
||||
} else if (qual.test_static()) {
|
||||
|
||||
// Special case: this is a static property. Ignore the
|
||||
// "this" sig and use the property itself, which is not
|
||||
// part of the sig, as the l-value.
|
||||
NetNet*psig = class_type->find_static_property(method_name);
|
||||
ivl_assert(*this, psig);
|
||||
|
||||
NetAssign_*lv = new NetAssign_(psig);
|
||||
return lv;
|
||||
|
||||
} else if (qual.test_const()) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Property " << class_type->get_prop_name(pidx)
|
||||
<< " is constant in this context." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
|
||||
NetAssign_*lv = new NetAssign_(sig);
|
||||
lv->set_property(method_name);
|
||||
|
||||
ivl_type_t ptype = class_type->get_prop_type(pidx);
|
||||
const netdarray_t*mtype = dynamic_cast<const netdarray_t*> (ptype);
|
||||
if (mtype) {
|
||||
const name_component_t&name_tail = path_.back();
|
||||
if (! name_tail.index.empty()) {
|
||||
cerr << get_fileline() << ": sorry: "
|
||||
<< "Array index of array properties not supported."
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
|
||||
<< "Processing member_cur=" << member_cur
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// Make sure the property is really present in the class. If
|
||||
// not, then generate an error message and return an error.
|
||||
int pidx = class_type->property_idx_from_name(method_name);
|
||||
if (pidx < 0) {
|
||||
cerr << get_fileline() << ": error: Class " << class_type->get_name()
|
||||
<< " does not have a property " << method_name << "." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
property_qualifier_t qual = class_type->get_prop_qual(pidx);
|
||||
if (qual.test_local() && ! class_type->test_scope_is_method(scope)) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Local property " << class_type->get_prop_name(pidx)
|
||||
<< " is not accessible (l-value) in this context."
|
||||
<< " (scope=" << scope_path(scope) << ")" << endl;
|
||||
des->errors += 1;
|
||||
|
||||
} else if (qual.test_static()) {
|
||||
|
||||
// Special case: this is a static property. Ignore the
|
||||
// "this" sig and use the property itself, which is not
|
||||
// part of the sig, as the l-value.
|
||||
NetNet*psig = class_type->find_static_property(method_name);
|
||||
ivl_assert(*this, psig);
|
||||
|
||||
lv = new NetAssign_(psig);
|
||||
return lv;
|
||||
|
||||
} else if (qual.test_const()) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Property " << class_type->get_prop_name(pidx)
|
||||
<< " is constant in this context." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
lv = lv? new NetAssign_(lv) : new NetAssign_(sig);
|
||||
lv->set_property(method_name);
|
||||
|
||||
// Now get the type of the property.
|
||||
ivl_type_t ptype = class_type->get_prop_type(pidx);
|
||||
const netdarray_t*mtype = dynamic_cast<const netdarray_t*> (ptype);
|
||||
if (mtype) {
|
||||
if (! member_cur.index.empty()) {
|
||||
cerr << get_fileline() << ": sorry: "
|
||||
<< "Array index of array properties not supported."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current member is a class object, then get the
|
||||
// type. We may wind up iterating, and need the proper
|
||||
// class type.
|
||||
class_type = dynamic_cast<const netclass_t*>(ptype);
|
||||
|
||||
} while (member_path.size() > 0);
|
||||
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This method is caled to handle l-value identifiers that are packed
|
||||
* structs. The lv is already attached to the variable, so this method
|
||||
* calculates the part select that is defined by the member_path. For
|
||||
* example, if the path_ is main.sub.sub_local, and the variable is
|
||||
* main, then we know at this point that main is a packed struct, and
|
||||
* lv contains the reference to the bound variable (main). In this
|
||||
* case member_path==sub.sub_local, and it is up to this method to
|
||||
* work out the part select that the member_path represents.
|
||||
*/
|
||||
bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
|
||||
NetAssign_*lv,
|
||||
const name_component_t&member_comp) const
|
||||
pform_name_t member_path) const
|
||||
{
|
||||
const perm_string&member_name = member_comp.name;
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
|
||||
<< "path_=" << path_
|
||||
<< " member_path=" << member_path
|
||||
<< endl;
|
||||
}
|
||||
|
||||
NetNet*reg = lv->sig();
|
||||
ivl_assert(*this, reg);
|
||||
|
||||
const netstruct_t*struct_type = reg->struct_type();
|
||||
ivl_assert(*this, struct_type);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: elaborate lval packed member: "
|
||||
<< "path_=" << path_ << endl;
|
||||
}
|
||||
|
||||
if (! struct_type->packed()) {
|
||||
cerr << get_fileline() << ": sorry: Only packed structures "
|
||||
<< "are supported in l-value." << endl;
|
||||
|
|
@ -1153,99 +1154,169 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Shouldn't be seeing unpacked arrays of packed structs...
|
||||
ivl_assert(*this, reg->unpacked_dimensions() == 0);
|
||||
|
||||
// This is a packed member, so the name is of the form
|
||||
// "a.b[...].c[...]" which means that the path_ must have at
|
||||
// least 2 components. We are processing "c[...]" at that
|
||||
// point (otherwise known as member_name) so we'll save a
|
||||
// reference to it in name_tail. We are also processing "b[]"
|
||||
// so save that as name_base.
|
||||
|
||||
ivl_assert(*this, path_.size() >= 2);
|
||||
|
||||
// Looking for the base name. We need that to know about
|
||||
// indices we may need to apply. This is to handle the case
|
||||
// that the base is an array of structs, and not just a
|
||||
// struct.
|
||||
pform_name_t::const_reverse_iterator name_idx = path_.rbegin();
|
||||
ivl_assert(*this, name_idx->name == member_name);
|
||||
const name_component_t&name_tail = *name_idx;
|
||||
for (size_t idx = 1 ; idx < member_path.size() ; idx += 1)
|
||||
++ name_idx;
|
||||
if (name_idx->name != peek_head_name(member_path)) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "name_idx=" << name_idx->name
|
||||
<< ", expecting member_name=" << peek_head_name(member_path)
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
ivl_assert(*this, name_idx->name == peek_head_name(member_path));
|
||||
++ name_idx;
|
||||
const name_component_t&name_base = *name_idx;
|
||||
|
||||
// Calculate the offset within the packed structure of the
|
||||
// member, and any indices. We will add in the offset of the
|
||||
// struct into the packed array later. Note that this works
|
||||
// for packed unions as well (although the offset will be 0
|
||||
// for union members).
|
||||
unsigned long off;
|
||||
const netstruct_t::member_t* member = struct_type->packed_member(member_name, off);
|
||||
// Shouldn't be seeing unpacked arrays of packed structs...
|
||||
ivl_assert(*this, reg->unpacked_dimensions() == 0);
|
||||
|
||||
if (member == 0) {
|
||||
cerr << get_fileline() << ": error: Member " << member_name
|
||||
<< " is not a member of variable " << reg->name() << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
// These make up the "part" select that is the equivilent of
|
||||
// following the member path through the nested structs. To
|
||||
// start with, the off[set] is zero, and use_width is the
|
||||
// width of the entire variable. The first member_comp is at
|
||||
// some offset within the variable, and will have a reduced
|
||||
// width. As we step through the member_path the off
|
||||
// increases, and use_width shrinks.
|
||||
unsigned long off = 0;
|
||||
unsigned long use_width = struct_type->packed_width();
|
||||
|
||||
unsigned long use_width = member->net_type->packed_width();
|
||||
pform_name_t completed_path;
|
||||
do {
|
||||
const name_component_t member_comp = member_path.front();
|
||||
const perm_string&member_name = member_comp.name;
|
||||
|
||||
// Get the index component type. At this point, we only
|
||||
// support bit select or none.
|
||||
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
||||
if (!name_tail.index.empty())
|
||||
use_sel = name_tail.index.back().sel;
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
|
||||
<< "Processing member_comp=" << member_comp
|
||||
<< " (completed_path=" << completed_path << ")"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) {
|
||||
cerr << get_fileline() << ": sorry: Assignments to part selects of "
|
||||
"a struct member are not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
// This is a packed member, so the name is of the form
|
||||
// "a.b[...].c[...]" which means that the path_ must have at
|
||||
// least 2 components. We are processing "c[...]" at that
|
||||
// point (otherwise known as member_name) and we have a
|
||||
// reference to it in member_comp.
|
||||
|
||||
if (! name_tail.index.empty()) {
|
||||
// The member_path is the members we want to follow for the
|
||||
// variable. For example, main[N].a.b may have main[N] as the
|
||||
// base_name, and member_path=a.b. The member_name is the
|
||||
// start of the member_path, and is "a". The member_name
|
||||
// should be a member of the struct_type type.
|
||||
|
||||
// If there are index expressions in this l-value
|
||||
// expression, then the implicit assumption is that the
|
||||
// member is a vector type with packed dimensions. For
|
||||
// example, if the l-value expression is "foo.member[1][2]",
|
||||
// then the member should be something like:
|
||||
// ... logic [h:l][m:n] foo;
|
||||
// Get the dimensions from the netvector_t that this implies.
|
||||
const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member->net_type);
|
||||
ivl_assert(*this, mem_vec);
|
||||
const vector<netrange_t>&mem_packed_dims = mem_vec->packed_dims();
|
||||
// Calculate the offset within the packed structure of the
|
||||
// member, and any indices. We will add in the offset of the
|
||||
// struct into the packed array later. Note that this works
|
||||
// for packed unions as well (although the offset will be 0
|
||||
// for union members).
|
||||
unsigned long tmp_off;
|
||||
const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off);
|
||||
|
||||
if (name_tail.index.size() > mem_packed_dims.size()) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Too many index expressions for member." << endl;
|
||||
if (member == 0) {
|
||||
cerr << get_fileline() << ": error: Member " << member_name
|
||||
<< " is not a member of struct type of "
|
||||
<< reg->name()
|
||||
<< "." << completed_path << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
|
||||
<< "Member type: " << *(member->net_type)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
off += tmp_off;
|
||||
ivl_assert(*this, use_width >= (unsigned long)member->net_type->packed_width());
|
||||
use_width = member->net_type->packed_width();
|
||||
|
||||
// At this point, off and use_width are the part select
|
||||
// expressed by the member_comp, which is a member of the
|
||||
// struct. We can further refine the part select with any
|
||||
// indices that might be present.
|
||||
|
||||
// Get the index component type. At this point, we only
|
||||
// support bit select or none.
|
||||
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
||||
if (!member_comp.index.empty())
|
||||
use_sel = member_comp.index.back().sel;
|
||||
|
||||
if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) {
|
||||
cerr << get_fileline() << ": sorry: Assignments to part selects of "
|
||||
"a struct member are not yet supported." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate all but the last index expression, into prefix_indices.
|
||||
list<long>prefix_indices;
|
||||
bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index);
|
||||
ivl_assert(*this, rc);
|
||||
if (! member_comp.index.empty()) {
|
||||
|
||||
// Evaluate the last index expression into a constant long.
|
||||
NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true);
|
||||
long tmp;
|
||||
if (texpr == 0 || !eval_as_long(tmp, texpr)) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
"Array index expressions must be constant here." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
// If there are index expressions in this l-value
|
||||
// expression, then the implicit assumption is that the
|
||||
// member is a vector type with packed dimensions. For
|
||||
// example, if the l-value expression is "foo.member[1][2]",
|
||||
// then the member should be something like:
|
||||
// ... logic [h:l][m:n] foo;
|
||||
// Get the dimensions from the netvector_t that this implies.
|
||||
const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member->net_type);
|
||||
ivl_assert(*this, mem_vec);
|
||||
const vector<netrange_t>&mem_packed_dims = mem_vec->packed_dims();
|
||||
|
||||
if (member_comp.index.size() > mem_packed_dims.size()) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
<< "Too many index expressions for member." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate all but the last index expression, into prefix_indices.
|
||||
list<long>prefix_indices;
|
||||
bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index);
|
||||
ivl_assert(*this, rc);
|
||||
|
||||
// Evaluate the last index expression into a constant long.
|
||||
NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true);
|
||||
long tmp;
|
||||
if (texpr == 0 || !eval_as_long(tmp, texpr)) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
"Array index expressions must be constant here." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete texpr;
|
||||
|
||||
// Now use the prefix_to_slice function to calculate the
|
||||
// offset and width of the addressed slice of the member.
|
||||
long loff;
|
||||
unsigned long lwid;
|
||||
prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid);
|
||||
|
||||
off += loff;
|
||||
use_width = lwid;
|
||||
}
|
||||
|
||||
delete texpr;
|
||||
// Complete this component of the path, mark it
|
||||
// completed, and set up for the next component.
|
||||
struct_type = dynamic_cast<const netstruct_t*> (member->net_type);
|
||||
completed_path .push_back(member_comp);
|
||||
member_path.pop_front();
|
||||
|
||||
// Now use the prefix_to_slice function to calculate the
|
||||
// offset and width of the addressed slice of the member.
|
||||
long loff;
|
||||
unsigned long lwid;
|
||||
prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid);
|
||||
} while (member_path.size() > 0 && struct_type != 0);
|
||||
|
||||
off += loff;
|
||||
use_width = lwid;
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
|
||||
<< "After processing member_path, "
|
||||
<< "off=" << off
|
||||
<< ", use_width=" << use_width
|
||||
<< ", completed_path=" << completed_path
|
||||
<< ", member_path=" << member_path
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// The dimensions in the expression must match the packed
|
||||
|
|
|
|||
Loading…
Reference in New Issue