Merge pull request #454 from antmicro/wsip/typeparam

Handle type parameters
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 9d2abeb..3a2524e 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -54,6 +54,7 @@
 static IdString is_imported;
 static IdString is_simplified_wire;
 static IdString low_high_bound;
+static IdString is_type_parameter;
 }; // namespace attr_id
 
 // TODO(mglb): use attr_id::* directly everywhere and remove those methods.
@@ -84,6 +85,7 @@
     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");
 }
 
 void attr_id_cleanup()
@@ -96,6 +98,25 @@
     attr_id::unpacked_ranges = IdString();
     attr_id::packed_ranges = IdString();
     attr_id::partial = IdString();
+    attr_id::is_type_parameter = IdString();
+}
+
+static AST::AstNode *get_attribute(AST::AstNode *node, const IdString &attribute)
+{
+    log_assert(node);
+    if (!node->attributes.count(attribute))
+        return nullptr;
+
+    return node->attributes[attribute];
+}
+
+// Consumes attr_node.
+static void set_attribute(AST::AstNode *node, const IdString &attribute, AST::AstNode *attr_node)
+{
+    log_assert(node);
+    log_assert(attr_node);
+    delete node->attributes[attribute];
+    node->attributes[attribute] = attr_node;
 }
 
 // Delete the selected attribute if it exists.
@@ -119,7 +140,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()}) {
+                       UhdmAst::is_simplified_wire(), UhdmAst::low_high_bound(), attr_id::is_type_parameter}) {
         delete_attribute(node, attr);
     }
 }
