[reggen] Refactor reggen, first step towards nested multiregs
diff --git a/util/reggen/data.py b/util/reggen/data.py
index e3edfdb..aaad2e1 100644
--- a/util/reggen/data.py
+++ b/util/reggen/data.py
@@ -9,7 +9,7 @@
     """Field in a register.
 
     Field class contains necessary info to generate RTL code.
-    It has two additional (tool generated) feidls, swrdaccess and swwraccess,
+    It has two additional (tool generated) fields, swrdaccess and swwraccess,
     which represent read and write type. This makes RTL generation code simpler.
     """
     name = ""  # required
@@ -22,6 +22,7 @@
     hwaccess = HwAccess.HRO
     hwqe = False
     hwre = False
+    hwext = False
 
     def __init__(self):
         self.name = ""  # required
@@ -34,6 +35,24 @@
         self.hwaccess = HwAccess.HRO
         self.hwqe = False
         self.hwre = False
+        self.hwext = False
+
+    def get_n_bits(self, bittype=["q"]):
+        n_bits = 0
+        if "q" in bittype and self.hwaccess in [HwAccess.HRW, HwAccess.HRO]:
+            n_bits += self.msb - self.lsb + 1
+        if "d" in bittype and self.hwaccess in [HwAccess.HRW, HwAccess.HWO]:
+            n_bits += self.msb - self.lsb + 1
+        if "qe" in bittype and self.hwaccess in [HwAccess.HRW, HwAccess.HRO]:
+            n_bits += self.hwqe
+        if "re" in bittype and self.hwaccess in [HwAccess.HRW, HwAccess.HRO]:
+            n_bits += self.hwre
+        if "de" in bittype and self.hwaccess in [HwAccess.HRW, HwAccess.HWO]:
+            n_bits += not self.hwext
+        return n_bits
+
+    def get_fields_flat(self):
+        return [self]
 
 
 class Reg():
@@ -48,8 +67,8 @@
     fields = []
     width = 0  # indicate register size
 
-    def __init__(self):
-        self.name = ""
+    def __init__(self, name=""):
+        self.name = name
         self.offset = 0
         self.hwqe = False
         self.hwre = False
@@ -60,15 +79,85 @@
         self.fields = []
         self.width = 0
 
+    def is_multi_reg(self):
+        """Returns true if this is a multireg"""
+        return False
+
+    def get_n_bits(self, bittype=["q"]):
+        """Returns number of bits in this register (including all multiregs and
+        fields). By default this function counts read data bits (bittype "q"),
+        but other bits such as "d", qe", "re", "de" can be counted as well by
+        specifying them in the bittype list argument.
+        """
+        n_bits = 0
+        for f in self.fields:
+            n_bits += f.get_n_bits(bittype)
+        return n_bits
+
+    def get_fields_flat(self):
+        """Returns a flat list of all the fields in this register"""
+        fields = []
+        for f in self.fields:
+            fields += f.get_fields_flat()
+        return fields
+
+    def get_field_flat(self, linear_idx):
+        """Returns a specific field at a linear index position in
+        the flattened field list"""
+        fields_flat = self.get_fields_flat()
+        return fields_flat[linear_idx]
+
+    def get_n_fields_flat(self):
+        """Returns the number of fields contained in the flat field list"""
+        return len(self.get_fields_flat())
+
+    def get_regs_flat(self):
+        """Returns the a flatt list containing all
+        registers and subregisters"""
+        if isinstance(self.fields[0], Field):
+            return [self]
+        else:
+            regs = []
+            for r in self.fields:
+                regs += r.get_regs_flat()
+            return regs
+
+    def get_reg_flat(self, linear_index):
+        """Returns a specific register at a linear index position in
+        the flattened regiser list"""
+        regs_flat = self.get_regs_flat()
+        return regs_flat[linear_index]
+
+    def get_n_regs_flat(self):
+        """Returns the number of registers contained in
+        the flat register list"""
+        return len(self.get_regs_flat())
+
+    def get_nested_dims(self):
+        """Recursively get dimensions of nested registers (outputs a list)"""
+        dims = []
+        if self.is_multi_reg():
+            dims = self.fields[0].get_nested_dims()
+        return [len(self.fields)] + dims
+
+    def get_nested_params(self):
+        """Recursively get parameters of nested registers (outputs a list)"""
+        params = []
+        if self.is_multi_reg():
+            params = self.fields[0].get_nested_params()
+        return [self.param] + params
+
 
 class MultiReg(Reg):
     param = ""
