[reggen] Add support for non-homogenous multiregs
diff --git a/util/reggen/data.py b/util/reggen/data.py
index 4ad732d..cb30b89 100644
--- a/util/reggen/data.py
+++ b/util/reggen/data.py
@@ -4,6 +4,14 @@
 
 from .field_enums import HwAccess, SwAccess, SwRdAccess, SwWrAccess
 
+# helper funtion that strips trailing number from name
+# TODO: this is a workaround, should solve this in validate.py
+def _get_basename(name):
+    outname = ""
+    for (k, c) in enumerate(name[::-1]):
+        if not str.isdigit(c):
+            return name[0:len(name)-k]
+    return ""
 
 class Field():
     """Field in a register.
@@ -54,6 +62,8 @@
     def get_fields_flat(self):
         return [self]
 
+    def get_basename(self):
+        return _get_basename(self.name)
 
 class Reg():
     name = ""
@@ -66,6 +76,7 @@
     regwen = ""
     fields = []
     width = 0  # indicate register size
+    ishomog = 0
 
     def __init__(self, name=""):
         self.name = name
@@ -78,6 +89,7 @@
         self.regwen = ""
         self.fields = []
         self.width = 0
+        self.ishomog = 0
 
     def is_multi_reg(self):
         """Returns true if this is a multireg"""
@@ -137,10 +149,14 @@
         """Recursively get dimensions of nested registers (outputs a list)"""
         # return length of flattened field array if this is a regular register,
         # or if this is the last multiregister level in a nested multiregister
-        if not isinstance(self, MultiReg) or \
-            isinstance(self, MultiReg) and   \
-            not isinstance(self.fields[0], MultiReg):
+        if not isinstance(self, MultiReg):
             dims = [len(self.get_fields_flat())]
+        if  isinstance(self, MultiReg) and   \
+            not isinstance(self.fields[0], MultiReg):
+            if self.ishomog:
+                dims = [len(self.get_fields_flat())]
+            else:
+                dims = [len(self.fields)]
         else:
         # nested multiregister case
             dims = [len(self.fields)] + self.fields[0].get_nested_dims()
@@ -155,6 +171,9 @@
             params += self.fields[0].get_nested_params()
         return params
 
+    def get_basename(self):
+        return _get_basename(self.name)
+
 
 class MultiReg(Reg):
     param = ""
@@ -220,3 +239,11 @@
 
     def get_n_regs_flat(self):
         return len(self.get_regs_flat())
+
+    def contains_multiregs(self):
+        """Returns true if there are multiregs in this block
+        """
+        for r in self.regs:
+            if isinstance(r, MultiReg):
+                return True
+        return False
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index be60626..3aaa949 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -70,6 +70,8 @@
         # since this is a multireg, the list of fields can
         # contain regs or multiregs
         reg.fields = regs
+        # a homogenous multireg contains only one single field that is replicated
+        reg.ishomog = len(obj['multireg']['fields']) == 1
         # TODO: need to rework this once the underlying JSON has been changed
         reg.name = escape_name(obj['multireg']['name'])
         # TODO: need to reference proper param here such that it can be used
diff --git a/util/reggen/reg_pkg.tpl.sv b/util/reggen/reg_pkg.tpl.sv
index ed0bd28..98877f2 100644
--- a/util/reggen/reg_pkg.tpl.sv
+++ b/util/reggen/reg_pkg.tpl.sv
@@ -17,11 +17,14 @@
   localparam ${param["type"]} ${param["name"]} = ${param["default"]};
 % endfor
 
+% if block.contains_multiregs():
 /////////////////////////////////////////////////////////////////////
 // Typedefs for multiregs
 /////////////////////////////////////////////////////////////////////
+
 % for r in block.regs:
-% if r.is_multi_reg() and r.get_n_bits(["q"]):
+  ## in this case we have a homogeneous multireg, with only one replicated field
+  % if r.is_multi_reg() and r.get_n_bits(["q"]) and r.ishomog:
 typedef struct packed {
   logic [${r.get_field_flat(0).get_n_bits()-1}:0] q;
   % if r.get_field_flat(0).hwqe:
@@ -31,9 +34,49 @@
   logic re;
   % endif
 } ${block.name + "_reg2hw_" + r.name + "_mreg_t"};
-% endif
+  ## in this case we have an inhomogeneous multireg, with several different fields per register
+  % elif r.is_multi_reg() and r.get_n_bits(["q"]) and not r.ishomog:
+typedef struct packed {
+    % for f in r.get_reg_flat(0).fields:
+  struct packed {
+    logic [${f.get_n_bits()-1}:0] q;
+      % if f.hwqe:
+    logic qe;
+      % endif
+      % if f.hwre:
+    logic re;
+      % endif
+  } ${f.get_basename()};
+    %endfor
+} ${block.name + "_reg2hw_" + r.name + "_mreg_t"};
+  %endif
 % endfor
 
+% for r in block.regs:
+ ## in this case we have a homogeneous multireg, with only one replicated field
+  % if r.is_multi_reg() and r.get_n_bits(["d"]) and r.ishomog:
+typedef struct packed {
+  logic [${r.get_field_flat(0).get_n_bits(["d"])-1}:0] d;
+    % if not r.get_reg_flat(0).hwext:
+  logic de;
+    % endif
+} ${block.name + "_hw2reg_" + r.name + "_mreg_t"};
+  ## in this case we have an inhomogeneous multireg, with several different fields per register
+  % elif r.is_multi_reg() and r.get_n_bits(["d"]) and not r.ishomog:
+typedef struct packed {
+    % for f in r.get_reg_flat(0).fields:
+  struct packed {
+    logic [${f.get_n_bits(["d"])-1}:0] d;
+      % if not r.hwext:
+    logic de;
+      % endif
+  } ${f.get_basename()};
+    %endfor
+} ${block.name + "_hw2reg_" + r.name + "_mreg_t"};
+  % endif
+% endfor
+
+% endif
 /////////////////////////////////////////////////////////////////////
 // Register to internal design logic
 /////////////////////////////////////////////////////////////////////
@@ -89,21 +132,6 @@
 } ${block.name}_reg2hw_t;
 
 /////////////////////////////////////////////////////////////////////
-// Typedefs for multiregs
-/////////////////////////////////////////////////////////////////////
-
-% for r in block.regs:
-% if r.is_multi_reg() and r.get_n_bits(["d"]):
-typedef struct packed {
-  logic [${r.get_field_flat(0).get_n_bits()-1}:0] d;
-  % if not r.get_reg_flat(0).hwext:
-  logic de;
-  % endif
-} ${block.name + "_hw2reg_" + r.name + "_mreg_t"};
-% endif
-% endfor
-
-/////////////////////////////////////////////////////////////////////
 // Internal design logic to register
 /////////////////////////////////////////////////////////////////////
 <%
diff --git a/util/reggen/reg_top.tpl.sv b/util/reggen/reg_top.tpl.sv
index 0aa529a..7531b51 100644
--- a/util/reggen/reg_top.tpl.sv
+++ b/util/reggen/reg_top.tpl.sv
@@ -200,7 +200,11 @@
           % for f in sr.fields:
 <%
             finst_name = sr.name + "_" + f.name
-            fsig_name = r.name + "[%d]" % k
+            if r.ishomog:
+              fsig_name = r.name + "[%d]" % k
+              k = k + 1
+            else:
+              fsig_name = r.name + "[%d]" % k + "." + f.get_basename()
             msb = f.msb
             lsb = f.lsb
             swaccess = f.swaccess
@@ -212,11 +216,14 @@
             hwext = sr.hwext
             resval = f.resval
             regwen = sr.regwen
-            k = k + 1
 %>
   // F[${f.name}]: ${f.msb}:${f.lsb}
 ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
           % endfor
+<%
+          if not r.ishomog:
+            k += 1
+%>
         % endif
       ## for: mreg_flat
       % endfor