[reggen] Add mubi support into hjson

- This primarily ensures reset values are consistent
- Fixes #8521

Signed-off-by: Timothy Chen <timothytim@google.com>

[reggen] Report error when bit fields are re-used

- Fixes #7566

Signed-off-by: Timothy Chen <timothytim@google.com>

Update used bits check

Signed-off-by: Timothy Chen <timothytim@google.com>

[reggen] Relocate mubi script

- Add to `util/design` area so that it can be directly referenced
  by fields.py

Signed-off-by: Timothy Chen <timothytim@google.com>

address comments

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/entropy_src/data/entropy_src.hjson b/hw/ip/entropy_src/data/entropy_src.hjson
index 03fc857..ba0df93 100644
--- a/hw/ip/entropy_src/data/entropy_src.hjson
+++ b/hw/ip/entropy_src/data/entropy_src.hjson
@@ -135,49 +135,55 @@
       fields: [
         { bits: "3:0",
           name: "ENABLE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will enable the ENTROPY_SRC module.
                 '''
-          resval: "0x5"
+          resval: false,
         },
         { bits: "7:4",
           name: "ENTROPY_DATA_REG_ENABLE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will enable reading entropy values from the
                 ENTROPY_DATA register. This function also requires that the efuse_es_sw_reg_en
                 input is set.
                 '''
-          resval: "0x5"
+          resval: "false"
         },
         { bits: "11:8",
           name: "LFSR_ENABLE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will enable the ENTROPY_SRC LFSR mode.
                 '''
-          resval: "0x5"
+          resval: false
         },
         { bits: "15:12",
           name: "BOOT_BYPASS_DISABLE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will disables the initial generation of non-FIPS entropy.
                 '''
-          resval: "0x5"
+          resval: false
         },
         { bits: "19:16",
           name: "HEALTH_TEST_CLR",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will clear all registers related to the
                 health test operations.
                 '''
-          resval: "0x5"
+          resval: false
         },
         { bits: "23:20",
           name: "RNG_BIT_ENABLE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA enables the single RNG bit mode, where only
                 one bit is sampled.
                 '''
-          resval: "0x5"
+          resval: false
         },
         { bits: "25:24",
           name: "RNG_BIT_SEL",
@@ -218,21 +224,23 @@
       fields: [
         { bits: "3:0",
           name: "ES_ROUTE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA routes the generated entropy value to the ENTROPY_DATA
                 register to be read by firmware. When this field is 0x5, the generated
                 entropy will be forwarded out of this module to the hardware interface.
                 '''
-          resval: "0x5"
+          resval: false
         },
         { bits: "7:4",
           name: "ES_TYPE",
+          mubi: true,
           desc: '''
                 Setting this field to 0xA will bypass the conditioning logic and bring raw entropy
                 data to the ENTROPY_DATA register. When 0x5, FIPS compliant entropy
                 will be brought the ENTROPY_DATA register, after being conditioned.
                 '''
-          resval: "0x5"
+          resval: false
         },
       ]
     },
diff --git a/hw/ip/prim/data/prim_mubi.core.tpl b/util/design/data/prim_mubi.core.tpl
similarity index 100%
rename from hw/ip/prim/data/prim_mubi.core.tpl
rename to util/design/data/prim_mubi.core.tpl
diff --git a/hw/ip/prim/data/prim_mubi_dec.sv.tpl b/util/design/data/prim_mubi_dec.sv.tpl
similarity index 100%
rename from hw/ip/prim/data/prim_mubi_dec.sv.tpl
rename to util/design/data/prim_mubi_dec.sv.tpl
diff --git a/hw/ip/prim/data/prim_mubi_pkg.sv.tpl b/util/design/data/prim_mubi_pkg.sv.tpl
similarity index 93%
rename from hw/ip/prim/data/prim_mubi_pkg.sv.tpl
rename to util/design/data/prim_mubi_pkg.sv.tpl
index 5abc940..34dbd5c 100644
--- a/hw/ip/prim/data/prim_mubi_pkg.sv.tpl
+++ b/util/design/data/prim_mubi_pkg.sv.tpl
@@ -12,14 +12,12 @@
 
 package prim_mubi_pkg;
 
-% for n in range(1, n_max_nibbles + 1):
 <%
-  nbits = n * 4
-  true_val = ''
-  false_val = ''
-  for k in range(1,n+1):
-    true_val = ('A' if (k % 2) else '5') + true_val
-    false_val = ('5' if (k % 2) else 'A') + false_val
+import prim_mubi as prim_mubi
+%>\
+% for n in range(1, N_MAX_NIBBLES + 1):
+<%
+   nbits = n * 4
 %>\
   //////////////////////////////////////////////
   // ${nbits} Bit Multibit Type and Functions //
@@ -27,8 +25,8 @@
 
   parameter int MuBi${nbits}Width = ${nbits};
   typedef enum logic [MuBi${nbits}Width-1:0] {
-    MuBi${nbits}True = ${nbits}'h${true_val}, // enabled
-    MuBi${nbits}False = ${nbits}'h${false_val}  // disabled
+    MuBi${nbits}True = ${nbits}'h${prim_mubi.mubi_value(True, nbits)}, // enabled
+    MuBi${nbits}False = ${nbits}'h${prim_mubi.mubi_value(False, nbits)}  // disabled
   } mubi${nbits}_e;
 
   // make a typedef such that this can be used as an intersignal type as well
diff --git a/hw/ip/prim/data/prim_mubi_sender.sv.tpl b/util/design/data/prim_mubi_sender.sv.tpl
similarity index 100%
rename from hw/ip/prim/data/prim_mubi_sender.sv.tpl
rename to util/design/data/prim_mubi_sender.sv.tpl
diff --git a/hw/ip/prim/data/prim_mubi_sync.sv.tpl b/util/design/data/prim_mubi_sync.sv.tpl
similarity index 100%
rename from hw/ip/prim/data/prim_mubi_sync.sv.tpl
rename to util/design/data/prim_mubi_sync.sv.tpl
diff --git a/util/design/prim_mubi.py b/util/design/prim_mubi.py
new file mode 100755
index 0000000..f73ce54
--- /dev/null
+++ b/util/design/prim_mubi.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+r"""Converts mubi mako templates
+"""
+from mako.template import Template
+
+MUBI_PKG_TPL_PATH = "util/design/data/prim_mubi_pkg.sv.tpl"
+MUBI_CORE_TPL_PATH = "util/design/data/prim_mubi.core.tpl"
+MUBI_SENDER_TPL_PATH = "util/design/data/prim_mubi_sender.sv.tpl"
+MUBI_SYNC_TPL_PATH = "util/design/data/prim_mubi_sync.sv.tpl"
+MUBI_DEC_TPL_PATH = "util/design/data/prim_mubi_dec.sv.tpl"
+
+MUBI_PKG_OUT_PATH = "hw/ip/prim/rtl/prim_mubi_pkg.sv"
+MUBI_CORE_OUT_PATH = "hw/ip/prim/prim_mubi.core"
+MUBI_SENDER_OUT_PATH = "hw/ip/prim/rtl/prim_mubi{}_sender.sv"
+MUBI_SYNC_OUT_PATH = "hw/ip/prim/rtl/prim_mubi{}_sync.sv"
+MUBI_DEC_OUT_PATH = "hw/ip/prim/rtl/prim_mubi{}_dec.sv"
+
+N_MAX_NIBBLES = 4
+
+
+def is_width_valid(width):
+    return width % 4 == 0
+
+
+def mubi_value(sel: bool, width: int):
+
+    if is_width_valid(width):
+        nibble = int(width / 4)
+    else:
+        raise ValueError(f'mubi does not support width of {width}')
+
+    true_val = ''
+    false_val = ''
+
+    for k in range(1, nibble + 1):
+        true_val = ('A' if (k % 2) else '5') + true_val
+        false_val = ('5' if (k % 2) else 'A') + false_val
+
+    return true_val if sel else false_val
+
+
+def main():
+
+    tpls = [
+        (MUBI_PKG_TPL_PATH, MUBI_PKG_OUT_PATH),
+        (MUBI_CORE_TPL_PATH, MUBI_CORE_OUT_PATH),
+    ]
+    for tpl, out in tpls:
+        with open(tpl) as inf:
+            reg_tpl = Template(inf.read())
+            with open(out, 'w') as outf:
+                outf.write(reg_tpl.render(n_max_nibbles=N_MAX_NIBBLES))
+
+    tpls = [
+        (MUBI_SENDER_TPL_PATH, MUBI_SENDER_OUT_PATH),
+        (MUBI_SYNC_TPL_PATH, MUBI_SYNC_OUT_PATH),
+        (MUBI_DEC_TPL_PATH, MUBI_DEC_OUT_PATH)
+    ]
+    for tpl, out in tpls:
+        with open(tpl) as inf:
+            reg_tpl = Template(inf.read())
+            for n in range(1, N_MAX_NIBBLES + 1):
+                n_bits = n * 4
+                with open(out.format(n_bits), 'w') as outf:
+                    outf.write(reg_tpl.render(n_bits=n_bits))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/util/reggen/field.py b/util/reggen/field.py
index 2f9d0b9..0ce5f37 100644
--- a/util/reggen/field.py
+++ b/util/reggen/field.py
@@ -7,9 +7,11 @@
 from .access import SWAccess, HWAccess
 from .bits import Bits
 from .enum_entry import EnumEntry
-from .lib import (check_keys, check_str, check_name,
+from .lib import (check_keys, check_str, check_name, check_bool,
                   check_list, check_str_list, check_xint)
 from .params import ReggenParams
+from design.prim_mubi import is_width_valid, mubi_value #type: ignore
+
 
 REQUIRED_FIELDS = {
     'bits': ['b', "bit or bit range (msb:lsb)"]
@@ -39,6 +41,10 @@
     'tags': [
         's',
         "tags for the field, followed by the format 'tag_name:item1:item2...'"
+    ],
+    'mubi': [
+        'b',
+        "boolean flag for whether the field is a multi-bit type"
     ]
 }
 
@@ -110,14 +116,29 @@
         else:
             hwaccess = default_hwaccess
 
+        raw_mubi = rd.get('mubi', False)
+        is_mubi = check_bool(raw_mubi, 'mubi field for {}'.format(where))
+
         # Currently internal shadow registers do not support hw write type
         if not hwext and shadowed and hwaccess.allows_write():
             raise ValueError('Internal Shadow registers do not currently support '
                              'hardware write')
 
         bits = Bits.from_raw(where, reg_width, params, rd['bits'])
-
         raw_resval = rd.get('resval')
+        if is_mubi:
+            # When mubi type, the resval supplied is a boolean which is converted
+            # to a mubi value
+            chk_resval = check_bool(raw_resval, 'resval field for {}'.format(where))
+
+            # Check mubi width is supported
+            if not is_width_valid(bits.width()):
+                raise ValueError(f'mubi field for {name} does not support width '
+                                 f'of {bits.width()}')
+
+            # Get actual integer value based on mubi selection
+            raw_resval = mubi_value(chk_resval, bits.width())
+
         if raw_resval is None:
             # The field doesn't define a reset value. Use bits from reg_resval
             # if it's defined, otherwise None (which means "x").
diff --git a/util/reggen/register.py b/util/reggen/register.py
index 2692c3d..a9205b9 100644
--- a/util/reggen/register.py
+++ b/util/reggen/register.py
@@ -295,18 +295,31 @@
                                 'fields for {} register'.format(name))
         if not raw_fields:
             raise ValueError('Register {} has no fields.'.format(name))
-        fields = [Field.from_raw(name,
-                                 idx,
-                                 len(raw_fields),
-                                 swaccess,
-                                 hwaccess,
-                                 resval,
-                                 reg_width,
-                                 params,
-                                 hwext,
-                                 shadowed,
-                                 rf)
-                  for idx, rf in enumerate(raw_fields)]
+
+        fields = []
+        used_bits = 0
+        for idx, rf in enumerate(raw_fields):
+
+            field = (Field.from_raw(name,
+                                    idx,
+                                    len(raw_fields),
+                                    swaccess,
+                                    hwaccess,
+                                    resval,
+                                    reg_width,
+                                    params,
+                                    hwext,
+                                    shadowed,
+                                    rf))
+
+            overlap_bits = used_bits & field.bits.bitmask()
+            if overlap_bits:
+                raise ValueError(f'Field {field.name} uses bits '
+                                 f'{overlap_bits:#x} that appear in other '
+                                 f'fields.')
+
+            used_bits |= field.bits.bitmask()
+            fields.append(field)
 
         raw_uea = rd.get('update_err_alert')
         if raw_uea is None: