blob: 1c0036ffc7778151bb371a0055286afb89291075 [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
from mako.template import Template
from mako import exceptions
from pkg_resources import resource_filename
from .data import Field, Reg, MultiReg, Window, Block
from .field_enums import HwAccess, SwRdAccess, SwWrAccess
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
f.swaccess = obj["genswaccess"]
f.swrdaccess = obj["genswrdaccess"]
f.swwraccess = obj["genswwraccess"]
f.hwaccess = obj["genhwaccess"]
f.hwqe = obj["genhwqe"]
f.hwre = obj["genhwre"]
f.hwext = reg.hwext
f.tags = obj["tags"]
f.shadowed = reg.shadowed
# 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 or MultiRegister object.
Supports nested MultiRegisters.
"""
if 'multireg' in obj:
regs = []
for genr in obj['multireg']['genregs']:
regs += [parse_reg(genr)]
# get register properties of the first register in the multireg and
# copy them to the parent
# since all regs in a multireg have the same props
reg = MultiReg(regs[0].get_reg_flat(0))
# since this is a multireg, the list of fields can
# contain regs or multiregs
reg.fields = regs
# a homogenous multireg contains only one single field that is replicated
reg.ishomog = len(obj['multireg']['fields']) == 1
# TODO: need to rework this once the underlying JSON has been changed
reg.name = escape_name(obj['multireg']['name'])
# TODO: need to reference proper param here such that it can be used
# in the package template for the array declaration
# reg.param = ...
else:
reg = Reg(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()
reg.ishomog = len(obj['fields']) == 1
reg.tags = (obj['tags'])
reg.shadowed = (obj["shadowed"] == "true")
# Parsing Fields
for f in obj["fields"]:
field = parse_field(f, reg, len(obj["fields"]))
if field is not None:
reg.fields.append(field)
reg.width = max(reg.width, field.msb + 1)
# TODO: 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)
block.params = obj["param_list"] if "param_list" in obj else []
block.hier_path = obj["hier_path"] if "hier_path" in obj else ""
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 is not None:
block.wins.append(win)
continue
block.regs += [parse_reg(r)]
# 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.sv.tpl'))
reg_pkg_tpl = Template(
filename=resource_filename('reggen', 'reg_pkg.sv.tpl'))
# Generate pkg.sv with block name
with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w',
encoding='UTF-8') as fout:
try:
fout.write(
reg_pkg_tpl.render(block=block,
HwAccess=HwAccess,
SwRdAccess=SwRdAccess,
SwWrAccess=SwWrAccess))
except: # noqa: F722 for template Exception handling
log.error(exceptions.text_error_template().render())
# Generate top.sv
with open(outdir + "/" + block.name + "_reg_top.sv", 'w',
encoding='UTF-8') as fout:
try:
fout.write(
reg_top_tpl.render(block=block,
HwAccess=HwAccess,
SwRdAccess=SwRdAccess,
SwWrAccess=SwWrAccess))
except: # noqa: F722 for template Exception handling
log.error(exceptions.text_error_template().render())