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 d2475f4..ffa766e 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -58,6 +58,7 @@
 static IdString is_simplified_wire;
 static IdString low_high_bound;
 static IdString is_type_parameter;
+static IdString is_elaborated_module;
 }; // namespace attr_id
 
 // TODO(mglb): use attr_id::* directly everywhere and remove those methods.
@@ -68,6 +69,9 @@
 /*static*/ const IdString &UhdmAst::is_imported() { return attr_id::is_imported; }
 /*static*/ const IdString &UhdmAst::is_simplified_wire() { return attr_id::is_simplified_wire; }
 /*static*/ const IdString &UhdmAst::low_high_bound() { return attr_id::low_high_bound; }
+/*static*/ const IdString &UhdmAst::is_elaborated_module() { return attr_id::is_elaborated_module; }
+
+#define MAKE_INTERNAL_ID(X) IdString("$systemverilog_plugin$" #X)
 
 void attr_id_init()
 {
@@ -80,14 +84,15 @@
 
     // Register IdStrings. Can't be done statically, as the IdString class uses resources created during Yosys initialization which happens after
     // static initialization of the plugin when everything is statically linked.
-    attr_id::partial = IdString("$systemverilog_plugin$partial");
-    attr_id::packed_ranges = IdString("$systemverilog_plugin$packed_ranges");
-    attr_id::unpacked_ranges = IdString("$systemverilog_plugin$unpacked_ranges");
-    attr_id::force_convert = IdString("$systemverilog_plugin$force_convert");
-    attr_id::is_imported = IdString("$systemverilog_plugin$is_imported");
-    attr_id::is_simplified_wire = IdString("$systemverilog_plugin$is_simplified_wire");
-    attr_id::low_high_bound = IdString("$systemverilog_plugin$low_high_bound");
-    attr_id::is_type_parameter = IdString("$systemverilog_plugin$is_type_parameter");
+    attr_id::partial = MAKE_INTERNAL_ID(partial);
+    attr_id::packed_ranges = MAKE_INTERNAL_ID(packed_ranges);
+    attr_id::unpacked_ranges = MAKE_INTERNAL_ID(unpacked_ranges);
+    attr_id::force_convert = MAKE_INTERNAL_ID(force_convert);
+    attr_id::is_imported = MAKE_INTERNAL_ID(is_imported);
+    attr_id::is_simplified_wire = MAKE_INTERNAL_ID(is_simplified_wire);
+    attr_id::low_high_bound = MAKE_INTERNAL_ID(low_high_bound);
+    attr_id::is_type_parameter = MAKE_INTERNAL_ID(is_type_parameter);
+    attr_id::is_elaborated_module = MAKE_INTERNAL_ID(is_elaborated_module);
 }
 
 void attr_id_cleanup()
@@ -101,6 +106,7 @@
     attr_id::packed_ranges = IdString();
     attr_id::partial = IdString();
     attr_id::is_type_parameter = IdString();
+    attr_id::is_elaborated_module = IdString();
     attr_id::already_initialized = false;
 }
 
@@ -143,7 +149,7 @@
         return;
 
     for (auto &attr : {UhdmAst::partial(), UhdmAst::packed_ranges(), UhdmAst::unpacked_ranges(), UhdmAst::force_convert(), UhdmAst::is_imported(),
-                       UhdmAst::is_simplified_wire(), UhdmAst::low_high_bound(), attr_id::is_type_parameter}) {
+                       UhdmAst::is_simplified_wire(), UhdmAst::low_high_bound(), attr_id::is_type_parameter, attr_id::is_elaborated_module}) {
         delete_attribute(node, attr);
     }
 }
@@ -496,6 +502,8 @@
             unpacked_ranges.push_back(r->clone());
         }
     }
+    delete_attribute(wire_node, attr_id::packed_ranges);
+    delete_attribute(wire_node, attr_id::unpacked_ranges);
     AST::AstNode *wiretype_ast = nullptr;
     log_assert(AST_INTERNAL::current_scope.count(wiretype_node->str));
     wiretype_ast = AST_INTERNAL::current_scope[wiretype_node->str];
@@ -512,28 +520,40 @@
         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)) {
+    if (wiretype_ast) {
+        log_assert(wire_node->attributes.count(ID::wiretype));
         log_assert(wiretype_ast->type == AST::AST_TYPEDEF);
         wire_node->attributes[ID::wiretype]->id2ast = wiretype_ast->children[0];
     }
     if ((wire_node->children[0]->type == AST::AST_RANGE || (wire_node->children.size() > 1 && wire_node->children[1]->type == AST::AST_RANGE)) &&
         wire_node->multirange_dimensions.empty()) {
+        // We need to save order in which ranges appear in wiretype and add them before wire range
+        // We need to copy this ranges, so create new vector for them
+        std::vector<AST::AstNode *> packed_ranges_wiretype;
+        std::vector<AST::AstNode *> unpacked_ranges_wiretype;
         if (wiretype_ast && !wiretype_ast->children.empty() && wiretype_ast->children[0]->attributes.count(UhdmAst::packed_ranges()) &&
             wiretype_ast->children[0]->attributes.count(UhdmAst::unpacked_ranges())) {
             for (auto r : wiretype_ast->children[0]->attributes[UhdmAst::packed_ranges()]->children) {
-                packed_ranges.push_back(r->clone());
+                packed_ranges_wiretype.push_back(r->clone());
             }
             for (auto r : wiretype_ast->children[0]->attributes[UhdmAst::unpacked_ranges()]->children) {
-                unpacked_ranges.push_back(r->clone());
+                unpacked_ranges_wiretype.push_back(r->clone());
             }
         } else {
             if (wire_node->children[0]->type == AST::AST_RANGE)
-                packed_ranges.push_back(wire_node->children[0]->clone());
+                packed_ranges_wiretype.push_back(wire_node->children[0]->clone());
             else if (wire_node->children[1]->type == AST::AST_RANGE)
-                packed_ranges.push_back(wire_node->children[1]->clone());
+                packed_ranges_wiretype.push_back(wire_node->children[1]->clone());
             else
                 log_error("Unhandled case in resolve_wiretype!\n");
         }
+        // add wiretype range before current wire ranges
+        std::reverse(packed_ranges_wiretype.begin(), packed_ranges_wiretype.end());
+        std::reverse(unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end());
+        std::reverse(packed_ranges.begin(), packed_ranges.end());
+        std::reverse(unpacked_ranges.begin(), unpacked_ranges.end());
+        packed_ranges.insert(packed_ranges.begin(), packed_ranges_wiretype.begin(), packed_ranges_wiretype.end());
+        unpacked_ranges.insert(unpacked_ranges.begin(), unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end());
         AST::AstNode *value = nullptr;
         if (wire_node->children[0]->type != AST::AST_RANGE) {
             value = wire_node->children[0]->clone();
@@ -541,26 +561,7 @@
         delete_children(wire_node);
         if (value)
             wire_node->children.push_back(value);
-        wire_node->attributes[UhdmAst::packed_ranges()] = AST::AstNode::mkconst_int(1, false, 1);
-        if (!packed_ranges.empty()) {
-            std::reverse(packed_ranges.begin(), packed_ranges.end());
-            wire_node->attributes[UhdmAst::packed_ranges()]->children.insert(wire_node->attributes[UhdmAst::packed_ranges()]->children.end(),
-                                                                             packed_ranges.begin(), packed_ranges.end());
-            packed_ranges.clear();
-        }
-
-        wire_node->attributes[UhdmAst::unpacked_ranges()] = AST::AstNode::mkconst_int(1, false, 1);
-        if (!unpacked_ranges.empty()) {
-            wire_node->attributes[UhdmAst::unpacked_ranges()]->children.insert(wire_node->attributes[UhdmAst::unpacked_ranges()]->children.end(),
-                                                                               unpacked_ranges.begin(), unpacked_ranges.end());
-            unpacked_ranges.clear();
-        }
-    }
-    for (auto *range : packed_ranges) {
-        delete range;
-    }
-    for (auto *range : unpacked_ranges) {
-        delete range;
+        add_multirange_wire(wire_node, packed_ranges, unpacked_ranges, false /* reverse */);
     }
 }
 
@@ -748,11 +749,6 @@
         }
     }
 
