Start of public OpenTitan development history

Code contributors:
Alex Bradbury <asb@lowrisc.org>
Cindy Chen <chencindy@google.com>
Eunchan Kim <eunchan@google.com>
Gaurang Chitroda <gaurangg@google.com>
Mark Hayter <mark.hayter@gmail.com>
Michael Schaffner <msf@google.com>
Miguel Osorio <miguelosorio@google.com>
Nils Graf <nilsg@google.com>
Philipp Wagner <phw@lowrisc.org>
Pirmin Vogel <vogelpi@lowrisc.org>
Ram Babu Penugonda <rampenugonda@google.com>
Scott Johnson <scottdj@google.com>
Shail Kushwah <kushwahs@google.com>
Srikrishna Iyer <sriyer@google.com>
Steve Nelson <Steve.Nelson@wdc.com>
Tao Liu <taliu@google.com>
Timothy Chen <timothytim@google.com>
Tobias Wölfel <tobias.woelfel@mailbox.org>
Weicai Yang <weicai@google.com>
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
new file mode 100644
index 0000000..c7d4bfa
--- /dev/null
+++ b/util/reggen/gen_rtl.py
@@ -0,0 +1,268 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Generate SystemVerilog designs from validated register json tree
+"""
+
+import logging as log
+import operator
+import sys
+
+from mako.template import Template
+from pkg_resources import resource_filename
+
+from .field_enums import HwAccess, SwAccess, SwRdAccess, SwWrAccess
+
+
+class Field():
+    """Field in a register.
+
+    Field class contains necessary info to generate RTL code.
+    It has two additional (tool generated) feidls, swrdaccess and swwraccess,
+    which represent read and write type. This makes RTL generation code simpler.
+    """
+    name = ""  # required
+    msb = 31  # required
+    lsb = 0  # required
+    resval = 0  # optional
+    swaccess = SwAccess.NONE  # optional
+    swrdaccess = SwRdAccess.NONE
+    swwraccess = SwWrAccess.NONE
+    hwaccess = HwAccess.HRO
+    hwqe = False
+    hwre = False
+
+    def __init__(self):
+        self.name = ""  # required
+        self.msb = 31  # required
+        self.lsb = 0  # required
+        self.resval = 0  # optional
+        self.swaccess = SwAccess.NONE  # optional
+        self.swrdaccess = SwRdAccess.NONE
+        self.swwraccess = SwWrAccess.NONE
+        self.hwaccess = HwAccess.HRO
+        self.hwqe = False
+        self.hwre = False
+
+
+class Register():
+    name = ""
+    offset = 0
+    hwqe = False
+    hwre = False
+    hwext = False  # External register
+    resval = 0
+    dvrights = "RO"  # Used by UVM REG only
+    regwen = ""
+    fields = []
+
+    def __init__(self):
+        self.name = ""
+        self.offset = 0
+        self.hwqe = False
+        self.hwre = False
+        self.hwext = False  # External register
+        self.resval = 0
+        self.dvrights = "RO"  # Used by UVM REG only
+        self.regwen = ""
+        self.fields = []
+
+
+class Window():
+    base_addr = 0
+    limit_addr = 0
+    n_bits = 0
+
+    def __init__(self):
+        self.base_addr = 0
+        self.limit_addr = 0
+        self.n_bits = 0
+
+class Block():
+    width = 32
+    addr_width = 12
+    base_addr = 0
+    name = ""
+    regs = []
+    wins = []
+    blocks = []
+
+    def __init__(self):
+        self.width = 32
+        self.addr_width = 12
+        self.base_addr = 0
+        self.name = ""
+        self.regs = []
+        self.wins = []
+        self.blocks = []
+
+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 parse_field(obj, reg, nfields):
+    """Convert OrderedDict field into Field class
+    """
+    f = Field()
+    f.name = escape_name(obj["name"])
+    # if name doesn't exist and only one field in a reg
+    if f.name == "" and nfields == 1:
+        f.name = reg.name
+
+    # MSB, LSB
+    f.lsb = obj["bitinfo"][2]
+    f.msb = f.lsb + obj["bitinfo"][1] - 1
+
+    #assert not 'swaccess' in obj, "R[%s] F[%s]: SwAccess in Field not supported" % (reg.name, f.name)
+    f.swaccess = obj["genswaccess"]
+    f.swrdaccess = obj["genswrdaccess"]
+    f.swwraccess = obj["genswwraccess"]
+    f.hwaccess = obj["genhwaccess"]
+    f.hwqe = obj["genhwqe"]
+    f.hwre = obj["genhwre"]
+
+    # resval handling. `genresval` has zero value if `resval` field is defined
+    # as unknown 'x'
+    f.resval = obj["genresval"]
+
+    return f
+
+
+def parse_reg(obj):
+    """Convert OrderedDict register into Register class
+    """
+
+    reg = Register()
+    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()
+
+    # Parsing Fields
+    for f in obj["fields"]:
+        field = parse_field(f, reg, len(obj["fields"]))
+        if field != None:
+            reg.fields.append(field)
+
+    # 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
+
+
+def parse_win(obj, width):
+    # Convert register window fields into Window class
+    # base_addr : genoffset
+    # limit_addr : genoffset + items*width
+    win = Window()
+    win.name = obj["name"]
+    win.base_addr = obj["genoffset"]
+    win.limit_addr = obj["genoffset"] + int(obj["items"]) * (width // 8)
+    win.dvrights = obj["swaccess"]
+    win.n_bits = obj["genvalidbits"]
+
+    # TODO: Generate warnings of `noalign` or `unusual`
+    return win
+
+
+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)
+
+    for r in obj["registers"]:
+        # Check if any exception condition hit
+        if 'reserved' in r:
+            continue
+        elif 'skipto' in r:
+            continue
+        elif 'sameaddr' in r:
+            log.error("Current tool doesn't support 'sameaddr' type")
+            continue
+        elif 'window' in r:
+            win = parse_win(r['window'], block.width)
+            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
+
+    # 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)
+
+    # Read Register templates
+    reg_top_tpl = Template(
+        filename=resource_filename('reggen', 'reg_top.tpl.sv'))
+    reg_pkg_tpl = Template(
+        filename=resource_filename('reggen', 'reg_pkg.tpl.sv'))
+
+    # Generate pkg.sv with block name
+    with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w',
+              encoding='UTF-8') as fout:
+        fout.write(
+            reg_pkg_tpl.render(block=block,
+                               HwAccess=HwAccess,
+                               SwRdAccess=SwRdAccess,
+                               SwWrAccess=SwWrAccess))
+
+    # Generate top.sv
+    with open(outdir + "/" + block.name + "_reg_top.sv", 'w',
+              encoding='UTF-8') as fout:
+        fout.write(
+            reg_top_tpl.render(block=block,
+                               HwAccess=HwAccess,
+                               SwRdAccess=SwRdAccess,
+                               SwWrAccess=SwWrAccess))