-    regs = []
 
-    def __init__(self):
-        super.__init__(self)
+    def __init__(self, name):
+        Reg.__init__(self, name)
         self.param = ""
-        self.regs = []
+
+    def is_multi_reg(self):
+        """Returns true if this is a multireg"""
+        return True
 
 
 class Window():
@@ -101,3 +190,25 @@
         self.wins = []
         self.blocks = []
         self.params = []
+
+    def get_regs_flat(self):
+        """Returns flattened register list
+        """
+        regs = []
+        for r in self.regs:
+            regs += r.get_regs_flat()
+        return regs
+
+    def get_n_bits(self, bittype=["q"]):
+        """Returns number of bits in this block (including all multiregs and
+        fields). By default this function counts read data bits (bittype "q"),
+        but other bits such as "d", qe", "re", "de" can be counted as well by
+        specifying them in the bittype list argument.
+        """
+        n_bits = 0
+        for r in self.regs:
+            n_bits += r.get_n_bits(bittype)
+        return n_bits
+
+    def get_n_regs_flat(self):
+        return len(self.get_regs_flat())
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index d4197db..be60626 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -11,8 +11,8 @@
 from mako.template import Template
 from pkg_resources import resource_filename
 
-from .field_enums import HwAccess, SwAccess, SwRdAccess, SwWrAccess
 from .data import *
+from .field_enums import HwAccess, SwAccess, SwRdAccess, SwWrAccess
 
 
 def escape_name(name):
@@ -46,6 +46,7 @@
     f.hwaccess = obj["genhwaccess"]
     f.hwqe = obj["genhwqe"]
     f.hwre = obj["genhwre"]
+    f.hwext = reg.hwext
 
     # resval handling. `genresval` has zero value if `resval` field is defined
     # as unknown 'x'
@@ -55,32 +56,48 @@
 
 
 def parse_reg(obj):