-    if (wire_node->type == AST::AST_STRUCT_ITEM || wire_node->type == AST::AST_STRUCT) {
-        delete_attribute(wire_node, UhdmAst::packed_ranges());
-        delete_attribute(wire_node, UhdmAst::unpacked_ranges());
-    }
-
     // Insert new range
     wire_node->children.insert(wire_node->children.end(), ranges.begin(), ranges.end());
 }
@@ -1374,12 +1370,13 @@
         }
         break;
     case AST::AST_STRUCT_ITEM:
-        AST_INTERNAL::current_scope[current_node->str] = current_node;
-        if (current_node->attributes.count(UhdmAst::packed_ranges()) || current_node->attributes.count(UhdmAst::unpacked_ranges())) {
+        if (!current_node->attributes.count(UhdmAst::is_simplified_wire())) {
+            current_node->attributes[UhdmAst::is_simplified_wire()] = AST::AstNode::mkconst_int(1, true);
+            AST_INTERNAL::current_scope[current_node->str] = current_node;
             convert_packed_unpacked_range(current_node);
+            while (simplify(current_node, true, false, false, 1, -1, false, false)) {
+            };
         }
-        while (simplify(current_node, true, false, false, 1, -1, false, false)) {
-        };
         break;
     case AST::AST_TCALL:
         if (current_node->str == "$display" || current_node->str == "$write")
@@ -1864,17 +1861,15 @@
             current_node->str = str;
             delete node;
         } else if (node) {
-            current_node->str = node->str;
-            if (node->type == AST::AST_ENUM && !node->children.empty()) {
-                for (auto *c : node->children[0]->children) {
-                    if (c->type == AST::AST_RANGE && c->str.empty())
-                        packed_ranges.push_back(c);
-                    else
-                        delete c;
-                }
-                node->children[0]->children.clear();
+            if (!node->str.empty()) {
+                AST::AstNode *const wiretype_node = make_named_node(AST::AST_WIRETYPE);
+                wiretype_node->str = node->str;
+                current_node->children.push_back(wiretype_node);
+                current_node->is_custom_type = true;
+                auto it = shared.param_types.find(current_node->str);
+                if (it == shared.param_types.end())
+                    shared.param_types.insert(std::make_pair(current_node->str, node->clone()));
             }
-            delete node;
         }
     });
     add_multirange_wire(current_node, std::move(packed_ranges), std::move(unpacked_ranges));
@@ -2046,15 +2041,17 @@
             delete type_node;
         } else {
             type_node->str = "$enum" + std::to_string(shared.next_enum_id());
+            std::vector<AST::AstNode *> packed_ranges;
             auto wire_node = new AST::AstNode(AST::AST_WIRE);
             wire_node->is_reg = true;
             wire_node->attributes["\\enum_type"] = AST::AstNode::mkconst_str(type_node->str);
             if (!type_node->children.empty() && type_node->children[0]->children.size() > 1) {
-                wire_node->children.push_back(type_node->children[0]->children[1]->clone());
+                packed_ranges.push_back(type_node->children[0]->children[1]->clone());
             } else {
                 // Add default range
-                wire_node->children.push_back(make_range(31, 0));
+                packed_ranges.push_back(make_range(31, 0));
             }
+            add_multirange_wire(wire_node, std::move(packed_ranges), {});
             typedef_node->children.push_back(wire_node);
             current_node->children.push_back(type_node);
             current_node->children.push_back(typedef_node);
@@ -2205,6 +2202,8 @@
                                            [](auto node) { return node->type == AST::AST_INITIAL || node->type == AST::AST_ALWAYS; });
             auto children_after_process = std::vector<AST::AstNode *>(process_it, current_node->children.end());
             current_node->children.erase(process_it, current_node->children.end());
