[reggen] Define a class wrapping the top-level IP block

The bulk of this patch is in ip_block.py, which defines the IpBlock class.
This object replaces the top-level dictionary that we were parsing.
Client code then replaces something like this:

    obj = hjson.load(hjson_file.open('r'),
                     use_decimal=True,
                     object_pairs_hook=OrderedDict)
    if validate.validate(obj, params=[]) != 0:
        log.info("Parsing %s configuration failed." % hjson_file)
        sys.exit(1)

with

    obj = IpBlock.from_path(str(hjson_file), [])

where obj is now an IpBlock object instead of a dict.

Other than some pesky rewrites in the various gen_FOO scripts and
template files, the other big change on the reggen side was to replace
the hierarchical "Block" class that was defined in data.py. Now, we
have a Top class (created by topgen code) and a Top can contain
multiple blocks. We've also now got some validation logic to make sure
that the sub-blocks and memories don't overlap: I'm not sure that was
there before.

As well as changing how we load files (as described above), topgen
also needed a bit of work. We now have to convert various objects to
dicts in the merge stage. (Before, we cloned the dictionaries and
added some keys; now we construct the new dictionary explicitly).

The idea is that in time we'll start to generate objects instead of
dicts in topgen as well. As a bonus, we should be able to get rid of
some of the spurious "dump & load" logic found there.

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index 5bc04dd..a0df901 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -11,64 +11,14 @@
 from pkg_resources import resource_filename
 
 from .access import HwAccess, SwRdAccess, SwWrAccess
-from .data import Block
+from .ip_block import IpBlock
 
 
 def escape_name(name):
     return name.lower().replace(' ', '_')
 
 
-def check_field_bool(obj, field, default):
-    if field in obj:
-        return True if obj[field] == "true" else False
-    else:
-        return default
-
-
-def json_to_reg(obj):
-    """Converts JSON OrderedDict into structure having useful information for
-    Template to use.
-
-    Main purpose of this function is:
-        - Add Offset value based on auto calculation
-        - Prepare Systemverilog data structure to generate _pkg file
-    """
-    block = Block()
-
-    # Name
-    block.name = escape_name(obj["name"])
-    log.info("Processing module: %s", block.name)
-
-    block.width = int(obj["regwidth"], 0)
-
-    if block.width != 32 and block.width != 64:
-        log.error(
-            "Current reggen tool doesn't support field width that is not 32 nor 64"
-        )
-
-    log.info("Data Width is set to %d bits", block.width)
-
-    block.params = obj["param_list"] if "param_list" in obj else []
-
-    block.hier_path = obj["hier_path"] if "hier_path" in obj else ""
-
-    block.reg_block = obj['registers']
-
-    # Last offset and calculate space
-    #  Later on, it could use block.regs[-1].genoffset
-    if "space" in obj:
-        block.addr_width = int(obj["space"], 0).bit_length()
-    else:
-        block.addr_width = (obj["gensize"] - 1).bit_length()
-
-    return block
-
-
-def gen_rtl(obj, outdir):
-    # obj: OrderedDict
-
-    block = json_to_reg(obj)
-
+def gen_rtl(block: IpBlock, outdir: str):
     # Read Register templates
     reg_top_tpl = Template(
         filename=resource_filename('reggen', 'reg_top.sv.tpl'))
@@ -76,7 +26,7 @@
         filename=resource_filename('reggen', 'reg_pkg.sv.tpl'))
 
     # Generate pkg.sv with block name
-    with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w',
+    with open(outdir + "/" + block.name.lower() + "_reg_pkg.sv", 'w',
               encoding='UTF-8') as fout:
         try:
             fout.write(
@@ -89,7 +39,7 @@
             return 1
 
     # Generate top.sv
-    with open(outdir + "/" + block.name + "_reg_top.sv", 'w',
+    with open(outdir + "/" + block.name.lower() + "_reg_top.sv", 'w',
               encoding='UTF-8') as fout:
         try:
             fout.write(