Merge pull request #479 from antmicro/kr/module_input_type

systemverilog-plugin: fix size of struct used as I/O wire
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 5d317c7..8424469 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);
     }
 }
@@ -1885,6 +1906,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();
@@ -1998,6 +2020,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;
@@ -2010,7 +2033,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;
                     }
@@ -2022,6 +2046,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());
@@ -2078,6 +2138,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)
@@ -2302,7 +2363,9 @@
     bool invalidvalue = false;
     UHDM::ExprEval eval;
     UHDM::expr *resolved_operation = eval.reduceExpr(expr, invalidvalue, inst, pexpr);
-    log_assert(!invalidvalue);
+    if (invalidvalue) {
+        log_file_warning(std::string(expr->VpiFile()), expr->VpiLineNo(), "Could not reduce expression.\n");
+    }
     return resolved_operation;
 }
 
@@ -4603,6 +4666,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;
@@ -4875,11 +5001,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/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc
index 47d04f7..0770605 100644
--- a/systemverilog-plugin/third_party/yosys/simplify.cc
+++ b/systemverilog-plugin/third_party/yosys/simplify.cc
@@ -36,6 +36,8 @@
 #include <stdlib.h>
 #include <math.h>
 
+#include "simplify.h"
+
 YOSYS_NAMESPACE_BEGIN
 namespace VERILOG_FRONTEND
 {
@@ -49,8 +51,6 @@
 using namespace ::Yosys;
 using namespace ::Yosys::AST_INTERNAL;
 
-bool simplify(Yosys::AST::AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
-
 void annotateTypedEnums(Yosys::AST::AstNode *ast_node, Yosys::AST::AstNode *template_node)
 {
 	//check if enum
@@ -175,6 +175,22 @@
 	bool is_union = (snode->type == Yosys::AST::AST_UNION);
 	int offset = 0;
 	int packed_width = -1;
+
+	// embedded struct or union with range?
+	auto it = std::remove_if(snode->children.begin(), snode->children.end(), [](Yosys::AST::AstNode *node) { return node->type == Yosys::AST::AST_RANGE; });
+	std::vector<Yosys::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;
@@ -3778,7 +3794,7 @@
 	{
 		bool string_op;
 		std::vector<RTLIL::State> tmp_bits;
-		RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int);
+		RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, ys_size_type);
 		RTLIL::Const dummy_arg;
 
 		switch (ast_node->type)
diff --git a/systemverilog-plugin/third_party/yosys/simplify.h b/systemverilog-plugin/third_party/yosys/simplify.h
index 5620ab9..69cd4f4 100644
--- a/systemverilog-plugin/third_party/yosys/simplify.h
+++ b/systemverilog-plugin/third_party/yosys/simplify.h
@@ -2,5 +2,6 @@
 
 namespace systemverilog_plugin
 {
+    using ys_size_type = int;  // Makes it easy to change if changed upstream.
     bool simplify(Yosys::AST::AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
 }
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;