+            auto old_top = shared.current_top_node;
+            shared.current_top_node = current_node;
             visit_one_to_many({vpiModule, vpiInterface, vpiParameter, vpiParamAssign, vpiPort, vpiNet, vpiArrayNet, vpiTaskFunc, vpiGenScopeArray,
                                vpiContAssign, vpiVariables},
                               obj_h, [&](AST::AstNode *node) {
@@ -2223,6 +2222,7 @@
                     current_node->children.push_back(node);
                 }
             });
+            shared.current_top_node = old_top;
             current_node->children.insert(current_node->children.end(), children_after_process.begin(), children_after_process.end());
 
             delete_attribute(current_node, UhdmAst::partial());
@@ -2328,6 +2328,10 @@
                 module_node = module_node->clone();
                 module_node->str = module_name;
             }
+        } else if (auto attribute = get_attribute(module_node, attr_id::is_elaborated_module); attribute && attribute->integer == 1) {
+            // we already processed module with this parameters, just create cell node
+            make_cell(obj_h, current_node, module_node);
+            return;
         }
         shared.top_nodes[module_node->str] = module_node;
         visit_one_to_many({vpiParamAssign}, obj_h, [&](AST::AstNode *node) {
@@ -2370,6 +2374,7 @@
                           });
         make_cell(obj_h, current_node, module_node);
         shared.current_top_node = old_top;
+        set_attribute(module_node, attr_id::is_elaborated_module, AST::AstNode::mkconst_int(1, true));
     }
 }
 
@@ -2695,9 +2700,8 @@
         // anonymous typespec
         std::string typedef_name = "$systemverilog_plugin$anonymous_enum" + std::to_string(shared.next_anonymous_enum_typedef_id());
         current_node->str = typedef_name;
-        auto current_scope = find_ancestor({AST::AST_PACKAGE, AST::AST_MODULE, AST::AST_BLOCK, AST::AST_GENBLOCK});
-        uhdmast_assert(current_scope != nullptr);
-        move_type_to_new_typedef(current_scope, current_node);
+        uhdmast_assert(shared.current_top_node != nullptr);
+        move_type_to_new_typedef(shared.current_top_node, current_node);
         current_node = make_node(AST::AST_WIRETYPE);
         current_node->str = typedef_name;
         shared.anonymous_enums[enum_object] = std::move(typedef_name);
@@ -4488,6 +4492,8 @@
         }
     }
     visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); });
+    if (packed_ranges.empty())
+        packed_ranges.push_back(make_range(0, 0));
     add_multirange_wire(current_node, packed_ranges, unpacked_ranges);
     current_node->is_signed = vpi_get(vpiSigned, obj_h);
 }
diff --git a/systemverilog-plugin/UhdmAst.h b/systemverilog-plugin/UhdmAst.h
index adee39c..bf2d1b5 100644
--- a/systemverilog-plugin/UhdmAst.h
+++ b/systemverilog-plugin/UhdmAst.h
@@ -201,6 +201,7 @@
     static const ::Yosys::IdString &is_imported();
     static const ::Yosys::IdString &is_simplified_wire();
     static const ::Yosys::IdString &low_high_bound();
+    static const ::Yosys::IdString &is_elaborated_module();
 };
 
 // Utility for building AstNode trees.
diff --git a/systemverilog-plugin/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc
index 0770605..cdc39b4 100644
--- a/systemverilog-plugin/third_party/yosys/simplify.cc
+++ b/systemverilog-plugin/third_party/yosys/simplify.cc
@@ -1485,7 +1485,7 @@
 	}
 
 	// resolve types of wires
-	if (ast_node->type == Yosys::AST::AST_WIRE || ast_node->type == Yosys::AST::AST_MEMORY) {
+	if (ast_node->type == Yosys::AST::AST_WIRE || ast_node->type == Yosys::AST::AST_MEMORY || ast_node->type == Yosys::AST::AST_STRUCT_ITEM) {
 		if (ast_node->is_custom_type) {
 			log_assert(ast_node->children.size() >= 1);
 			log_assert(ast_node->children[0]->type == Yosys::AST::AST_WIRETYPE);