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))