-    """Convert OrderedDict register into Register class
+    """Convert OrderedDict register into Register or MultiRegister object.
+    Supports nested MultiRegisters.
     """
+    if 'multireg' in obj:
+        regs = []
+        for genr in obj['multireg']['genregs']:
+            regs += [parse_reg(genr)]
+        # get register properties of the first register in the multireg and
+        # copy them to the parent
+        # since all regs in a multireg have the same props
+        reg = MultiReg(regs[0].get_reg_flat(0))
+        # since this is a multireg, the list of fields can
+        # contain regs or multiregs
+        reg.fields = regs
+        # 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
+        # in the package template for the array declaration
+        # reg.param = ...
+    else:
+        reg = Reg(escape_name(obj['name']))
+        reg.offset = obj["genoffset"]
+        reg.fields = []
 
-    reg = Reg()
-    reg.name = escape_name(obj['name'])
-    reg.offset = obj["genoffset"]
-    reg.fields = []
+        reg.hwext = (obj['hwext'] == "true")
+        reg.hwqe = (obj["hwqe"] == "true")
+        reg.hwre = (obj["hwre"] == "true")
+        reg.resval = obj["genresval"]
+        reg.dvrights = obj["gendvrights"]
+        reg.regwen = obj["regwen"].lower()
 
-    reg.hwext = (obj['hwext'] == "true")
-    reg.hwqe = (obj["hwqe"] == "true")
-    reg.hwre = (obj["hwre"] == "true")
-    reg.resval = obj["genresval"]
-    reg.dvrights = obj["gendvrights"]
-    reg.regwen = obj["regwen"].lower()
+        # Parsing Fields
+        for f in obj["fields"]:
+            field = parse_field(f, reg, len(obj["fields"]))
+            if field != None:
+                reg.fields.append(field)
+                reg.width = max(reg.width, field.msb + 1)
 
-    # Parsing Fields
-    for f in obj["fields"]:
-        field = parse_field(f, reg, len(obj["fields"]))
-        if field != None:
-            reg.fields.append(field)
-            reg.width = max(reg.width, field.msb + 1)
-
-    # TODO(eunchan): Field bitfield overlapping check
-    log.info("R[0x%04x]: %s ", reg.offset, reg.name)
-    for f in reg.fields:
-        log.info("  F[%2d:%2d]: %s", f.msb, f.lsb, f.name)
+        # TODO(eunchan): Field bitfield overlapping check
+        log.info("R[0x%04x]: %s ", reg.offset, reg.name)
+        for f in reg.fields:
+            log.info("  F[%2d:%2d]: %s", f.msb, f.lsb, f.name)
 
     return reg
 
@@ -139,16 +156,8 @@
             if win != None:
                 block.wins.append(win)
             continue
-        elif 'multireg' in r:
-            for genr in r['multireg']['genregs']:
-                reg = parse_reg(genr)
-                if reg != None:
-                    block.regs.append(reg)
-            continue
-        reg = parse_reg(r)
-        if reg != None:
-            block.regs.append(reg)
-        # mdhayter -- moved logging into parse_regs
+
+        block.regs += [parse_reg(r)]
 
     # Last offset and calculate space
     #  Later on, it could use block.regs[-1].genoffset
diff --git a/util/reggen/reg_pkg.tpl.sv b/util/reggen/reg_pkg.tpl.sv
index 376adde..b77fbff 100644
--- a/util/reggen/reg_pkg.tpl.sv
+++ b/util/reggen/reg_pkg.tpl.sv
@@ -5,7 +5,7 @@
 // Register Package auto-generated by `reggen` containing data structure
 
 <%
-  num_regs = len(block.regs)
+  num_regs = block.get_n_regs_flat()
   max_regs_char = len("{}".format(num_regs-1))
 %>\
 package ${block.name}_reg_pkg;
@@ -17,39 +17,45 @@
   localparam ${param["type"]} ${param["name"]} = ${param["default"]};
 % endfor
 
-// Register to internal design logic
+/////////////////////////////////////////////////////////////////////
+// Typedefs for multiregs
+/////////////////////////////////////////////////////////////////////
+% for r in block.regs:
+% if r.is_multi_reg() and r.get_n_bits(["q"]):
 typedef struct packed {
+  logic [${r.get_field_flat(0).get_n_bits()-1}:0] q;
+  % if r.get_field_flat(0).hwqe:
+  logic qe;
+  % endif
+  % if r.get_field_flat(0).hwre:
+  logic re;
+  % endif
+} ${block.name + "_reg2hw_" + r.name + "_mreg_t"};
+% endif
+% endfor
+
+/////////////////////////////////////////////////////////////////////
+// Register to internal design logic
+/////////////////////////////////////////////////////////////////////
 <%
-# directly mirrors below (avoided optimizations to ensure a match)
-# have to do as a python block to avoid inserting blank lines
-# compute number of bits because packed structs are declared msb first
-packbit = 0
-for r in block.regs:
-  if len(r.fields) == 1 and r.fields[0].hwaccess in [HwAccess.HRW, HwAccess.HRO]:
-    packbit += 1 + r.fields[0].msb - r.fields[0].lsb
-    if r.fields[0].hwqe:
-      packbit += 1
-    if r.fields[0].hwre:
-      packbit += 1
-  elif len(r.fields) >= 2 and len([f for f in r.fields if f.hwaccess in [HwAccess.HRW, HwAccess.HRO]]):
-    for f in r.fields:
-      if f.hwaccess in [HwAccess.HRW, HwAccess.HRO]:
-        if f.msb != f.lsb:
-          packbit += 1 + f.msb - f.lsb
-        else:
-          packbit += 1
-        if r.hwqe:
-          packbit += 1
-        if r.fields[0].hwre:
-          packbit += 1
-nbits = packbit - 1
+nbits = block.get_n_bits(["q","qe","re"]) - 1
 packbit = 0
 %>
+typedef struct packed {
 % for r in block.regs:
-  % if len(r.fields) == 1 and r.fields[0].hwaccess in [HwAccess.HRW, HwAccess.HRO]:
+  ######################## multiregister ###########################
+  % if r.is_multi_reg() and r.get_n_bits(["q"]):
+  <%
+  dims = r.get_nested_dims()
+  array_dims = ""
+  for d in dims[1:]:
+    array_dims += "[%d:0]" % (d-1)
+  %>${block.name + "_reg2hw_" + r.name + "_mreg_t"} ${array_dims} ${r.name}; // [${nbits - packbit}:${nbits - (packbit + r.get_n_bits(["q", "qe", "re"]) - 1)}]<% packbit += r.get_n_bits(["q", "qe", "re"]) %>
+  ######################## register with single field ###########################
+  % elif len(r.fields) == 1 and r.get_n_bits(["q"]):
     ## Only one field, should use register name as it is
   struct packed {
-    logic [${r.fields[0].msb - r.fields[0].lsb}:0] q; // [${nbits - packbit}:${nbits - (packbit + r.fields[0].msb - r.fields[0].lsb)}]<% packbit += 1 + r.fields[0].msb - r.fields[0].lsb %>
+    logic [${r.fields[0].get_n_bits()-1}:0] q; // [${nbits - packbit}:${nbits - (packbit + r.fields[0].get_n_bits() - 1)}]<% packbit += r.fields[0].get_n_bits() %>
     % if r.fields[0].hwqe:
     logic qe; // [${nbits - packbit}]<% packbit += 1 %>
     % endif
@@ -57,21 +63,22 @@
     logic re; // [${nbits - packbit}]<% packbit += 1 %>
     % endif
   } ${r.name};
-  % elif len(r.fields) >= 2 and len([f for f in r.fields if f.hwaccess in [HwAccess.HRW, HwAccess.HRO]]):
+  ######################## register with multiple fields ###########################
+  % elif len(r.fields) >= 2 and r.get_n_bits(["q"]):
   struct packed {
     % for f in r.fields:
       % if f.hwaccess in [HwAccess.HRW, HwAccess.HRO]:
     struct packed {
       ## reg2hw signal based on HW type and virtual?
-      % if f.msb != f.lsb:
-      logic [${f.msb - f.lsb}:0] q; // [${nbits - packbit}:${nbits - (packbit + f.msb - f.lsb)}]<% packbit += 1 + f.msb - f.lsb %>
+      % if f.get_n_bits() > 1:
+      logic [${f.get_n_bits()-1}:0] q; // [${nbits - packbit}:${nbits - (packbit + f.get_n_bits() - 1)}]<% packbit += f.get_n_bits() %>
       % else:
       logic q; // [${nbits - packbit}]<% packbit += 1 %>
       % endif
       % if f.hwqe:
       logic qe; // [${nbits - packbit}]<% packbit += 1 %>
       % endif
-      % if r.fields[0].hwre:
+      % if f.hwre:
       logic re; // [${nbits - packbit}]<% packbit += 1 %>
       % endif
     } ${f.name};
@@ -82,47 +89,60 @@
 % endfor
 } ${block.name}_reg2hw_t;
 
-// Internal design logic to register
+/////////////////////////////////////////////////////////////////////
+// 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
+/////////////////////////////////////////////////////////////////////
 <%
-packbit = 0
-for r in block.regs:
-  if len(r.fields) == 1 and r.fields[0].hwaccess in [HwAccess.HRW, HwAccess.HWO]:
-    packbit += 1 + r.fields[0].msb - r.fields[0].lsb
-    if r.hwext == 0:
-      packbit += 1
-  elif len(r.fields) >= 2 and len([f for f in r.fields if f.hwaccess in [HwAccess.HRW, HwAccess.HWO]]):
-    for f in r.fields:
-      if f.hwaccess in [HwAccess.HRW, HwAccess.HWO]:
-        if f.msb != f.lsb:
-          packbit += 1 + f.msb - f.lsb
-        else:
-          packbit += 1
-        if r.hwext == 0:
-          packbit += 1
-nbits = packbit - 1
+nbits = block.get_n_bits(["d","de"]) - 1
 packbit = 0
 %>
+typedef struct packed {
 % for r in block.regs:
-  % if len(r.fields) == 1 and r.fields[0].hwaccess in [HwAccess.HRW, HwAccess.HWO]:
+  ######################## multiregister ###########################
+  % if r.is_multi_reg() and r.get_n_bits(["d"]):
+  <%
+  dims = r.get_nested_dims()
+  array_dims = ""
+  for d in dims[1:]:
+    array_dims += "[%d:0]" % (d-1)
+  %>${block.name + "_hw2reg_" + r.name + "_mreg_t"} ${array_dims} ${r.name}; // [${nbits - packbit}:${nbits - (packbit + r.get_n_bits(["d", "de"]) - 1)}]<% packbit += r.get_n_bits(["d", "de"]) %>
+  ######################## register with single field ###########################
+  % elif len(r.fields) == 1 and r.get_n_bits(["d"]):
     ## Only one field, should use register name as it is
   struct packed {
-    logic [${r.fields[0].msb - r.fields[0].lsb}:0] d; // [${nbits - packbit}:${nbits - (packbit + r.fields[0].msb - r.fields[0].lsb)}]<% packbit += 1 + r.fields[0].msb - r.fields[0].lsb %>
+    logic [${r.fields[0].get_n_bits("d")-1}:0] d; // [${nbits - packbit}:${nbits - (packbit + r.fields[0].get_n_bits("d") - 1)}]<% packbit += r.fields[0].get_n_bits("d") %>
     % if r.hwext == 0:
     logic de; // [${nbits - packbit}]<% packbit += 1 %>
     % endif
   } ${r.name};
-  % elif len(r.fields) >= 2 and len([f for f in r.fields if f.hwaccess in [HwAccess.HRW, HwAccess.HWO]]):
+  ######################## register with multiple fields ###########################
+  % elif len(r.fields) >= 2 and r.get_n_bits(["d"]):
   struct packed {
     % for f in r.fields:
       % if f.hwaccess in [HwAccess.HRW, HwAccess.HWO]:
     struct packed {
-      % if f.msb != f.lsb:
-      logic [${f.msb - f.lsb}:0] d; // [${nbits - packbit}:${nbits - (packbit + f.msb - f.lsb)}]<% packbit += 1 + f.msb - f.lsb %>
+      ## reg2hw signal based on HW type and virtual?
+      % if f.get_n_bits("d") > 1:
+      logic [${f.get_n_bits("d")-1}:0] d; // [${nbits - packbit}:${nbits - (packbit + f.get_n_bits("d") - 1)}]<% packbit += f.get_n_bits("d") %>
       % else:
-      logic d;  // [${nbits - packbit}]<% packbit += 1 %>
+      logic d; // [${nbits - packbit}]<% packbit += 1 %>
       % endif
-      % if r.hwext == 0:
+      % if not r.hwext:
       logic de; // [${nbits - packbit}]<% packbit += 1 %>
       % endif
     } ${f.name};
@@ -134,7 +154,7 @@
 } ${block.name}_hw2reg_t;
 
   // Register Address
-% for r in block.regs:
+% for r in block.get_regs_flat():
   parameter ${block.name.upper()}_${r.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % r.offset};
 % endfor
 
@@ -148,14 +168,14 @@
 
   // Register Index
   typedef enum int {
-% for r in block.regs:
+% for r in block.get_regs_flat():
     ${block.name.upper()}_${r.name.upper()}${"" if loop.last else ","}
 % endfor
   } ${block.name}_id_e;
 
   // Register width information to check illegal writes
-  localparam logic [3:0] ${block.name.upper()}_PERMIT [${len(block.regs)}] = '{
-% for i,r in enumerate(block.regs):
+  localparam logic [3:0] ${block.name.upper()}_PERMIT [${block.get_n_regs_flat()}] = '{
+% for i,r in enumerate(block.get_regs_flat()):
 <% index_str = "{}".format(i).rjust(max_regs_char) %>\
   % if r.width > 16:
     4'b 1111${" " if i == num_regs-1 else ","} // index[${index_str}] ${block.name.upper()}_${r.name.upper()}
diff --git a/util/reggen/reg_top.tpl.sv b/util/reggen/reg_top.tpl.sv
index bc26c68..0aa529a 100644
--- a/util/reggen/reg_top.tpl.sv
+++ b/util/reggen/reg_top.tpl.sv
@@ -7,11 +7,10 @@
   num_wins = len(block.wins)
   num_wins_width = ((num_wins+1).bit_length()) - 1
   num_dsp  = num_wins + 1
-  num_regs = len(block.regs)
-  max_regs_char = len("{}".format(num_regs-1))
   params = [p for p in block.params if p["local"] == "false"]
+  max_regs_char = len("{}".format(block.get_n_regs_flat()-1))
+  regs_flat = block.get_regs_flat()
 %>
-
 module ${block.name}_reg_top ${print_param(params)}(
   input clk_i,
   input rst_ni,
@@ -138,7 +137,7 @@
   // Define SW related signals
   // Format: <reg>_<field>_{wd|we|qs}
   //        or <reg>_{wd|we|qs} if field == 1 or 0
-  % for r in block.regs:
+  % for r in regs_flat:
     % if len(r.fields) == 1:
 <%
       msb = r.fields[0].msb
@@ -169,41 +168,96 @@
 
   // Register instances
   % for r in block.regs:
-  // R[${r.name}]: V(${str(r.hwext)})
-    % if len(r.fields) == 1:
+  ######################## multiregister ###########################
+    % if r.is_multi_reg():
 <%
-      f = r.fields[0]
-      finst_name = r.name
-      fsig_name = r.name
-      msb = f.msb
-      lsb = f.lsb
-      swaccess = f.swaccess
-      swrdaccess = f.swrdaccess
-      swwraccess = f.swwraccess
-      hwaccess = f.hwaccess
-      hwqe = f.hwqe
-      hwre = f.hwre
-      hwext = r.hwext
-      resval = f.resval
-      regwen = r.regwen
+      mreg_flat = r.get_regs_flat()
+      k = 0
+%>
+      % for sr in mreg_flat:
+  // Subregister ${k} of Multireg ${r.name}
+  // R[${sr.name}]: V(${str(sr.hwext)})
+        % if len(sr.fields) == 1:
+<%
+          f = sr.fields[0]
+          finst_name = sr.name
+          fsig_name = r.name + "[%d]" % k
+          msb = f.msb
+          lsb = f.lsb
+          swaccess = f.swaccess
+          swrdaccess = f.swrdaccess
+          swwraccess = f.swwraccess
+          hwaccess = f.hwaccess
+          hwqe = f.hwqe
+          hwre = f.hwre
+          hwext = sr.hwext
+          resval = f.resval
+          regwen = sr.regwen
+          k = k + 1
 %>
 ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
+        % else:
+          % for f in sr.fields:
+<%
+            finst_name = sr.name + "_" + f.name
+            fsig_name = r.name + "[%d]" % k
+            msb = f.msb
+            lsb = f.lsb
+            swaccess = f.swaccess
+            swrdaccess = f.swrdaccess
+            swwraccess = f.swwraccess
+            hwaccess = f.hwaccess
+            hwqe = f.hwqe
+            hwre = f.hwre
+            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
+        % endif
+      ## for: mreg_flat
+      % endfor
+######################## register with single field ###########################
+    % elif len(r.fields) == 1:
+  // R[${r.name}]: V(${str(r.hwext)})
+<%
+        f = r.fields[0]
+        finst_name = r.name
+        fsig_name = r.name
+        msb = f.msb
+        lsb = f.lsb
+        swaccess = f.swaccess
+        swrdaccess = f.swrdaccess
+        swwraccess = f.swwraccess
+        hwaccess = f.hwaccess
+        hwqe = f.hwqe
+        hwre = f.hwre
+        hwext = r.hwext
+        resval = f.resval
+        regwen = r.regwen
+%>
+${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
+######################## register with multiple fields ###########################
     % else:
+  // R[${r.name}]: V(${str(r.hwext)})
       % for f in r.fields:
 <%
-      finst_name = r.name + "_" + f.name
-      fsig_name = r.name + "." + f.name
-      msb = f.msb
-      lsb = f.lsb
-      swaccess = f.swaccess
-      swrdaccess = f.swrdaccess
-      swwraccess = f.swwraccess
-      hwaccess = f.hwaccess
-      hwqe = f.hwqe
-      hwre = f.hwre
-      hwext = r.hwext
-      resval = f.resval
-      regwen = r.regwen
+        finst_name = r.name + "_" + f.name
+        fsig_name = r.name + "." + f.name
+        msb = f.msb
+        lsb = f.lsb
+        swaccess = f.swaccess
+        swrdaccess = f.swrdaccess
+        swwraccess = f.swwraccess
+        hwaccess = f.hwaccess
+        hwqe = f.hwqe
+        hwre = f.hwre
+        hwext = r.hwext
+        resval = f.resval
+        regwen = r.regwen
 %>
   //   F[${f.name}]: ${f.msb}:${f.lsb}
 ${finst_gen(finst_name, fsig_name, msb, lsb, swaccess, swrdaccess, swwraccess, hwaccess, hwqe, hwre, hwext, resval, regwen)}
@@ -213,10 +267,11 @@
   ## for: block.regs
   % endfor
 
-  logic [${len(block.regs)-1}:0] addr_hit;
+
+  logic [${len(regs_flat)-1}:0] addr_hit;
   always_comb begin
     addr_hit = '0;
-    % for i,r in enumerate(block.regs):
+    % for i,r in enumerate(regs_flat):
     addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${block.name.upper()}_${r.name.upper()}_OFFSET);
     % endfor
   end
@@ -226,12 +281,12 @@
   // Check sub-word write is permitted
   always_comb begin
     wr_err = 1'b0;
-    % for i,r in enumerate(block.regs):
+    % for i,r in enumerate(regs_flat):
 <% index_str = "{}".format(i).rjust(max_regs_char) %>\
     if (addr_hit[${index_str}] && reg_we && (${block.name.upper()}_PERMIT[${index_str}] != (${block.name.upper()}_PERMIT[${index_str}] & reg_be))) wr_err = 1'b1 ;
     % endfor
   end
-  % for i, r in enumerate(block.regs):
+  % for i, r in enumerate(regs_flat):
     % if len(r.fields) == 1:
 <%
       f = r.fields[0]
@@ -264,7 +319,7 @@
   always_comb begin
     reg_rdata_next = '0;
     unique case (1'b1)
-      % for i, r in enumerate(block.regs):
+      % for i, r in enumerate(regs_flat):
         % if len(r.fields) == 1:
 <%
           f = r.fields[0]
diff --git a/util/reggen/uvm_reg.tpl.sv b/util/reggen/uvm_reg.tpl.sv
index 249bdb0..40f5f22 100644
--- a/util/reggen/uvm_reg.tpl.sv
+++ b/util/reggen/uvm_reg.tpl.sv
@@ -5,13 +5,14 @@
 // UVM Registers auto-generated by `reggen` containing data structure
 // Do Not Edit directly
 <% from reggen import (gen_dv)
+regs_flat = block.get_regs_flat()
 %>\
 ##// [PY-COMMENT]: function to generate typedefs recursively for blocks
 <%def name="typedefs(block)">\
 % for b in block.blocks:
 ${typedefs(b)}
 % endfor
-% for r in block.regs:
+% for r in regs_flat:
 typedef class ${gen_dv.rcname(block, r)};
 % endfor
 % for w in block.wins:
@@ -26,7 +27,7 @@
 
 % endfor
 // Block: ${block.name}
-% for r in block.regs:
+% for r in regs_flat:
 <%
   reg_width = block.width
 %>\
@@ -108,10 +109,10 @@
 % for b in block.blocks:
   rand ${gen_dv.bcname(b)} ${b.name};
 % endfor
-% if block.regs:
+% if regs_flat:
   // registers
 % endif
-% for r in block.regs:
+% for r in regs_flat:
   rand ${gen_dv.rcname(block, r)} ${r.name};
 % endfor
 % if block.wins:
@@ -145,11 +146,11 @@
     default_map.add_submap(.child_map(${b.name}.default_map),
                            .offset(base_addr + ${gen_dv.sv_base_addr(b)}));
 % endfor
-% if block.regs:
+% if regs_flat:
 
     // create registers
 % endif
-% for r in block.regs:
+% for r in regs_flat:
 <%
   reg_name = r.name
   reg_right = r.dvrights