blob: c7d4bfa4fcf8e3af352688c3d433c4853a5411ac [file] [log] [blame]
# 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))