[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_html.py b/util/reggen/gen_html.py
index 6e01600..5f7d35d 100644
--- a/util/reggen/gen_html.py
+++ b/util/reggen/gen_html.py
@@ -286,28 +286,21 @@
 # Must have called validate, so should have no errors
 
 
-def gen_html(regs, outfile, toclist=None, toclevel=3):
-    component = regs['name']
-    registers = regs['registers']
-    rnames = regs['genrnames']
+def gen_html(block, outfile, toclist=None, toclevel=3):
+    rnames = list(block.regs.name_to_offset.keys())
 
-    if 'regwidth' in regs:
-        regwidth = int(regs['regwidth'], 0)
-    else:
-        regwidth = 32
-
-    for x in registers.entries:
+    for x in block.regs.entries:
         if isinstance(x, Register):
-            gen_html_register(outfile, x, component, regwidth, rnames, toclist,
-                              toclevel)
+            gen_html_register(outfile, x, block.name, block.regwidth, rnames,
+                              toclist, toclevel)
             continue
         if isinstance(x, MultiRegister):
             for reg in x.regs:
-                gen_html_register(outfile, reg, component, regwidth, rnames,
-                                  toclist, toclevel)
+                gen_html_register(outfile, reg, block.name, block.regwidth,
+                                  rnames, toclist, toclevel)
             continue
         if isinstance(x, Window):
-            gen_html_window(outfile, x, component, regwidth, rnames,
+            gen_html_window(outfile, x, block.name, block.regwidth, rnames,
                             toclist, toclevel)
             continue