Merge pull request #1060 from larsclausen/ident-test-width
Correctly calculate width of nested path identifiers
This commit is contained in:
commit
7c25e8506c
6
PExpr.h
6
PExpr.h
|
|
@ -530,11 +530,11 @@ class PEIdent : public PExpr {
|
|||
unsigned expr_wid,
|
||||
unsigned flags) const;
|
||||
|
||||
unsigned test_width_method_(const symbol_search_results &sr);
|
||||
|
||||
|
||||
unsigned test_width_parameter_(const NetExpr *par, width_mode_t&mode);
|
||||
|
||||
ivl_type_t resolve_type_(Design *des, const symbol_search_results &sr,
|
||||
unsigned int &index_depth) const;
|
||||
|
||||
private:
|
||||
NetNet* elaborate_lnet_common_(Design*des, NetScope*scope,
|
||||
bool bidirectional_flag) const;
|
||||
|
|
|
|||
354
elab_expr.cc
354
elab_expr.cc
|
|
@ -2313,30 +2313,6 @@ static NetExpr* check_for_enum_methods(const LineInfo*li,
|
|||
return sys_expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the method matches a structure member then return the member otherwise
|
||||
* return 0. Also return the offset of the member.
|
||||
*/
|
||||
static const netstruct_t::member_t*get_struct_member(const LineInfo*li,
|
||||
Design*des, NetScope*,
|
||||
NetNet*net,
|
||||
perm_string method_name,
|
||||
unsigned long&off)
|
||||
{
|
||||
const netstruct_t*type = net->struct_type();
|
||||
ivl_assert(*li, type);
|
||||
|
||||
if (! type->packed()) {
|
||||
cerr << li->get_fileline()
|
||||
<< ": sorry: unpacked structures not supported here. "
|
||||
<< "Method=" << method_name << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return type->packed_member(method_name, off);
|
||||
}
|
||||
|
||||
bool calculate_part(const LineInfo*li, Design*des, NetScope*scope,
|
||||
const index_component_t&index, long&off, unsigned long&wid)
|
||||
{
|
||||
|
|
@ -4212,73 +4188,6 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope,
|
|||
return tmp;
|
||||
}
|
||||
|
||||
unsigned PEIdent::test_width_method_(const symbol_search_results &sr)
|
||||
{
|
||||
if (!gn_system_verilog())
|
||||
return 0;
|
||||
if (path_.size() < 2)
|
||||
return 0;
|
||||
|
||||
pform_name_t use_path = path_.name;
|
||||
perm_string member_name = peek_tail_name(path_);
|
||||
use_path.pop_back();
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::test_width_method_: "
|
||||
<< "Try to find method=" << member_name
|
||||
<< " of signal " << use_path << endl;
|
||||
}
|
||||
|
||||
NetNet *net = sr.net;
|
||||
if (net == 0) {
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": PEIdent::test_width_method_: "
|
||||
<< "Only nets can have methods, so give up here." << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (/*const netdarray_t*dtype =*/ net->darray_type()) {
|
||||
if (member_name == "size") {
|
||||
expr_type_ = IVL_VT_BOOL;
|
||||
expr_width_ = 32;
|
||||
min_width_ = 32;
|
||||
signed_flag_= true;
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
if (const class netqueue_t *queue = net->queue_type()) {
|
||||
if (member_name == "pop_back" || member_name == "pop_front") {
|
||||
expr_type_ = queue->element_base_type();
|
||||
expr_width_ = queue->element_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = queue->get_signed();
|
||||
return expr_width_;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the enumeration attributes.
|
||||
if (const netenum_t*netenum = net->enumeration()) {
|
||||
if (member_name == "num") {
|
||||
expr_type_ = IVL_VT_BOOL;
|
||||
expr_width_ = 32;
|
||||
min_width_ = 32;
|
||||
signed_flag_= true;
|
||||
return 32;
|
||||
}
|
||||
if ((member_name == "first") || (member_name == "last") ||
|
||||
(member_name == "next") || (member_name == "prev")) {
|
||||
expr_type_ = netenum->base_type();
|
||||
expr_width_ = netenum->packed_width();;
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = netenum->get_signed();
|
||||
return expr_width_;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned PEIdent::test_width_parameter_(const NetExpr *par, width_mode_t&mode)
|
||||
{
|
||||
// The width of an enumeration literal is the width of the
|
||||
|
|
@ -4306,14 +4215,126 @@ unsigned PEIdent::test_width_parameter_(const NetExpr *par, width_mode_t&mode)
|
|||
return expr_width_;
|
||||
}
|
||||
|
||||
ivl_type_t PEIdent::resolve_type_(Design *des, const symbol_search_results &sr,
|
||||
unsigned int &index_depth) const
|
||||
{
|
||||
ivl_type_t type;
|
||||
if (sr.net && sr.net->unpacked_dimensions())
|
||||
type = sr.net->array_type();
|
||||
else
|
||||
type = sr.type;
|
||||
|
||||
auto path = sr.path_tail.cbegin();
|
||||
|
||||
ivl_assert(*this, !sr.path_head.empty());
|
||||
|
||||
// Start with processing the indices of the path head
|
||||
auto indices = &sr.path_head.back().index;
|
||||
|
||||
while (type) {
|
||||
auto index = indices->cbegin();
|
||||
index_depth = indices->size();
|
||||
|
||||
// First process all indices
|
||||
while (index_depth) {
|
||||
if (type == &netstring_t::type_string) {
|
||||
index++;
|
||||
index_depth--;
|
||||
type = &netvector_t::atom2u8;
|
||||
} else if (auto array = dynamic_cast<const netsarray_t*>(type)) {
|
||||
auto array_size = array->static_dimensions().size();
|
||||
|
||||
// Not enough indices to consume the array
|
||||
if (index_depth < array_size)
|
||||
return type;
|
||||
|
||||
index_depth -= array_size;
|
||||
while (array_size--)
|
||||
index++;
|
||||
|
||||
type = array->element_type();
|
||||
} else if (auto darray = dynamic_cast<const netdarray_t*>(type)) {
|
||||
index++;
|
||||
index_depth--;
|
||||
type = darray->element_type();
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
if (path == sr.path_tail.cend())
|
||||
return type;
|
||||
|
||||
// Next look up the next path element based on name
|
||||
|
||||
const auto &name = path->name;
|
||||
|
||||
if (auto class_type = dynamic_cast<const netclass_t*>(type)) {
|
||||
// If the type is an object, the next path member may be a
|
||||
// class property.
|
||||
ivl_type_t par_type;
|
||||
if (class_type->get_parameter(des, name, par_type)) {
|
||||
type = par_type;
|
||||
} else {
|
||||
int pidx = class_type->property_idx_from_name(name);
|
||||
if (pidx < 0)
|
||||
return nullptr;
|
||||
|
||||
type = class_type->get_prop_type(pidx);
|
||||
}
|
||||
} else if (auto struct_type = dynamic_cast<const netstruct_t*>(type)) {
|
||||
// If this net is a struct, the next path element may be a
|
||||
// struct member. If it is, then we know the type of this
|
||||
// identifier by knowing the type of the member.
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PEIdent::test_width: "
|
||||
<< "Element is a struct, "
|
||||
<< "checking width of member " << name << endl;
|
||||
}
|
||||
|
||||
unsigned long unused;
|
||||
auto mem = struct_type->packed_member(name, unused);
|
||||
if (!mem)
|
||||
return nullptr;
|
||||
|
||||
type = mem->net_type;
|
||||
} else if (auto queue = dynamic_cast<const netqueue_t*>(type)) {
|
||||
if (name == "size")
|
||||
type = &netvector_t::atom2s32;
|
||||
else if (name == "pop_back" || name == "pop_front")
|
||||
type = queue->element_type();
|
||||
else
|
||||
return nullptr;
|
||||
} else if (dynamic_cast<const netdarray_t*>(type)) {
|
||||
if (name == "size")
|
||||
type = &netvector_t::atom2s32;
|
||||
else
|
||||
return nullptr;
|
||||
} else if (auto netenum = dynamic_cast<const netenum_t*>(type)) {
|
||||
if (name == "num")
|
||||
type = &netvector_t::atom2s32;
|
||||
else if ((name == "first") || (name == "last") ||
|
||||
(name == "next") || (name == "prev"))
|
||||
type = netenum;
|
||||
else
|
||||
return nullptr;
|
||||
} else {
|
||||
// Type has no members, properties or functions. Path is
|
||||
// invalid.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
indices = &path->index;
|
||||
path++;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
|
||||
{
|
||||
symbol_search_results sr;
|
||||
symbol_search(this, des, scope, path_, &sr);
|
||||
|
||||
if (unsigned tmp = test_width_method_(sr)) {
|
||||
return tmp;
|
||||
}
|
||||
bool found_symbol = symbol_search(this, des, scope, path_, &sr);
|
||||
|
||||
// If there is a part/bit select expression, then process it
|
||||
// here. This constrains the results no matter what kind the
|
||||
|
|
@ -4364,60 +4385,19 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
|
|||
ivl_assert(*this, 0);
|
||||
}
|
||||
|
||||
if (const netdarray_t*darray = sr.net ? sr.net->darray_type() : NULL) {
|
||||
switch (use_sel) {
|
||||
case index_component_t::SEL_BIT:
|
||||
case index_component_t::SEL_BIT_LAST:
|
||||
expr_type_ = darray->element_base_type();
|
||||
expr_width_ = darray->element_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = sr.net->get_signed();
|
||||
break;
|
||||
default:
|
||||
expr_type_ = sr.net->data_type();
|
||||
expr_width_ = sr.net->vector_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = sr.net->get_signed();
|
||||
break;
|
||||
}
|
||||
return expr_width_;
|
||||
}
|
||||
unsigned int use_depth = path_.back().index.size();
|
||||
ivl_type_t type = nullptr;
|
||||
|
||||
if (sr.net) {
|
||||
// Similarly, if this net is an object, the path tail may
|
||||
// be a class property.
|
||||
const netclass_t *class_type = dynamic_cast<const netclass_t*>(sr.type);
|
||||
if (class_type && !sr.path_tail.empty()) {
|
||||
perm_string pname = peek_tail_name(sr.path_tail);
|
||||
ivl_type_t par_type;
|
||||
const NetExpr *par = class_type->get_parameter(des, pname, par_type);
|
||||
if (par)
|
||||
return test_width_parameter_(par, mode);
|
||||
if (found_symbol)
|
||||
type = resolve_type_(des, sr, use_depth);
|
||||
|
||||
int pidx = class_type->property_idx_from_name(pname);
|
||||
if (pidx >= 0) {
|
||||
const name_component_t member_comp = sr.path_tail.front();
|
||||
ivl_type_t ptype = class_type->get_prop_type(pidx);
|
||||
if (!member_comp.index.empty()) {
|
||||
const netuarray_t*tmp_ua = dynamic_cast<const netuarray_t*>(ptype);
|
||||
if (tmp_ua) ptype = tmp_ua->element_type();
|
||||
}
|
||||
expr_type_ = ptype->base_type();
|
||||
expr_width_ = ptype->packed_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = ptype->get_signed();
|
||||
return expr_width_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t use_depth = name_tail.index.size();
|
||||
if (use_width != UINT_MAX && (!sr.net || use_depth > sr.net->unpacked_dimensions())) {
|
||||
if (use_width != UINT_MAX && (!type || (use_depth != 0 && type->packed()))) {
|
||||
// We have a bit/part select. Account for any remaining dimensions
|
||||
// beyond the indexed dimension.
|
||||
if (sr.net) {
|
||||
use_depth -= sr.net->unpacked_dimensions();
|
||||
use_width *= sr.net->slice_width(use_depth);
|
||||
if (type) {
|
||||
const auto &slice_dims = type->slice_dimensions();
|
||||
for ( ; use_depth < slice_dims.size(); use_depth++)
|
||||
use_width *= slice_dims[use_depth].width();
|
||||
}
|
||||
|
||||
expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic
|
||||
|
|
@ -4428,78 +4408,52 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
|
|||
return expr_width_;
|
||||
}
|
||||
|
||||
// The width of a signal expression is the width of the signal.
|
||||
if (sr.net != 0) {
|
||||
// If this net is a struct, the path tail may be
|
||||
// a struct member. If it is, then we know the
|
||||
// width of this identifier by knowing the width
|
||||
// of the member. We don't even need to know
|
||||
// anything about positions in containing arrays.
|
||||
if (sr.net->struct_type() != 0 && !sr.path_tail.empty()) {
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PEIdent::test_width: "
|
||||
<< "Net " << sr.path_head << " is a struct, "
|
||||
<< "checking width of member " << sr.path_tail << endl;
|
||||
}
|
||||
|
||||
const netstruct_t::member_t*mem;
|
||||
unsigned long unused;
|
||||
mem = get_struct_member(this, des, scope, sr.net,
|
||||
peek_tail_name(sr.path_tail), unused);
|
||||
if (mem) {
|
||||
expr_type_ = mem->data_type();
|
||||
expr_width_ = mem->net_type->packed_width();
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = mem->get_signed();
|
||||
return expr_width_;
|
||||
}
|
||||
}
|
||||
// The width of a parameter is the width of the parameter value
|
||||
// (as evaluated earlier).
|
||||
if (sr.par_val != 0)
|
||||
return test_width_parameter_(sr.par_val, mode);
|
||||
|
||||
// If the identifier has a type take the information from the type
|
||||
if (type) {
|
||||
// Unindexed indentifier
|
||||
if (use_width == UINT_MAX)
|
||||
use_width = 1;
|
||||
// Account for unpacked dimensions by assuming that the
|
||||
// unpacked dimensions are consumed first, so subtract
|
||||
// the unpacked dimensions from the dimension depth
|
||||
// useable for making the slice.
|
||||
if (use_depth >= sr.net->unpacked_dimensions()) {
|
||||
use_depth -= sr.net->unpacked_dimensions();
|
||||
} else {
|
||||
// In this case, we have an unpacked array or a slice of an
|
||||
// unpacked array. These expressions strictly speaking do
|
||||
// not have a width. But we use the value calculated here
|
||||
// for things $bits(), so return the full number of bits of
|
||||
// the expression.
|
||||
const auto &dims = sr.net->unpacked_dims();
|
||||
for (size_t idx = use_depth; idx < dims.size(); idx++)
|
||||
use_width *= dims[idx].width();
|
||||
|
||||
// In this case, we have an unpacked array or a slice of an
|
||||
// unpacked array. These expressions strictly speaking do
|
||||
// not have a width. But we use the value calculated here
|
||||
// for things $bits(), so return the full number of bits of
|
||||
// the expression.
|
||||
while (auto uarray = dynamic_cast<const netuarray_t *>(type)) {
|
||||
const auto &dims = uarray->static_dimensions();
|
||||
for ( ; use_depth < dims.size(); use_depth++)
|
||||
use_width *= dims[use_depth].width();
|
||||
|
||||
type = uarray->element_type();
|
||||
use_depth = 0;
|
||||
}
|
||||
|
||||
expr_type_ = sr.net->data_type();
|
||||
expr_width_ = sr.net->slice_width(use_depth) * use_width;
|
||||
const auto &slice_dims = type->slice_dimensions();
|
||||
for ( ; use_depth < slice_dims.size(); use_depth++)
|
||||
use_width *= slice_dims[use_depth].width();
|
||||
|
||||
expr_type_ = type->base_type();
|
||||
expr_width_ = use_width;
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = sr.net->get_signed();
|
||||
signed_flag_ = type->get_signed();
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": PEIdent::test_width: "
|
||||
<< sr.net->name() << " is a net, "
|
||||
<< "type=" << expr_type_
|
||||
<< path_
|
||||
<< ", type=" << expr_type_
|
||||
<< ", width=" << expr_width_
|
||||
<< ", signed_=" << (signed_flag_ ? "true" : "false")
|
||||
<< ", use_depth=" << use_depth
|
||||
<< ", packed_dimensions=" << sr.net->packed_dimensions()
|
||||
<< ", unpacked_dimensions=" << sr.net->unpacked_dimensions()
|
||||
<< endl;
|
||||
}
|
||||
return expr_width_;
|
||||
}
|
||||
|
||||
// The width of a parameter is the width of the parameter value
|
||||
// (as evaluated earlier).
|
||||
if (sr.par_val != 0)
|
||||
return test_width_parameter_(sr.par_val, mode);
|
||||
|
||||
if (path_.size() == 1
|
||||
&& scope->genvar_tmp.str()
|
||||
&& strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
// Check that the width on a nested struct member is calculated correctly
|
||||
|
||||
module test;
|
||||
|
||||
bit failed = 1'b0;
|
||||
|
||||
`define check(expr, val) do \
|
||||
if (expr !== val) begin \
|
||||
$display("FAILED(%0d): `%s`, expected %0d, got %0d", `__LINE__, `"expr`", val, expr); \
|
||||
failed = 1'b1; \
|
||||
end \
|
||||
while (0)
|
||||
|
||||
typedef enum bit [15:0] {
|
||||
A, B
|
||||
} E;
|
||||
|
||||
|
||||
struct packed {
|
||||
struct packed {
|
||||
struct packed {
|
||||
reg [7:0][3:0] x;
|
||||
E e;
|
||||
} s2;
|
||||
int z;
|
||||
} s1;
|
||||
int y;
|
||||
} s0;
|
||||
|
||||
initial begin
|
||||
|
||||
`check($bits(s0), 112);
|
||||
`check($bits(s0.s1), 80);
|
||||
`check($bits(s0.s1.s2), 48);
|
||||
`check($bits(s0.s1.s2.x), 32);
|
||||
`check($bits(s0.s1.s2.e), 16);
|
||||
`check($bits(s0.s1.s2.e.next), 16);
|
||||
`check($bits(s0.s1.s2.e.prev), 16);
|
||||
`check($bits(s0.s1.s2.e.first), 16);
|
||||
`check($bits(s0.s1.s2.e.last), 16);
|
||||
`check($bits(s0.s1.s2.e.num), 32);
|
||||
|
||||
`check($bits(s0[0]), 1);
|
||||
`check($bits(s0.s1[0]), 1);
|
||||
`check($bits(s0.s1.s2[0]), 1);
|
||||
`check($bits(s0.s1.s2.x[0]), 4);
|
||||
`check($bits(s0.s1.s2.x[0][0]), 1);
|
||||
`check($bits(s0.s1.s2.e[0]), 1);
|
||||
|
||||
`check($bits(s0[1:0]), 2);
|
||||
`check($bits(s0.s1[1:0]), 2);
|
||||
`check($bits(s0.s1.s2[1:0]), 2);
|
||||
`check($bits(s0.s1.s2.x[1:0]), 8);
|
||||
`check($bits(s0.s1.s2.x[0][1:0]), 2);
|
||||
`check($bits(s0.s1.s2.e[1:0]), 2);
|
||||
|
||||
`check($bits(s0[0+:2]), 2);
|
||||
`check($bits(s0.s1[0+:2]), 2);
|
||||
`check($bits(s0.s1.s2[0+:2]), 2);
|
||||
`check($bits(s0.s1.s2.x[0+:2]), 8);
|
||||
`check($bits(s0.s1.s2.x[0][0+:2]), 2);
|
||||
`check($bits(s0.s1.s2.e[0+:2]), 2);
|
||||
|
||||
if (!failed) begin
|
||||
$display("PASSED");
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -139,6 +139,7 @@ sf_onehot_fail vvp_tests/sf_onehot_fail.json
|
|||
sf_onehot0_fail vvp_tests/sf_onehot0_fail.json
|
||||
struct_enum_partsel vvp_tests/struct_enum_partsel.json
|
||||
struct_field_left_right vvp_tests/struct_field_left_right.json
|
||||
struct_nested1 vvp_tests/struct_nested1.json
|
||||
struct_packed_write_read vvp_tests/struct_packed_write_read.json
|
||||
struct_packed_write_read2 vvp_tests/struct_packed_write_read2.json
|
||||
sv_2state_array_init_prop vvp_tests/sv_2state_array_init_prop.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type" : "normal",
|
||||
"source" : "struct_nested1.v",
|
||||
"iverilog-args" : [ "-g2005-sv" ]
|
||||
}
|
||||
Loading…
Reference in New Issue