Merge pull request #476 from antmicro/yosys-simplify-inside-plugin

Yosys simplify inside the plugin
diff --git a/environment.yml b/environment.yml
index 35b3ed9..a4cf46c 100644
--- a/environment.yml
+++ b/environment.yml
@@ -20,5 +20,5 @@
   - litex-hub
 dependencies:
   - litex-hub::yosys=0.17_7_g990c9b8e1=20220512_085338_py37
-  - litex-hub::surelog=0.0_5580_g493120558=20230116_180937_py37
+  - litex-hub::surelog
   - litex-hub::iverilog
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc
index 6b8d944..9d2abeb 100644
--- a/systemverilog-plugin/UhdmAst.cc
+++ b/systemverilog-plugin/UhdmAst.cc
@@ -414,7 +414,9 @@
     // needs to have access to all already defined ids
     while (simplify(wire_node, true, false, false, 1, -1, false, false)) {
     }
-    if (wiretype_ast->children[0]->type == AST::AST_STRUCT && wire_node->type == AST::AST_WIRE) {
+    log_assert(!wiretype_ast->children.empty());
+    if ((wiretype_ast->children[0]->type == AST::AST_STRUCT || wiretype_ast->children[0]->type == AST::AST_UNION) &&
+        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;
@@ -677,26 +679,6 @@
     }
     current_struct_elem = *struct_elem_it;
 
-    AST::AstNode *left = nullptr, *right = nullptr;
-    if (current_struct_elem->type == AST::AST_STRUCT_ITEM) {
-        left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true);
-        right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true);
-    } else if (current_struct_elem->type == AST::AST_STRUCT) {
-        // Struct can have multiple range, so to get size of 1 struct,
-        // we get left range for first children, and right range for last children
-        left = AST::AstNode::mkconst_int(current_struct_elem->children.front()->range_left, true);
-        right = AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right, true);
-    } else if (current_struct_elem->type == AST::AST_UNION) {
-        left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true);
-        right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true);
-    } else {
-        // Structs currently can only have AST_STRUCT, AST_STRUCT_ITEM, or AST_UNION.
-        log_file_error(current_struct_elem->filename, current_struct_elem->location.first_line,
-                       "Accessing struct member of type %s is unsupported.\n", type2str(current_struct_elem->type).c_str());
-    }
-
-    auto elem_size =
-      new AST::AstNode(AST::AST_ADD, new AST::AstNode(AST::AST_SUB, left->clone(), right->clone()), AST::AstNode::mkconst_int(1, true));
     AST::AstNode *sub_dot = nullptr;
     AST::AstNode *struct_range = nullptr;
 
@@ -708,10 +690,46 @@
         }
         if (c->type == AST::AST_RANGE) {
             // Currently supporting only 1 range
-            log_assert(!struct_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;
         }
     }
+    AST::AstNode *left = nullptr, *right = nullptr;
+    switch (current_struct_elem->type) {
+    case AST::AST_STRUCT_ITEM:
+        left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true);
+        right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true);
+        break;
+    case AST::AST_STRUCT:
+    case AST::AST_UNION:
+        // TODO(krak): add proper support for accessing struct/union elements
+        // with multirange
+        // 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) {
+            // 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);
+            right =
+              AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right * current_struct_elem->multirange_dimensions.back(), true);
+        } else {
+            left = AST::AstNode::mkconst_int(current_struct_elem->children.front()->range_left, true);
+            right = AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right, true);
+        }
+        break;
+    default:
+        // Structs currently can only have AST_STRUCT, AST_STRUCT_ITEM, or AST_UNION.
+        log_file_error(current_struct_elem->filename, current_struct_elem->location.first_line,
+                       "Accessing struct member of type %s is unsupported.\n", type2str(current_struct_elem->type).c_str());
+    };
+
+    auto elem_size =
+      new AST::AstNode(AST::AST_ADD, new AST::AstNode(AST::AST_SUB, left->clone(), right->clone()), AST::AstNode::mkconst_int(1, true));
+
     if (sub_dot) {
         // First select correct element in first struct
         std::swap(left, sub_dot->children[0]);
@@ -889,10 +907,6 @@
     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);
@@ -908,9 +922,11 @@
             // 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;
+                // Multiply widths of all dimensions.
+                // `multirange_dimensions` stores (repeating) pairs of [offset, width].
+                for (size_t i = 1; i < node->multirange_dimensions.size(); i += 2) {
+                    width *= node->multirange_dimensions[i];
+                }
             }
             // set range of struct
             node->range_right = base_offset + offset;
diff --git a/systemverilog-plugin/uhdmastshared.h b/systemverilog-plugin/uhdmastshared.h
index 36a1068..5ea5d22 100644
--- a/systemverilog-plugin/uhdmastshared.h
+++ b/systemverilog-plugin/uhdmastshared.h
@@ -1,6 +1,8 @@
 #ifndef _UHDM_AST_SHARED_H_
 #define _UHDM_AST_SHARED_H_ 1
 
+#include "frontends/ast/ast.h"
+
 #include "uhdmastreport.h"
 #include <string>
 #include <unordered_map>