Support nested struct in continuous assign l-values
When doing continuous assignment of packed structs, support the case where the value being assigned is a member of a member, etc. Procedural assignments already support this. See issue#307
This commit is contained in:
parent
1b4244474c
commit
a79b55ae28
|
|
@ -232,8 +232,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
||||||
|
|
||||||
// We are processing the tail of a string of names. For
|
// We are processing the tail of a string of names. For
|
||||||
// example, the Verilog may be "a.b.c", so we are processing
|
// example, the Verilog may be "a.b.c", so we are processing
|
||||||
// "c" at this point. (Note that if method_name is not nil,
|
// "c" at this point.
|
||||||
// then this is "a.b.c.method" and "a.b.c" is a struct or class.)
|
|
||||||
const name_component_t&name_tail = path_.back();
|
const name_component_t&name_tail = path_.back();
|
||||||
|
|
||||||
// Use the last index to determine what kind of select
|
// Use the last index to determine what kind of select
|
||||||
|
|
|
||||||
241
elab_net.cc
241
elab_net.cc
|
|
@ -456,7 +456,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||||
NetNet* sig = 0;
|
NetNet* sig = 0;
|
||||||
const NetExpr*par = 0;
|
const NetExpr*par = 0;
|
||||||
NetEvent* eve = 0;
|
NetEvent* eve = 0;
|
||||||
perm_string method_name;
|
|
||||||
|
|
||||||
symbol_search(this, des, scope, path_, sig, par, eve);
|
symbol_search(this, des, scope, path_, sig, par, eve);
|
||||||
|
|
||||||
|
|
@ -468,33 +467,16 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Break the path_ into the tail name and the prefix. For
|
pform_name_t base_path = path_;
|
||||||
// example, a name "a.b.c" is broken into name_tail="c" and
|
pform_name_t member_path;
|
||||||
// path_prefix="a.b".
|
while (sig == 0 && base_path.size() > 0) {
|
||||||
const name_component_t&path_tail = path_.back();
|
symbol_search(this, des, scope, base_path, sig, par, eve);
|
||||||
pform_name_t path_prefix = path_;
|
// Found it!
|
||||||
path_prefix.pop_back();
|
if (sig != 0) break;
|
||||||
|
// Not found. Try to pop another name off the base_path
|
||||||
/* If the signal is not found, check to see if this is a
|
// and push it to the front of the member_path.
|
||||||
member of a struct. Take the name of the form "a.b.member",
|
member_path.push_front( base_path.back() );
|
||||||
remove the member and store it into method_name, and retry
|
base_path.pop_back();
|
||||||
the search with "a.b". */
|
|
||||||
if (sig == 0 && path_.size() >= 2) {
|
|
||||||
if (debug_elaborate) {
|
|
||||||
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
||||||
"Symbol not found, try again with path_prefix=" << path_prefix
|
|
||||||
<< " and method_name=" << path_tail.name << endl;
|
|
||||||
}
|
|
||||||
method_name = path_tail.name;
|
|
||||||
symbol_search(this, des, scope, path_prefix, sig, par, eve);
|
|
||||||
|
|
||||||
// Whoops, not a struct signal, so give up on this avenue.
|
|
||||||
if (sig && sig->struct_type() == 0) {
|
|
||||||
cerr << get_fileline() << ": XXXXX: sig=" << sig->name()
|
|
||||||
<< " is found, but not a struct with member " << method_name << endl;
|
|
||||||
method_name = perm_string();
|
|
||||||
sig = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sig == 0) {
|
if (sig == 0) {
|
||||||
|
|
@ -504,18 +486,27 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(sig);
|
if (debug_elaborate) {
|
||||||
|
cerr << get_fileline() << ": " << __func__ << ": "
|
||||||
|
<< "Found l-value path_=" << path_
|
||||||
|
<< " as sig=" << sig->name()
|
||||||
|
<< " base_path=" << base_path
|
||||||
|
<< " member_path=" << member_path
|
||||||
|
<< " unpacked_dimensions()=" << sig->unpacked_dimensions()
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
/* If this is SystemVerilog and the variable is not yet
|
|
||||||
assigned by anything, then convert it to an unresolved
|
// If this is SystemVerilog and the variable is not yet
|
||||||
wire. */
|
// assigned by anything, then convert it to an unresolved
|
||||||
|
// wire.
|
||||||
if (gn_var_can_be_uwire()
|
if (gn_var_can_be_uwire()
|
||||||
&& (sig->type() == NetNet::REG)
|
&& (sig->type() == NetNet::REG)
|
||||||
&& (sig->peek_lref() == 0) ) {
|
&& (sig->peek_lref() == 0) ) {
|
||||||
sig->type(NetNet::UNRESOLVED_WIRE);
|
sig->type(NetNet::UNRESOLVED_WIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow registers as assign l-values. */
|
// Don't allow registers as assign l-values.
|
||||||
if (sig->type() == NetNet::REG) {
|
if (sig->type() == NetNet::REG) {
|
||||||
cerr << get_fileline() << ": error: reg " << sig->name()
|
cerr << get_fileline() << ": error: reg " << sig->name()
|
||||||
<< "; cannot be driven by primitives"
|
<< "; cannot be driven by primitives"
|
||||||
|
|
@ -524,61 +515,135 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default part select is the entire word.
|
// Some parts below need the tail component. This is a convenient
|
||||||
|
// reference to it.
|
||||||
|
const name_component_t&path_tail = path_.back();
|
||||||
|
|
||||||
|
// Default part select is the entire word.
|
||||||
unsigned midx = sig->vector_width()-1, lidx = 0;
|
unsigned midx = sig->vector_width()-1, lidx = 0;
|
||||||
// The default word select is the first.
|
// The default word select is the first.
|
||||||
long widx = 0;
|
long widx = 0;
|
||||||
// Set this to true if we calculate the word index. This is
|
// Set this to true if we calculate the word index. This is
|
||||||
// used to distinguish between unpacked array assignment and
|
// used to distinguish between unpacked array assignment and
|
||||||
// array word assignment.
|
// array word assignment.
|
||||||
bool widx_flag = false;
|
bool widx_flag = false;
|
||||||
|
|
||||||
list<long> unpacked_indices_const;
|
list<long> unpacked_indices_const;
|
||||||
|
|
||||||
|
// Detect the net is a structure and there was a method path
|
||||||
|
// detected. We have already broken the path_ into the path to
|
||||||
|
// the net, and the path of member names. For example, if the
|
||||||
|
// path_ is a.b.x.y, we have determined that a.b is a reference
|
||||||
|
// to the net, and that x.y are the member_path. So in this case
|
||||||
|
// we handle the member_path.
|
||||||
const netstruct_t*struct_type = 0;
|
const netstruct_t*struct_type = 0;
|
||||||
if ((struct_type = sig->struct_type()) && !method_name.nil()) {
|
if ((struct_type = sig->struct_type()) && !member_path.empty()) {
|
||||||
|
|
||||||
// Detect the variable is a structure and there was a
|
|
||||||
// method name detected. We've already found that
|
|
||||||
// the path_ is <>.sig.method_name and signal
|
|
||||||
// (NetNet). We also know that sig is struct_type(), so
|
|
||||||
// look for a method named method_name.
|
|
||||||
if (debug_elaborate)
|
|
||||||
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
||||||
<< "Signal " << sig->name() << " is a structure, "
|
|
||||||
<< "try to match member " << method_name << endl;
|
|
||||||
|
|
||||||
unsigned long member_off = 0;
|
|
||||||
const struct netstruct_t::member_t*member = struct_type->packed_member(method_name, member_off);
|
|
||||||
ivl_assert(*this, member);
|
|
||||||
|
|
||||||
if (debug_elaborate) {
|
if (debug_elaborate) {
|
||||||
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
||||||
<< "Member " << method_name
|
<< "Signal " << sig->name() << " is a structure, "
|
||||||
<< " has type " << *member->net_type << "." << endl;
|
<< "try to match member path " << member_path << endl;
|
||||||
cerr << get_fileline() << ": : "
|
|
||||||
<< "Tail name has " << path_tail.index.size() << " indices." << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite a member select of a packed structure as a
|
unsigned long member_off = 0;
|
||||||
// part select of the base variable.
|
unsigned long member_width = sig->vector_width();
|
||||||
|
|
||||||
|
// Might be an array of structs, like a.b[N].x.y. (A packed
|
||||||
|
// array.) Handle that here by taking a part select that
|
||||||
|
// reflects the array index.
|
||||||
|
if (sig->packed_dimensions() > 1) {
|
||||||
|
list<index_component_t>tmp_index = base_path.back().index;
|
||||||
|
index_component_t member_select;
|
||||||
|
member_select.sel = index_component_t::SEL_BIT;
|
||||||
|
member_select.msb = new PENumber(new verinum(member_off));
|
||||||
|
tmp_index.push_back(member_select);
|
||||||
|
NetExpr*packed_base = collapse_array_indices(des, scope, sig, tmp_index);
|
||||||
|
if (debug_elaborate) {
|
||||||
|
cerr << get_fileline() << ": " << __func__ << ": "
|
||||||
|
<< "packed_base=" << *packed_base
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
long tmp;
|
||||||
|
if (packed_base && eval_as_long(tmp, packed_base)) {
|
||||||
|
member_off = tmp;
|
||||||
|
member_width = struct_type->packed_width();
|
||||||
|
delete packed_base;
|
||||||
|
packed_base = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only support constant dimensions here.
|
||||||
|
ivl_assert(*this, packed_base == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now run through the member names, possibly nested, to take
|
||||||
|
// further part selects reflected by the member name. So for
|
||||||
|
// example, (.x.y) member x has an offset and width within the
|
||||||
|
// containing vector, and member y an offset and width within
|
||||||
|
// that.
|
||||||
|
pform_name_t use_path = member_path;
|
||||||
|
while (! use_path.empty()) {
|
||||||
|
const name_component_t member_comp = use_path.front();
|
||||||
|
const perm_string&member_name = member_comp.name;
|
||||||
|
|
||||||
|
unsigned long tmp_off;
|
||||||
|
const struct netstruct_t::member_t*member = struct_type->packed_member(member_name, tmp_off);
|
||||||
|
ivl_assert(*this, member);
|
||||||
|
member_off += tmp_off;
|
||||||
|
member_width = member->net_type->packed_width();
|
||||||
|
|
||||||
|
if (const netstruct_t*tmp_struct = dynamic_cast<const netstruct_t*> (member->net_type)) {
|
||||||
|
struct_type = tmp_struct;
|
||||||
|
} else {
|
||||||
|
struct_type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
use_path.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for part selects on the final member. For example if
|
||||||
|
// the path is a.b.x.y[3:0], the end of the member_path will
|
||||||
|
// have an index that needs to be handled.
|
||||||
|
// For now, assume there is unly a single part/bit select, and
|
||||||
|
// assume it's constant.
|
||||||
|
if (member_path.back().index.size() > 0) {
|
||||||
|
list<index_component_t>tmp_index = member_path.back().index;
|
||||||
|
if (debug_elaborate) {
|
||||||
|
cerr << get_fileline() << ": " << __func__ << ": "
|
||||||
|
<< "Process trailing bit/part select. "
|
||||||
|
<< "index.size()=" << tmp_index.size()
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
ivl_assert(*this, tmp_index.size() == 1);
|
||||||
|
const index_component_t&tail_sel = tmp_index.back();
|
||||||
|
ivl_assert(*this, tail_sel.sel == index_component_t::SEL_PART || tail_sel.sel == index_component_t::SEL_BIT);
|
||||||
|
long tmp_off;
|
||||||
|
unsigned long tmp_wid;
|
||||||
|
bool rc = calculate_part(this, des, scope, tail_sel, tmp_off, tmp_wid);
|
||||||
|
ivl_assert(*this, rc);
|
||||||
|
member_off += tmp_off;
|
||||||
|
member_width = tmp_wid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug_elaborate) {
|
||||||
|
cerr << get_fileline() << ": " << __func__ << ": "
|
||||||
|
<< "Final, calculated member " << member_path
|
||||||
|
<< " offset=" << member_off
|
||||||
|
<< " width=" << member_width
|
||||||
|
<< endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite a member select of a packed structure as a
|
||||||
|
// part select of the base variable.
|
||||||
lidx = member_off;
|
lidx = member_off;
|
||||||
midx = lidx + member->net_type->packed_width() - 1;
|
midx = lidx + member_width - 1;
|
||||||
|
|
||||||
// The dimensions of the tail of the prefix must match
|
// Elaborate an expression from the packed indices and
|
||||||
// the dimensions of the signal at this point. (The sig
|
// the member offset (into the structure) to get a
|
||||||
// has a packed dimension for the packed struct size.)
|
// canonical expression into the packed signal vector.
|
||||||
// For example, if the path_=a[<m>][<n>].member, then
|
|
||||||
// sig must have 3 packed dimensions: one for the struct
|
|
||||||
// members and two actual packed dimensions.
|
|
||||||
ivl_assert(*this, path_prefix.back().index.size()+1 == sig->packed_dimensions());
|
|
||||||
|
|
||||||
// Elaborate an expression from the packed indices and
|
|
||||||
// the member offset (into the structure) to get a
|
|
||||||
// canonical expression into the packed signal vector.
|
|
||||||
NetExpr*packed_base = 0;
|
NetExpr*packed_base = 0;
|
||||||
if (sig->packed_dimensions() > 1) {
|
if (sig->packed_dimensions() > 1) {
|
||||||
list<index_component_t>tmp_index = path_prefix.back().index;
|
list<index_component_t>tmp_index = base_path.back().index;
|
||||||
index_component_t member_select;
|
index_component_t member_select;
|
||||||
member_select.sel = index_component_t::SEL_BIT;
|
member_select.sel = index_component_t::SEL_BIT;
|
||||||
member_select.msb = new PENumber(new verinum(member_off));
|
member_select.msb = new PENumber(new verinum(member_off));
|
||||||
|
|
@ -592,36 +657,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long tmp;
|
|
||||||
if (packed_base && eval_as_long(tmp, packed_base)) {
|
|
||||||
lidx = tmp;
|
|
||||||
midx = lidx + member->net_type->packed_width() - 1;
|
|
||||||
delete packed_base;
|
|
||||||
packed_base = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently, only support const dimensions here.
|
|
||||||
ivl_assert(*this, packed_base == 0);
|
|
||||||
|
|
||||||
// Now the lidx/midx values get us to the member. Next
|
|
||||||
// up, deal with bit/part selects from the member
|
|
||||||
// itself.
|
|
||||||
//XXXXivl_assert(*this, member->packed_dims.size() <= 1);
|
|
||||||
ivl_assert(*this, path_tail.index.size() <= 1);
|
|
||||||
if (! path_tail.index.empty()) {
|
|
||||||
long tmp_off;
|
|
||||||
unsigned long tmp_wid;
|
|
||||||
const index_component_t&tail_sel = path_tail.index.back();
|
|
||||||
ivl_assert(*this, tail_sel.sel == index_component_t::SEL_PART || tail_sel.sel == index_component_t::SEL_BIT);
|
|
||||||
bool rc = calculate_part(this, des, scope, tail_sel, tmp_off, tmp_wid);
|
|
||||||
ivl_assert(*this, rc);
|
|
||||||
if (debug_elaborate)
|
|
||||||
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
||||||
<< "tmp_off=" << tmp_off << ", tmp_wid=" << tmp_wid << endl;
|
|
||||||
lidx += tmp_off;
|
|
||||||
midx = lidx + tmp_wid - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (gn_system_verilog() && sig->unpacked_dimensions() > 0 && path_tail.index.empty()) {
|
} else if (gn_system_verilog() && sig->unpacked_dimensions() > 0 && path_tail.index.empty()) {
|
||||||
|
|
||||||
// In this case, we are doing a continuous assignment to
|
// In this case, we are doing a continuous assignment to
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue