Rework packed dimensions handling

Make packed structs more obviously parts of a vector.
Handle arrays of structs in certain cases.
This commit is contained in:
Stephen Williams 2012-08-19 17:27:48 -07:00
parent b4cc9d14a5
commit 0bdabab4fb
13 changed files with 271 additions and 73 deletions

View File

@ -1553,7 +1553,9 @@ bool calculate_part(const LineInfo*li, Design*des, NetScope*scope,
*/
static NetExpr* check_for_struct_members(const LineInfo*li,
Design*des, NetScope*scope,
NetNet*net, const name_component_t&comp)
NetNet*net,
const list<index_component_t>&base_index,
const name_component_t&comp)
{
unsigned long off;
const netstruct_t::member_t*mem = get_struct_member(li, des, 0, net,
@ -1563,12 +1565,14 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
unsigned use_width = mem->width();
if (debug_elaborate) {
cerr << li->get_fileline() << ": check_for_struct_members: "
cerr << li->get_fileline() << ": debug: check_for_struct_members: "
<< "Found struct member " << mem->name
<< " At offset " << off
<< ", member width = " << use_width << endl;
}
// The struct member may be a packed array. Process index
// expression that address the member element.
if ( ! comp.index.empty() ) {
// Evaluate all but the last index expression, into prefix_indices.
list<long>prefix_indices;
@ -1597,7 +1601,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
prefix_to_slice(mem->packed_dims, prefix_indices, poff, loff, lwid);
if (debug_elaborate) {
cerr << li->get_fileline() << ": check_for_struct_members: "
cerr << li->get_fileline() << ": debug: check_for_struct_members: "
<< "Evaluate prefix gives slice loff=" << loff
<< ", lwid=" << lwid << ", part select pwid=" << pwid << endl;
}
@ -1609,8 +1613,44 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
use_width = lwid;
}
// If the base symbol has dimensions, then this is a packed
// array of structures. Convert an array of indices to a
// single part select. For example, "net" is a packed array
// of struct, and "mem" is the struct member. In Verilog it
// looks something like "net[idx].mem". We've already
// converted "mem" to an offset into the packed struct, so now
// we just canonicalize "[idx]" and add the ".mem" offset to
// get a collapsed index.
NetExpr*packed_base = 0;
if(net->packed_dimensions() > 1) {
list<index_component_t>tmp_index = base_index;
index_component_t member_select;
member_select.sel = index_component_t::SEL_BIT;
member_select.msb = new PENumber(new verinum(off));
tmp_index.push_back(member_select);
packed_base = collapse_array_exprs(des, scope, li, net, tmp_index);
ivl_assert(*li, packed_base);
if (debug_elaborate) {
cerr << li->get_fileline() << ": debug: check_for_struct_members: "
<< "Got collapsed array expr: " << *packed_base << endl;
}
}
long tmp;
if (packed_base && eval_as_long(tmp, packed_base)) {
off = tmp;
delete packed_base;
packed_base = 0;
}
NetESignal*sig = new NetESignal(net);
NetEConst*base = make_const_val(off);
NetExpr *base = packed_base? packed_base : make_const_val(off);
if (debug_elaborate) {
cerr << li->get_fileline() << ": debug: check_for_struct_members: "
<< "Convert packed indices/member select into part select: " << *base << endl;
}
NetESelect*sel = new NetESelect(sig, base, use_width);
return sel;
}
@ -2352,7 +2392,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
ivl_assert(*this, use_enum != 0);
expr_type_ = use_enum->base_type();
expr_width_ = use_enum->base_width();
expr_width_ = use_enum->packed_width();
min_width_ = expr_width_;
signed_flag_ = par_enum->has_sign();
@ -2400,9 +2440,17 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
// Check to see if we have a net and if so is it a structure?
if (net != 0) {
// If this net is a struct, the method name may be
// a struct member.
// a struct member. If it is, then we know the
// width of this identifier my knowing the width
// of the member. We don't even need to know
// anything about positions in containing arrays.
if (net->struct_type() != 0) {
ivl_assert(*this, use_path.back().index.empty());
if (debug_elaborate) {
cerr << get_fileline() << ": debug: PEIdent::test_width: "
<< "Net " << use_path << " is a struct, "
<< "checking width of member " << method_name << endl;
}
const netstruct_t::member_t*mem;
unsigned long unused;
@ -2603,10 +2651,19 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
// If this net is a struct, the method name may be
// a struct member.
if (net->struct_type() != 0) {
ivl_assert(*this, use_path.back().index.empty());
if (debug_elaborate) {
cerr << get_fileline() << ": debug: "
<< "PEIdent::elaborate_expr: "
<< "Ident " << use_path
<< " is a struct."
<< " Expecting " << net->packed_dims().size()
<< "-1 dimensions, "
<< "got " << use_path.back().index.size() << "." << endl;
}
return check_for_struct_members(this, des, scope,
net, member_comp);
net, use_path.back().index,
member_comp);
}
}

View File

@ -713,20 +713,9 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
++ name_idx;
const name_component_t&name_base = *name_idx;
// The dimenions in the expression must match the packed
// dimensions that are declared for the variable. For example,
// if foo is a packed array of struct, then this expression
// must be "b[n][m]" with the right number of dimensions to
// match the declaration of "b".
ivl_assert(*this, name_base.index.size() == reg->packed_dimensions());
// Generate an expression that takes the input array of
// expressions and generates a canonical offset into the
// packed array.
NetExpr*packed_base = 0;
if (reg->packed_dimensions() > 0)
packed_base = collapse_array_indices(des, scope, reg, name_base.index);
// 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.
unsigned long off;
const netstruct_t::member_t* member = struct_type->packed_member(member_name, off);
@ -781,9 +770,31 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
use_width = lwid;
}
// The dimenions in the expression must match the packed
// dimensions that are declared for the variable. For example,
// if foo is a packed array of struct, then this expression
// must be "b[n][m]" with the right number of dimensions to
// match the declaration of "b".
// Note that one of the packed dimensions is the packed struct
// itself.
ivl_assert(*this, name_base.index.size()+1 == reg->packed_dimensions());
// Generate an expression that takes the input array of
// expressions and generates a canonical offset into the
// packed array.
NetExpr*packed_base = 0;
if (reg->packed_dimensions() > 1) {
list<index_component_t>tmp_index = name_base.index;
index_component_t member_select;
member_select.sel = index_component_t::SEL_BIT;
member_select.msb = new PENumber(new verinum(off));
tmp_index.push_back(member_select);
packed_base = collapse_array_indices(des, scope, reg, tmp_index);
}
long tmp;
if (eval_as_long(tmp, packed_base)) {
off += tmp * struct_type->packed_width();
if (packed_base && eval_as_long(tmp, packed_base)) {
off = tmp;
delete packed_base;
packed_base = 0;
}
@ -793,6 +804,10 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
return true;
}
// Oops, packed_base is not fully evaluated, so I don't know
// yet what to do with it.
cerr << get_fileline() << ": internal error: "
<< "I don't know how to handle this index expression? " << *packed_base << endl;
ivl_assert(*this, 0);
return false;
}

View File

@ -181,12 +181,12 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
verinum max_value (0);
if (enum_type->signed_flag) {
min_value = v_not((pow(verinum(2),
verinum(use_enum->base_width()-1)))) +
verinum(use_enum->packed_width()-1)))) +
one_value;
max_value = pow(verinum(2), verinum(use_enum->base_width()-1)) -
max_value = pow(verinum(2), verinum(use_enum->packed_width()-1)) -
one_value;
} else {
max_value = pow(verinum(2), verinum(use_enum->base_width())) -
max_value = pow(verinum(2), verinum(use_enum->packed_width())) -
one_value;
}
min_value.has_sign(true);
@ -249,15 +249,15 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
// The values are explicitly sized to the width of the
// base type of the enumeration.
verinum tmp_val (0);
if (cur_value.len() < use_enum->base_width()) {
if (cur_value.len() < use_enum->packed_width()) {
// Pad the current value if it is narrower than the final
// width of the enum.
tmp_val = pad_to_width (cur_value, use_enum->base_width());
tmp_val = pad_to_width (cur_value, use_enum->packed_width());
tmp_val.has_len(true);
} else {
// Truncate an oversized value. We report out of bound
// values above. This may create duplicates.
tmp_val = verinum(cur_value, use_enum->base_width());
tmp_val = verinum(cur_value, use_enum->packed_width());
}
tmp_val.has_sign(enum_type->signed_flag);

View File

@ -1213,7 +1213,9 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Create signal " << wtype
<< " enumeration "
<< name_ << " in scope " << scope_path(scope) << endl;
<< name_ << " in scope " << scope_path(scope)
<< " with packed_dimensions=" << packed_dimensions
<< " and packed_width=" << use_enum->packed_width() << endl;
}
sig = new NetNet(scope, name_, wtype, packed_dimensions, unpacked_dimensions, use_enum);

View File

@ -334,7 +334,7 @@ NetESFunc::NetESFunc(const char*n, netenum_t*enum_type, unsigned np)
: name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np)
{
name_ = lex_strings.add(n);
expr_width(enum_type->base_width());
expr_width(enum_type->packed_width());
}
NetESFunc::~NetESFunc()

View File

@ -32,6 +32,14 @@ netenum_t::~netenum_t()
{
}
long netenum_t::packed_width() const
{
if (msb_ >= lsb_)
return msb_ - lsb_ + 1;
else
return lsb_ - msb_ + 1;
}
bool netenum_t::insert_name(size_t name_idx, perm_string name, const verinum&val)
{
std::pair<std::map<perm_string,verinum>::iterator, bool> res;

View File

@ -37,7 +37,7 @@ class netenum_t : public LineInfo, public nettype_base_t {
~netenum_t();
ivl_variable_type_t base_type() const;
unsigned base_width() const;
long packed_width() const;
bool has_sign() const;
// The size() is the number of enumeration literals.
@ -73,14 +73,6 @@ class netenum_t : public LineInfo, public nettype_base_t {
inline ivl_variable_type_t netenum_t::base_type() const
{ return base_type_; }
inline unsigned netenum_t::base_width() const
{
if (msb_ >= lsb_)
return msb_ - lsb_ + 1;
else
return lsb_ - msb_ + 1;
}
inline size_t netenum_t::size() const { return names_.size(); }
inline bool netenum_t::has_sign() const { return signed_flag_; }

View File

@ -476,6 +476,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins)
// Synthesize a single range to describe this canonical vector.
packed_dims_.push_back(netrange_t(npins-1, 0));
calculate_slice_widths_from_packed_dims_();
Link::DIR dir = Link::PASSIVE;
@ -521,6 +522,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
eref_count_(0), lref_count_(0)
{
packed_dims_ = packed;
calculate_slice_widths_from_packed_dims_();
assert(s);
Link::DIR dir = Link::PASSIVE;
@ -564,6 +566,32 @@ static unsigned calculate_count(const list<netrange_t>&unpacked)
return sum;
}
template <class T> static unsigned calculate_count(T*type)
{
long wid = type->packed_width();
if (wid >= 0)
return wid;
else
return 1;
}
void NetNet::calculate_slice_widths_from_packed_dims_(void)
{
if (packed_dims_.empty()) {
slice_wids_.clear();
return;
}
slice_wids_.resize(packed_dims_.size());
slice_wids_[0] = netrange_width(packed_dims_);
list<netrange_t>::const_iterator cur = packed_dims_.begin();
for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1) {
slice_wids_[idx] = slice_wids_[idx-1] / cur->width();
}
}
NetNet::NetNet(NetScope*s, perm_string n, Type t,
const list<netrange_t>&packed,
const list<netrange_t>&unpacked,
@ -576,6 +604,9 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
eref_count_(0), lref_count_(0)
{
packed_dims_ = packed;
if (net_type)
packed_dims_.push_back(netrange_t(calculate_count(net_type)-1, 0));
calculate_slice_widths_from_packed_dims_();
size_t idx = 0;
for (list<netrange_t>::const_iterator cur = unpacked.begin()
; cur != unpacked.end() ; ++cur, idx += 1) {
@ -611,15 +642,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
s->add_signal(this);
}
template <class T> static unsigned calculate_count(T*type)
{
long wid = type->packed_width();
if (wid >= 0)
return wid;
else
return 1;
}
/*
* When we create a netnet for a packed struct, create a single
* vector with the msb_/lsb_ chosen to name enough bits for the entire
@ -634,6 +656,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty)
eref_count_(0), lref_count_(0)
{
packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0));
calculate_slice_widths_from_packed_dims_();
Link::DIR dir = Link::PASSIVE;
switch (t) {
@ -820,15 +843,23 @@ netdarray_t* NetNet::darray_type(void) const
return dynamic_cast<netdarray_t*> (net_type_);
}
unsigned long NetNet::vector_width() const
/*
* "depth" is the number of index expressions that the user is using
* to index this identifer. So consider if Net was declared like so:
*
* reg [5:0][3:0] foo;
*
* In this case, slice_width(2) == 1 (slice_width(N) where N is the
* number of dimensions will always be 1.) and represents
* $bits(foo[a][b]). Then, slice_width(1)==4 ($bits(foo[a]) and slice_width(0)==24.
*/
unsigned long NetNet::slice_width(size_t depth) const
{
unsigned long wid = netrange_width(packed_dims_);
if (net_type_) {
long tmp = net_type_->packed_width();
if (tmp > 0) wid *= tmp;
}
return wid;
if (depth > slice_wids_.size())
return 0;
if (depth == slice_wids_.size())
return 1;
return slice_wids_[depth];
}
ivl_discipline_t NetNet::get_discipline() const

View File

@ -664,7 +664,7 @@ class NetNet : public NetObj, public PortType {
/* The vector_width returns the bit width of the packed array,
vector or scaler that is this NetNet object. */
unsigned long vector_width() const;
inline unsigned long vector_width() const { return slice_width(0); }
/* Given a prefix of indices, figure out how wide the
resulting slice would be. This is a generalization of the
@ -744,6 +744,14 @@ class NetNet : public NetObj, public PortType {
std::list<netrange_t> packed_dims_;
std::vector<netrange_t> unpacked_dims_;
// These are the widths of the various slice depths. There is
// one entry in this vector for each packed dimension. The
// value at N is the slice width if N indices are provided.
//
// For example: slice_wids_[0] is vector_width().
void calculate_slice_widths_from_packed_dims_(void);
std::vector<unsigned long> slice_wids_;
unsigned eref_count_;
unsigned lref_count_;

View File

@ -180,6 +180,21 @@ static NetExpr* make_add_expr(NetExpr*expr, long val)
return res;
}
static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2)
{
bool use_signed = expr1->has_sign() && expr2->has_sign();
unsigned use_wid = expr1->expr_width();
if (expr2->expr_width() > use_wid)
use_wid = expr2->expr_width();
expr1 = pad_to_width(expr1, use_wid, *loc);
expr2 = pad_to_width(expr2, use_wid, *loc);
NetEBAdd*tmp = new NetEBAdd('+', expr1, expr2, use_wid, use_signed);
return tmp;
}
/*
* Subtract an existing expression from a signed constant.
*/
@ -254,6 +269,8 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
if (is_up) offset -= wid - 1;
/* Calculate the space needed for the offset. */
unsigned min_wid = num_bits(offset);
if (num_bits(soff) > min_wid)
min_wid = num_bits(soff);
/* 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();
@ -287,6 +304,8 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
if ((soff-offset) == 0) return base;
/* Calculate the space needed for the offset. */
unsigned min_wid = num_bits(-offset);
if (num_bits(soff) > min_wid)
min_wid = num_bits(soff);
/* 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();
@ -834,8 +853,9 @@ bool eval_as_double(double&value, NetExpr*expr)
* returns the path component name. It will evaluate the index
* expression if it is present.
*/
hname_t eval_path_component(Design*des, NetScope*scope,
const name_component_t&comp)
static hname_t eval_path_component(Design*des, NetScope*scope,
const name_component_t&comp,
bool&error_flag)
{
// No index expression, so the path component is an undecorated
// name, for example "foo".
@ -873,6 +893,7 @@ hname_t eval_path_component(Design*des, NetScope*scope,
return res;
}
#if 1
// Darn, the expression doesn't evaluate to a constant. That's
// an error to be reported. And make up a fake index value to
// return to the caller.
@ -880,6 +901,8 @@ hname_t eval_path_component(Design*des, NetScope*scope,
<< "Scope index expression is not constant: "
<< *index.msb << endl;
des->errors += 1;
#endif
error_flag = true;
delete tmp;
return hname_t (comp.name, 0);
@ -888,15 +911,20 @@ hname_t eval_path_component(Design*des, NetScope*scope,
std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
const pform_name_t&path)
{
bool path_error_flag = false;
list<hname_t> res;
typedef pform_name_t::const_iterator pform_path_it;
for (pform_path_it cur = path.begin() ; cur != path.end(); ++ cur ) {
const name_component_t&comp = *cur;
res.push_back( eval_path_component(des,scope,comp) );
res.push_back( eval_path_component(des,scope,comp,path_error_flag) );
}
#if 0
if (path_error_flag) {
cerr << "XXXXX: Errors evaluating path " << path << endl;
}
#endif
return res;
}
@ -1128,6 +1156,64 @@ bool evaluate_index_prefix(Design*des, NetScope*scope,
return true;
}
/*
* Evaluate the indices. The chain of indices are applied to the
* packed indices of a NetNet to generate a canonical expression to
* replace the exprs.
*/
NetExpr*collapse_array_exprs(Design*des, NetScope*scope,
const LineInfo*loc, NetNet*net,
const list<index_component_t>&indices)
{
// First elaborate all the expressions as far as possible.
list<NetExpr*> exprs;
list<long> exprs_const;
bool flag = indices_to_expressions(des, scope, loc, indices,
net->packed_dimensions(),
false, exprs, exprs_const);
ivl_assert(*loc, exprs.size() == net->packed_dimensions());
// Special Case: there is only 1 packed dimension, so the
// single expression should already be naturally canonical.
if (net->slice_width(1) == 1) {
return *exprs.begin();
}
const std::list<netrange_t>&pdims = net->packed_dims();
std::list<netrange_t>::const_iterator pcur = pdims.begin();
list<NetExpr*>::iterator ecur = exprs.begin();
NetExpr* base = 0;
for (size_t idx = 0 ; idx < net->packed_dimensions() ; idx += 1, ++pcur, ++ecur) {
unsigned cur_slice_width = net->slice_width(idx+1);
// This normalizes the expression of this index based on
// the msb/lsb values.
NetExpr*tmp = normalize_variable_base(*ecur, pcur->get_msb(),
pcur->get_lsb(),
cur_slice_width, true);
// If this slice has width, then scale it.
if (net->slice_width(idx+1) != 1) {
unsigned min_wid = tmp->expr_width();
if (num_bits(cur_slice_width) >= min_wid) {
min_wid = num_bits(cur_slice_width)+1;
tmp = pad_to_width(tmp, min_wid, *loc);
}
tmp = make_mult_expr(tmp, cur_slice_width);
}
// Now add it to the position we've accumulated so far.
if (base) {
base = make_add_expr(loc, base, tmp);
} else {
base = tmp;
}
}
return base;
}
/*
* Given a list of indices, treat them as packed indices and convert
* them to an expression that normalizes the list to a single index
@ -1147,5 +1233,7 @@ NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net,
NetExpr*base = elab_and_eval(des, scope, back_index.msb, -1, true);
NetExpr*res = normalize_variable_bit_base(prefix_indices, base, net);
eval_expr(res, -1);
return res;
}

View File

@ -256,13 +256,6 @@ void eval_expr(NetExpr*&expr, int context_width =-1);
bool eval_as_long(long&value, NetExpr*expr);
bool eval_as_double(double&value, NetExpr*expr);
/*
* Evaluate the component of a scope path to get an hname_t value. Do
* the evaluation in the context of the given scope.
*/
extern hname_t eval_path_component(Design*des, NetScope*scope,
const name_component_t&comp);
/*
* Evaluate an entire scope path in the context of the given scope.
*/
@ -314,4 +307,8 @@ extern bool evaluate_index_prefix(Design*des, NetScope*scope,
extern NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net,
const std::list<index_component_t>&indices);
extern NetExpr*collapse_array_exprs(Design*des, NetScope*scope,
const LineInfo*loc, NetNet*net,
const list<index_component_t>&indices);
#endif

View File

@ -2872,7 +2872,7 @@ static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type,
assert(enum_type->range.get() != 0);
assert(enum_type->range->size() == 1);
cur->set_range(*enum_type->range, SR_NET);
//XXXXcur->set_range(*enum_type->range, SR_NET);
cur->set_packed_type(enum_type);
pform_bind_attributes(cur->attributes, attr, true);
}

View File

@ -259,7 +259,7 @@ extern "C" ivl_variable_type_t ivl_enum_type(ivl_enumtype_t net)
extern "C" unsigned ivl_enum_width(ivl_enumtype_t net)
{
assert(net);
return net->base_width();
return net->packed_width();
}
extern "C" int ivl_enum_signed(ivl_enumtype_t net)