@@ -1879,6 +1900,7 @@
             continue;
         if (pair.second->type == AST::AST_PACKAGE) {
             check_memories(pair.second);
+            clear_current_scope();
             setup_current_scope(shared.top_nodes, pair.second);
             simplify_sv(pair.second, nullptr);
             clear_current_scope();
@@ -1992,6 +2014,7 @@
 
             delete_attribute(current_node, UhdmAst::partial());
         } else {
+            // processing nodes belonging to 'uhdmallModules'
             current_node = make_ast_node(AST::AST_MODULE);
             current_node->str = type;
             shared.top_nodes[current_node->str] = current_node;
@@ -2004,7 +2027,8 @@
             });
             visit_one_to_many({vpiModule, vpiParameter, vpiParamAssign, vpiNet, vpiArrayNet, vpiProcess}, obj_h, [&](AST::AstNode *node) {
                 if (node) {
-                    if (node->type == AST::AST_ASSIGN && node->children.size() < 2) {
+                    if ((node->type == AST::AST_ASSIGN && node->children.size() < 2) ||
+                        (node->type == AST::AST_PARAMETER && get_attribute(node, attr_id::is_type_parameter))) {
                         delete node;
                         return;
                     }
@@ -2016,6 +2040,42 @@
         // Not a top module, create instance
         current_node = make_ast_node(AST::AST_CELL);
         std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> parameters;
+
+        std::vector<AST::AstNode *> parameter_typedefs;
+
+        visit_one_to_many({vpiParameter}, obj_h, [&](AST::AstNode *node) {
+            log_assert(node);
+            AST::AstNode *attr = get_attribute(node, attr_id::is_type_parameter);
+
+            if (!attr) {
+                // Process type parameters only.
+                delete node;
+                return;
+            }
+
+            if (node->children.size() == 0) {
+                log_assert(!attr->str.empty());
+                // Anonymous types have no chidren, and store the parameter name in attr->str.
+                parameters.push_back(std::make_pair(node->str, attr->str));
+                delete node;
+                return;
+            }
+
+            for (auto child : node->children) {
+                if (child->type == AST::AST_TYPEDEF && !child->str.empty()) {
+                    // process_type_parameter should have created a node with the parameter name
+                    //   and a child with the name of the value assigned to the parameter.
+                    parameters.push_back(std::make_pair(node->str, child->str));
+                }
+
+                if (child->type == AST::AST_TYPEDEF || child->type == AST::AST_ENUM) {
+                    // Copy definition of the type provided as parameter.
+                    parameter_typedefs.push_back(child->clone());
+                }
+            }
+            delete node;
+        });
+
         visit_one_to_many({vpiParamAssign}, obj_h, [&](AST::AstNode *node) {
             if (node && node->type == AST::AST_PARAMETER) {
                 log_assert(!node->children.empty());
@@ -2072,6 +2132,7 @@
                 }
             }
         });
+        module_node->children.insert(std::end(module_node->children), std::begin(parameter_typedefs), std::end(parameter_typedefs));
         if (module_node->attributes.count(UhdmAst::partial())) {
             AST::AstNode *attr = module_node->attributes.at(UhdmAst::partial());
             if (attr->type == AST::AST_CONSTANT)
@@ -4597,6 +4658,69 @@
     log_func("%sCurrently not supported object of type '%s'\n", prefix.c_str(), UHDM::VpiTypeName(obj_h).c_str());
 }
 
+void UhdmAst::process_type_parameter()
+{
+    current_node = make_ast_node(AST::AST_PARAMETER);
+
+    // Use an attribute to distinguish "type parameters" from other parameters
+    set_attribute(current_node, attr_id::is_type_parameter, AST::AstNode::mkconst_int(1, false, 1));
+    std::string renamed_enum;
+
+    visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) {
+        if (!node)
+            return;
+
+        if (node->type == AST::AST_WIRE && node->str.empty()) {
+            // anonymous type
+            get_attribute(current_node, attr_id::is_type_parameter)->str = "anonymous_parameter" + std::to_string(shared.next_anonymous_type_id());
+            delete node;
+            return;
+        }
+
+        if (node->type == AST::AST_ENUM) {
+            // Enum typedefs are composed of AST_ENUM and AST_TYPEDEF where the enum shall be renamed,
+            // so that the original name used in code is assigned to the AST_TYPEDEF node,
+            // and a mangled name is assigned to the AST_ENUM node.
+            renamed_enum = node->str + "$enum" + std::to_string(shared.next_enum_id());
+        }
+
+        current_node->children.push_back(node->clone());
+
+        // The child stores information about the type assigned to the parameter
+        //   this information will be used to rename the module
+
+        // find the typedef for `node` in the upper scope and copy it to .children of the AST_PARAMETER node
+        // if unable to find the typedef, continue without error as this could be a globally available type
+
+        if (shared.current_top_node) {
+            for (auto child : shared.current_top_node->children) {
+                // name of the type we're looking for
+                if (child->str == node->str && child->type == AST::AST_TYPEDEF) {
+                    current_node->children.push_back(child->clone());
+                    break;
+                }
+            }
+        }
+        delete node;
+    });
+
+    if (!renamed_enum.empty()) {
+        for (auto child : current_node->children) {
+            if (child->type == AST::AST_TYPEDEF) {
+                log_assert(child->children.size() > 0);
+                set_attribute(child->children[0], ID::enum_type, AST::AstNode::mkconst_str(renamed_enum));
+            }
+            if (child->type == AST::AST_ENUM) {
+                child->str = renamed_enum;
+                // Names of enum variants need to be unique even accross Enums, otherwise Yosys fails.
+                for (auto grandchild : child->children) {
+                    grandchild->str = renamed_enum + "." + grandchild->str;
+                }
+            }
+        }
+    }
+}
+
 AST::AstNode *UhdmAst::process_object(vpiHandle obj_handle)
 {
     obj_h = obj_handle;
@@ -4869,11 +4993,7 @@
         process_unsupported_stmt(object);
         break;
     case vpiTypeParameter:
-        // Instances in an `uhdmTopModules` tree already have all parameter references
-        // substituted with the parameter type/value by Surelog,
-        // so the plugin doesn't need to process the parameter itself.
-        // Other parameter types are handled by the plugin
-        // mainly because they were implemented before Surelog did the substitution.
+        process_type_parameter();
         break;
     case vpiProgram:
     default:
diff --git a/systemverilog-plugin/UhdmAst.h b/systemverilog-plugin/UhdmAst.h
index 81679e3..e1931e9 100644
--- a/systemverilog-plugin/UhdmAst.h
+++ b/systemverilog-plugin/UhdmAst.h
@@ -150,6 +150,7 @@
     void process_while();
     void process_gate();
     void process_primterm();
+    void process_type_parameter();
     void simplify_parameter(::Yosys::AST::AstNode *parameter, ::Yosys::AST::AstNode *module_node = nullptr);
     void process_unsupported_stmt(const UHDM::BaseClass *object, bool is_error = true);
 
diff --git a/systemverilog-plugin/uhdmastshared.h b/systemverilog-plugin/uhdmastshared.h
index 5ea5d22..4bbc447 100644
--- a/systemverilog-plugin/uhdmastshared.h
+++ b/systemverilog-plugin/uhdmastshared.h
@@ -22,6 +22,9 @@
     // Used for generating loop names
     unsigned loop_count = 0;
 
+    // Used for generating unique names for anonymous types used as parameters.
+    unsigned anonymous_type_count = 0;
+
   public:
     ~UhdmAstShared()
     {
@@ -39,6 +42,9 @@
     // Generate the next loop ID (starting with 0)
     unsigned next_loop_id() { return loop_count++; }
 
+    // Generate the next anonymous type ID (starting with 0).
+    unsigned next_anonymous_type_id() { return anonymous_type_count++; }
+
     // Flag that determines whether debug info should be printed
     bool debug_flag = false;