Merge pull request #529 from antmicro/kr/fix_multirange_with_dot
yosys-systemverilog: fix multirange with dot usage
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 0715106..ffa766e 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -958,29 +958,49 @@
}
log_assert(struct_node);
auto expanded = expand_dot(struct_node, dot);
- if (node->children[0]->type == AST::AST_RANGE) {
- int struct_size_int = get_max_offset_struct(struct_node) + 1;
- log_assert(!wire_node->multirange_dimensions.empty());
- int range = wire_node->multirange_dimensions.back() - 1;
- if (!wire_node->attributes[UhdmAst::unpacked_ranges()]->children.empty() &&
- wire_node->attributes[UhdmAst::unpacked_ranges()]->children.back()->range_left == range) {
- expanded->children[1] = new AST::AstNode(
- AST::AST_ADD, expanded->children[1],
- new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32),
- new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(range, true, 32), node->children[0]->children[0]->clone())));
- expanded->children[0] = new AST::AstNode(
- AST::AST_ADD, expanded->children[0],
- new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32),
- new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(range, true, 32), node->children[0]->children[0]->clone())));
- } else {
- expanded->children[1] = new AST::AstNode(
- AST::AST_ADD, expanded->children[1],
- new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32), node->children[0]->children[0]->clone()));
- expanded->children[0] = new AST::AstNode(
- AST::AST_ADD, expanded->children[0],
- new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32), node->children[0]->children[0]->clone()));
+ // Now expand ranges that are at instance part of dotted reference
+ // `expand_dot` returns AST_RANGE with 2 children that selects member pointed by dotted reference
+ // now we need to move this range to select correct struct
+ std::vector<AST::AstNode *> struct_ranges;
+ for (auto c : node->children) {
+ if (c->type == AST::AST_RANGE) {
+ struct_ranges.push_back(c);
}
}
+ log_assert(wire_node->attributes.count(UhdmAst::unpacked_ranges()));
+ log_assert(wire_node->attributes.count(UhdmAst::packed_ranges()));
+ log_assert(struct_ranges.size() <= (wire_node->multirange_dimensions.size() / 2));
+ const auto wire_node_unpacked_ranges_size = wire_node->attributes[UhdmAst::unpacked_ranges()]->children.size();
+ // TODO(krak): wire ranges are sometimes under wiretype node (e.g. in case of typedef)
+ // but wiretype ranges contains also struct range that is already expanded in 'expand_dot'
+ // we need to find a way to calculate size of wire ranges without struct range here to enable this assert
+ // const auto wire_node_packed_ranges_size = wire_node->attributes[UhdmAst::packed_ranges()]->children.size();
+ // const auto wire_node_ranges_size = wire_node_packed_ranges_size + wire_node_unpacked_ranges_size;
+ // log_assert(struct_ranges.size() == (wire_node_ranges_size - 1));
+
+ // Get size of single structure
+ int struct_size_int = get_max_offset_struct(struct_node) + 1;
+ auto wire_dimension_size_it = wire_node->multirange_dimensions.rbegin();
+ unsigned long range_id = 0;
+ for (auto it = struct_ranges.rbegin(); it != struct_ranges.rend(); it++) {
+ // in 'dot' context, we need to select specific struct element,
+ // so assert that there is only 1 child in struct range (range with single child)
+ log_assert((*it)->children.size() == 1);
+ bool is_unpacked_range = range_id < wire_node_unpacked_ranges_size;
+ // if unpacked range, select from back
+ auto elem = is_unpacked_range
+ ? new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(*wire_dimension_size_it - 1, true, 32), (*it)->children[0]->clone())
+ : (*it)->children[0]->clone();
+ // calculate which struct we selected
+ auto move_offset = new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32), elem);
+ // move our expanded dot to currently selected struct
+ expanded->children[0] = new AST::AstNode(AST::AST_ADD, move_offset->clone(), expanded->children[0]);
+ expanded->children[1] = new AST::AstNode(AST::AST_ADD, move_offset, expanded->children[1]);
+ struct_size_int *= *wire_dimension_size_it;
+ // wire_dimension_size stores interleaved offset and size. Move to next dimension's size
+ wire_dimension_size_it += 2;
+ range_id++;
+ }
return expanded;
}