Move support for custom struct member with range to plugin Signed-off-by: Kamil Rakoczy <krakoczy@antmicro.com>
diff --git a/uhdm-plugin/UhdmAst.cc b/uhdm-plugin/UhdmAst.cc index e568dc5..ca90ddb 100644 --- a/uhdm-plugin/UhdmAst.cc +++ b/uhdm-plugin/UhdmAst.cc
@@ -107,7 +107,7 @@ #include "UhdmAstUpstream.cc" -static int get_max_offset(AST::AstNode *node) +static int get_max_offset_struct(AST::AstNode *node) { // get the width from the MS member in the struct // as members are laid out from left to right in the packed wire @@ -169,11 +169,16 @@ } log_assert(ranges[i]->children[0]->type == AST::AST_CONSTANT); log_assert(ranges[i]->children[1]->type == AST::AST_CONSTANT); - wire_node->multirange_dimensions.push_back(min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer)); - wire_node->multirange_dimensions.push_back(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_swapped.push_back(ranges[i]->range_swapped); - size *= wire_node->multirange_dimensions.back(); + if (wire_node->type != AST::AST_STRUCT_ITEM) { + wire_node->multirange_dimensions.push_back(min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer)); + wire_node->multirange_swapped.push_back(ranges[i]->range_swapped); + } + 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; + if (wire_node->type != AST::AST_STRUCT_ITEM || (wire_node->type == AST::AST_STRUCT_ITEM && i == 0)) { + wire_node->multirange_dimensions.push_back(elem_size); + } + size *= elem_size; } return size; } @@ -277,6 +282,12 @@ // needs to have access to all already definied ids while (wire_node->simplify(true, false, false, 1, -1, false, false)) { } + if (wiretype_ast->children[0]->type == AST::AST_STRUCT && wire_node->type == AST::AST_WIRE) { + auto struct_width = get_max_offset_struct(wiretype_ast->children[0]); + wire_node->range_left = struct_width; + wire_node->children[0]->range_left = struct_width; + wire_node->children[0]->children[0]->integer = struct_width; + } if (wiretype_ast && wire_node->attributes.count(ID::wiretype)) { log_assert(wiretype_ast->type == AST::AST_TYPEDEF); wire_node->attributes[ID::wiretype]->id2ast = wiretype_ast->children[0]; @@ -382,6 +393,11 @@ } } + if (wire_node->type == AST::AST_STRUCT_ITEM || wire_node->type == AST::AST_STRUCT) { + wire_node->attributes.erase(UhdmAst::packed_ranges()); + wire_node->attributes.erase(UhdmAst::unpacked_ranges()); + } + // Insert new range wire_node->children.insert(wire_node->children.end(), ranges.begin(), ranges.end()); } @@ -507,7 +523,7 @@ 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_node) + 1; + 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() && @@ -557,6 +573,181 @@ log_assert(AST_INTERNAL::current_ast_mod != nullptr); } +static int range_width_local(AST::AstNode *node, AST::AstNode *rnode) +{ + log_assert(rnode->type == AST::AST_RANGE); + if (!rnode->range_valid) { + log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str()); + } + // note: range swapping has already been checked for + return rnode->range_left - rnode->range_right + 1; +} + +static void save_struct_array_width_local(AST::AstNode *node, int width) +{ + // stash the stride for the array + node->multirange_dimensions.push_back(width); +} + +static int simplify_struct(AST::AstNode *snode, int base_offset, AST::AstNode *parent_node) +{ + // Struct members will be laid out in the structure contiguously from left to right. + // Union members all have zero offset from the start of the union. + // Determine total packed size and assign offsets. Store these in the member node. + bool is_union = (snode->type == AST::AST_UNION); + int offset = 0; + int packed_width = -1; + for (auto s : snode->children) { + if (s->type == AST::AST_RANGE) { + while (s->simplify(true, false, false, 1, -1, false, false)) { + }; + } + } + // embeded struct or union with range? + auto it = std::remove_if(snode->children.begin(), snode->children.end(), [](AST::AstNode *node) { return node->type == AST::AST_RANGE; }); + std::vector<AST::AstNode *> ranges(it, snode->children.end()); + snode->children.erase(it, snode->children.end()); + if (!ranges.empty()) { + if (ranges.size() > 1) { + log_file_error(ranges[1]->filename, ranges[1]->location.first_line, + "Currently support for custom-type with range is limited to single range\n"); + } + for (auto range : ranges) { + snode->multirange_dimensions.push_back(min(range->range_left, range->range_right)); + snode->multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); + snode->multirange_swapped.push_back(range->range_swapped); + } + } + // examine members from last to first + for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { + auto node = *it; + int width; + if (node->type == AST::AST_STRUCT || node->type == AST::AST_UNION) { + // embedded struct or union + width = simplify_struct(node, base_offset + offset, parent_node); + if (!node->multirange_dimensions.empty()) { + int number_of_structs = 1; + number_of_structs = node->multirange_dimensions.back(); + width *= number_of_structs; + } + // set range of struct + node->range_right = base_offset + offset; + node->range_left = base_offset + offset + width - 1; + node->range_valid = true; + } else { + log_assert(node->type == AST::AST_STRUCT_ITEM); + if (node->children.size() > 0 && node->children[0]->type == AST::AST_RANGE) { + // member width e.g. bit [7:0] a + width = range_width_local(node, node->children[0]); + if (node->children.size() == 2) { + if (node->children[1]->type == AST::AST_RANGE) { + // unpacked array e.g. bit [63:0] a [0:3] + auto rnode = node->children[1]; + int array_count = range_width_local(node, rnode); + if (array_count == 1) { + // C-type array size e.g. bit [63:0] a [4] + array_count = rnode->range_left; + } + save_struct_array_width_local(node, width); + width *= array_count; + } else { + // array element must be single bit for a packed array + log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", + node->str.c_str()); + } + } + // range nodes are now redundant + for (AST::AstNode *child : node->children) + delete child; + node->children.clear(); + } else if (node->children.size() == 1 && node->children[0]->type == AST::AST_MULTIRANGE) { + // packed 2D array, e.g. bit [3:0][63:0] a + auto rnode = node->children[0]; + if (rnode->children.size() != 2) { + // packed arrays can only be 2D + log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str()); + } + int array_count = range_width_local(node, rnode->children[0]); + width = range_width_local(node, rnode->children[1]); + save_struct_array_width_local(node, width); + width *= array_count; + // range nodes are now redundant + for (AST::AstNode *child : node->children) + delete child; + node->children.clear(); + } else if (node->range_left < 0) { + // 1 bit signal: bit, logic or reg + width = 1; + } else { + // already resolved and compacted + width = node->range_left - node->range_right + 1; + } + if (is_union) { + node->range_right = base_offset; + node->range_left = base_offset + width - 1; + } else { + node->range_right = base_offset + offset; + node->range_left = base_offset + offset + width - 1; + } + node->range_valid = true; + } + if (is_union) { + // check that all members have the same size + if (packed_width == -1) { + // first member + packed_width = width; + } else { + if (packed_width != width) { + + log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", + node->str.c_str(), width, packed_width); + } + } + } else { + offset += width; + } + } + if (!snode->str.empty() && parent_node && parent_node->type != AST::AST_TYPEDEF && parent_node->type != AST::AST_STRUCT && + AST_INTERNAL::current_scope.count(snode->str) != 0) { + AST_INTERNAL::current_scope[snode->str]->attributes[ID::wiretype] = AST::AstNode::mkconst_str(snode->str); + AST_INTERNAL::current_scope[snode->str]->attributes[ID::wiretype]->id2ast = snode; + } + return (is_union ? packed_width : offset); +} + +static void add_members_to_scope_local(AST::AstNode *snode, std::string name) +{ + // add all the members in a struct or union to local scope + // in case later referenced in assignments + log_assert(snode->type == AST::AST_STRUCT || snode->type == AST::AST_UNION); + for (auto *node : snode->children) { + auto member_name = name + "." + node->str; + AST_INTERNAL::current_scope[member_name] = node; + if (node->type != AST::AST_STRUCT_ITEM) { + // embedded struct or union + add_members_to_scope_local(node, name + "." + node->str); + } + } +} + +static AST::AstNode *make_packed_struct_local(AST::AstNode *template_node, std::string &name) +{ + // create a wire for the packed struct + auto wnode = new AST::AstNode(AST::AST_WIRE); + wnode->str = name; + wnode->is_logic = true; + wnode->range_valid = true; + wnode->is_signed = template_node->is_signed; + int offset = get_max_offset_struct(template_node); + auto range = make_range(offset, 0); + wnode->children.push_back(range); + // make sure this node is the one in scope for this name + AST_INTERNAL::current_scope[name] = wnode; + // add all the struct members to scope under the wire's name + add_members_to_scope_local(template_node, name); + return wnode; +} + static void simplify(AST::AstNode *current_node, AST::AstNode *parent_node) { AST::AstNode *expanded = nullptr; @@ -640,12 +831,24 @@ } break; case AST::AST_STRUCT: - if (!current_node->str.empty() && parent_node && parent_node->type != AST::AST_TYPEDEF && parent_node->type != AST::AST_STRUCT) { - while (current_node->simplify(true, false, false, 1, -1, false, false)) { - } - AST_INTERNAL::current_scope[current_node->str]->attributes[ID::wiretype] = AST::AstNode::mkconst_str(current_node->str); - AST_INTERNAL::current_scope[current_node->str]->attributes[ID::wiretype]->id2ast = current_node; + simplify_struct(current_node, 0, parent_node); + // instance rather than just a type in a typedef or outer struct? + if (!current_node->str.empty() && current_node->str[0] == '\\') { + // instance so add a wire for the packed structure + auto wnode = make_packed_struct_local(current_node, current_node->str); + log_assert(AST_INTERNAL::current_ast_mod); + AST_INTERNAL::current_ast_mod->children.push_back(wnode); + AST_INTERNAL::current_scope[wnode->str]->attributes[ID::wiretype] = AST::AstNode::mkconst_str(current_node->str); + AST_INTERNAL::current_scope[wnode->str]->attributes[ID::wiretype]->id2ast = current_node; } + + current_node->basic_prep = true; + break; + case AST::AST_STRUCT_ITEM: + AST_INTERNAL::current_scope[current_node->str] = current_node; + convert_packed_unpacked_range(current_node); + while (current_node->simplify(true, false, false, 1, -1, false, false)) { + }; break; default: break; @@ -1322,6 +1525,8 @@ void UhdmAst::process_typespec_member() { + std::vector<AST::AstNode *> packed_ranges; + std::vector<AST::AstNode *> unpacked_ranges; current_node = make_ast_node(AST::AST_STRUCT_ITEM); current_node->str = current_node->str.substr(1); vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h); @@ -1330,13 +1535,13 @@ case vpiBitTypespec: case vpiLogicTypespec: { current_node->is_logic = true; - visit_range(typespec_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); + visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); shared.report.mark_handled(typespec_h); break; } case vpiIntTypespec: { current_node->is_signed = true; - current_node->children.push_back(make_range(31, 0)); + packed_ranges.push_back(make_range(31, 0)); shared.report.mark_handled(typespec_h); break; } @@ -1360,20 +1565,36 @@ visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { if (node && node->type == AST::AST_STRUCT) { auto str = current_node->str; + if (node->attributes.count(UhdmAst::packed_ranges())) { + for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { + packed_ranges.push_back(r->clone()); + } + std::reverse(packed_ranges.begin(), packed_ranges.end()); + node->attributes.erase(UhdmAst::packed_ranges()); + } + if (node->attributes.count(UhdmAst::unpacked_ranges())) { + for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) { + unpacked_ranges.push_back(r->clone()); + } + node->attributes.erase(UhdmAst::unpacked_ranges()); + } node->cloneInto(current_node); current_node->str = str; + current_node->children.insert(current_node->children.end(), packed_ranges.begin(), packed_ranges.end()); + packed_ranges.clear(); delete node; } else if (node) { auto str = current_node->str; if (node->attributes.count(UhdmAst::packed_ranges())) { for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { - node->children.push_back(r->clone()); + packed_ranges.push_back(r->clone()); } + std::reverse(packed_ranges.begin(), packed_ranges.end()); node->attributes.erase(UhdmAst::packed_ranges()); } if (node->attributes.count(UhdmAst::unpacked_ranges())) { for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) { - node->children.push_back(r->clone()); + unpacked_ranges.push_back(r->clone()); } node->attributes.erase(UhdmAst::unpacked_ranges()); } @@ -1393,18 +1614,7 @@ } } vpi_release_handle(typespec_h); - if (current_node->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : current_node->attributes[UhdmAst::packed_ranges()]->children) { - current_node->children.push_back(r->clone()); - } - current_node->attributes.erase(UhdmAst::packed_ranges()); - } - if (current_node->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : current_node->attributes[UhdmAst::unpacked_ranges()]->children) { - current_node->children.push_back(r->clone()); - } - current_node->attributes.erase(UhdmAst::unpacked_ranges()); - } + add_multirange_wire(current_node, packed_ranges, unpacked_ranges); } void UhdmAst::process_enum_typespec()