allow multiple ranges in dot notation
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc index 8e4b4c5..4083df0 100644 --- a/systemverilog-plugin/UhdmAst.cc +++ b/systemverilog-plugin/UhdmAst.cc
@@ -325,9 +325,12 @@ } log_assert(ranges[i]->children[0]->type == AST::AST_CONSTANT); log_assert(ranges[i]->children[1]->type == AST::AST_CONSTANT); - auto elem_size = max(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer) - - min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer) + 1; - wire_node->multirange_dimensions.push_back(min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer)); + + const auto low = min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer); + const auto high = max(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer); + const auto elem_size = high - low + 1; + + wire_node->multirange_dimensions.push_back(low); wire_node->multirange_dimensions.push_back(elem_size); wire_node->multirange_swapped.push_back(ranges[i]->range_swapped); size *= elem_size; @@ -347,7 +350,8 @@ std::vector<int> single_elem_size; single_elem_size.push_back(elem_size); for (size_t j = 0; (j + 1) < wire_node->multirange_dimensions.size(); j = j + 2) { - elem_size *= wire_node->multirange_dimensions[j + 1] - wire_node->multirange_dimensions[j]; + // The ranges' widths are placed on odd indices of multirange_dimensions. + elem_size *= wire_node->multirange_dimensions[j + 1]; single_elem_size.push_back(elem_size); } std::reverse(single_elem_size.begin(), single_elem_size.end()); @@ -742,18 +746,22 @@ static AST::AstNode *expand_dot(const AST::AstNode *current_struct, const AST::AstNode *search_node) { - AST::AstNode *current_struct_elem = nullptr; + const AST::AstNode *current_struct_elem; auto search_str = search_node->str.find("\\") == 0 ? search_node->str.substr(1) : search_node->str; auto struct_elem_it = std::find_if(current_struct->children.begin(), current_struct->children.end(), [&](AST::AstNode *node) { return node->str == search_str; }); - if (struct_elem_it == current_struct->children.end()) { + if (struct_elem_it == current_struct->children.end() && current_struct->str == search_str) { + // Couldn't find the elem, but the search string matches the current struct. + current_struct_elem = current_struct; + } else if (struct_elem_it == current_struct->children.end()) { current_struct->dumpAst(NULL, "struct >"); log_error("Couldn't find search elem: %s in struct\n", search_str.c_str()); + } else { + current_struct_elem = *struct_elem_it; } - current_struct_elem = *struct_elem_it; AST::AstNode *sub_dot = nullptr; - AST::AstNode *struct_range = nullptr; + std::vector<AST::AstNode *> struct_ranges; for (auto c : search_node->children) { if (c->type == static_cast<int>(AST::Extended::AST_DOT)) { @@ -762,12 +770,7 @@ sub_dot = expand_dot(current_struct_elem, c); } if (c->type == AST::AST_RANGE) { - // Currently supporting only 1 range - if (struct_range) { - log_file_error(struct_range->filename, struct_range->location.first_line, - "Currently support for dot-access is limited to single range\n"); - } - struct_range = c; + struct_ranges.push_back(c); } } AST::AstNode *left = nullptr, *right = nullptr; @@ -783,7 +786,7 @@ // Currently support only special access to 2 dimensional packed element // when selecting single range log_assert(current_struct_elem->multirange_dimensions.size() % 2 == 0); - if (struct_range && (current_struct_elem->multirange_dimensions.size() / 2) == 2) { + if (!struct_ranges.empty() && (current_struct_elem->multirange_dimensions.size() / 2) == 2) { // get element size in number of bits const int single_elem_size = current_struct_elem->children.front()->range_left + 1; left = AST::AstNode::mkconst_int(single_elem_size * current_struct_elem->multirange_dimensions.back(), true); @@ -809,29 +812,52 @@ std::swap(right, sub_dot->children[1]); delete sub_dot; } - if (struct_range) { + + for (size_t i = 0; i < struct_ranges.size(); i++) { + const auto *struct_range = struct_ranges[i]; + auto const range_width_idx = i * 2 + 1; + auto const range_offset_idx = i * 2; + + int range_width = 0; + if (current_struct_elem->multirange_dimensions.empty()) { + range_width = 1; + } else if (current_struct_elem->multirange_dimensions.size() > range_width_idx) { + range_width = current_struct_elem->multirange_dimensions[range_width_idx]; + const auto range_offset = current_struct_elem->multirange_dimensions[range_offset_idx]; + if (range_offset != 0) { + log_file_error(struct_range->filename, struct_range->location.first_line, + "Accessing ranges that do not start from 0 is not supported."); + } + } else { + struct_range->dumpAst(NULL, "range >"); + log_file_error(struct_range->filename, struct_range->location.first_line, "Couldn't find range width."); + } // now we have correct element set, // but we still need to set correct struct log_assert(!struct_range->children.empty()); if (current_struct_elem->type == AST::AST_STRUCT_ITEM) { // if we selecting range of struct item, just add this range // to our current select + if (current_struct_elem->multirange_dimensions.size() > 2 && struct_range->children.size() == 2) { + log_error("Selecting a range of positions from a multirange is not supported in the dot notation.\n"); + } if (struct_range->children.size() == 2) { auto range_size = new AST::AstNode( AST::AST_ADD, new AST::AstNode(AST::AST_SUB, struct_range->children[0]->clone(), struct_range->children[1]->clone()), AST::AstNode::mkconst_int(1, true)); right = new AST::AstNode(AST::AST_ADD, right, struct_range->children[1]->clone()); - left = new AST::AstNode( - AST::AST_ADD, left, - new AST::AstNode(AST::AST_ADD, struct_range->children[1]->clone(), new AST::AstNode(AST::AST_SUB, range_size, elem_size->clone()))); + delete left; + left = new AST::AstNode(AST::AST_ADD, right->clone(), new AST::AstNode(AST::AST_SUB, range_size, AST::AstNode::mkconst_int(1, true))); + } else if (struct_range->children.size() == 1) { - if (!current_struct_elem->multirange_dimensions.empty()) { - right = new AST::AstNode(AST::AST_ADD, right, - new AST::AstNode(AST::AST_MUL, struct_range->children[0]->clone(), - AST::AstNode::mkconst_int(current_struct_elem->multirange_dimensions.back(), true))); + // Selected a single position, as in `foo.bar[i]`. + if (range_width > 1 && current_struct_elem->multirange_dimensions.size() > range_width_idx + 2) { + // if it's not the last dimension. + right = new AST::AstNode( + AST::AST_ADD, right, + new AST::AstNode(AST::AST_MUL, struct_range->children[0]->clone(), AST::AstNode::mkconst_int(range_width, true))); delete left; - left = new AST::AstNode(AST::AST_ADD, right->clone(), - AST::AstNode::mkconst_int(current_struct_elem->multirange_dimensions.back() - 1, true)); + left = new AST::AstNode(AST::AST_ADD, right->clone(), AST::AstNode::mkconst_int(range_width - 1, true)); } else { right = new AST::AstNode(AST::AST_ADD, right, struct_range->children[0]->clone()); delete left;