tinyriscv/tools/regtool/regtool.py

236 lines
8.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Command-line tool to validate and convert register hjson
"""
import argparse
import logging as log
import re
import sys
from pathlib import PurePath
from reggen import (gen_cheader, gen_dv, gen_fpv, gen_html,
gen_json, gen_rtl, gen_selfdoc, version)
from reggen.ip_block import IpBlock
DESC = """regtool, generate register info from Hjson source"""
USAGE = '''
regtool [options]
regtool [options] <input>
regtool (-h | --help)
regtool (-V | --version)
'''
def main():
verbose = 0
parser = argparse.ArgumentParser(
prog="regtool",
formatter_class=argparse.RawDescriptionHelpFormatter,
usage=USAGE,
description=DESC)
parser.add_argument('input',
nargs='?',
metavar='file',
type=argparse.FileType('r'),
default=sys.stdin,
help='input file in Hjson type')
parser.add_argument('-d',
action='store_true',
help='Output register documentation (html)')
parser.add_argument('--cdefines',
'-D',
action='store_true',
help='Output C defines header')
parser.add_argument('--doc',
action='store_true',
help='Output source file documentation (gfm)')
parser.add_argument('-j',
action='store_true',
help='Output as formatted JSON')
parser.add_argument('-c', action='store_true', help='Output as JSON')
parser.add_argument('-r',
action='store_true',
help='Output as SystemVerilog RTL')
parser.add_argument('-s',
action='store_true',
help='Output as UVM Register class')
parser.add_argument('-f',
action='store_true',
help='Output as FPV CSR rw assertion module')
parser.add_argument('--outdir',
'-t',
help='Target directory for generated RTL; '
'tool uses ../rtl if blank.')
parser.add_argument('--dv-base-prefix',
default='dv_base',
help='Prefix for the DV register classes from which '
'the register models are derived.')
parser.add_argument('--outfile',
'-o',
type=argparse.FileType('w'),
default=sys.stdout,
help='Target filename for json, html, gfm.')
parser.add_argument('--verbose',
'-v',
action='store_true',
help='Verbose and run validate twice')
parser.add_argument('--param',
'-p',
type=str,
default="",
help='''Change the Parameter values.
Only integer value is supported.
You can add multiple param arguments.
Format: ParamA=ValA;ParamB=ValB
''')
parser.add_argument('--version',
'-V',
action='store_true',
help='Show version')
parser.add_argument('--novalidate',
action='store_true',
help='Skip validate, just output json')
args = parser.parse_args()
if args.version:
version.show_and_exit(__file__, ["Hjson", "Mako"])
verbose = args.verbose
if (verbose):
log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
else:
log.basicConfig(format="%(levelname)s: %(message)s")
# Entries are triples of the form (arg, (format, dirspec)).
#
# arg is the name of the argument that selects the format. format is the
# name of the format. dirspec is None if the output is a single file; if
# the output needs a directory, it is a default path relative to the source
# file (used when --outdir is not given).
arg_to_format = [('j', ('json', None)), ('c', ('compact', None)),
('d', ('html', None)), ('doc', ('doc', None)),
('r', ('rtl', 'rtl')), ('s', ('dv', 'dv')),
('f', ('fpv', 'fpv/vip')), ('cdefines', ('cdh', None))]
format = None
dirspec = None
for arg_name, spec in arg_to_format:
if getattr(args, arg_name):
if format is not None:
log.error('Multiple output formats specified on '
'command line ({} and {}).'.format(format, spec[0]))
sys.exit(1)
format, dirspec = spec
if format is None:
format = 'hjson'
infile = args.input
# Split parameters into key=value pairs.
raw_params = args.param.split(';') if args.param else []
params = []
for idx, raw_param in enumerate(raw_params):
tokens = raw_param.split('=')
if len(tokens) != 2:
raise ValueError('Entry {} in list of parameter defaults to '
'apply is {!r}, which is not of the form '
'param=value.'
.format(idx, raw_param))
params.append((tokens[0], tokens[1]))
# Define either outfile or outdir (but not both), depending on the output
# format.
outfile = None
outdir = None
if dirspec is None:
if args.outdir is not None:
log.error('The {} format expects an output file, '
'not an output directory.'.format(format))
sys.exit(1)
outfile = args.outfile
else:
if args.outfile is not sys.stdout:
log.error('The {} format expects an output directory, '
'not an output file.'.format(format))
sys.exit(1)
if args.outdir is not None:
outdir = args.outdir
elif infile is not sys.stdin:
outdir = str(PurePath(infile.name).parents[1].joinpath(dirspec))
else:
# We're using sys.stdin, so can't infer an output directory name
log.error(
'The {} format writes to an output directory, which '
'cannot be inferred automatically if the input comes '
'from stdin. Use --outdir to specify it manually.'.format(
format))
sys.exit(1)
if format == 'doc':
with outfile:
gen_selfdoc.document(outfile)
exit(0)
srcfull = infile.read()
try:
obj = IpBlock.from_text(srcfull, params, infile.name)
except ValueError as err:
log.error(str(err))
exit(1)
if args.novalidate:
with outfile:
gen_json.gen_json(obj, outfile, format)
outfile.write('\n')
else:
if format == 'rtl':
return gen_rtl.gen_rtl(obj, outdir)
if format == 'dv':
return gen_dv.gen_dv(obj, args.dv_base_prefix, outdir)
if format == 'fpv':
return gen_fpv.gen_fpv(obj, outdir)
src_lic = None
src_copy = ''
found_spdx = None
found_lunder = None
copy = re.compile(r'.*(copyright.*)|(.*\(c\).*)', re.IGNORECASE)
spdx = re.compile(r'.*(SPDX-License-Identifier:.+)')
lunder = re.compile(r'.*(Licensed under.+)', re.IGNORECASE)
for line in srcfull.splitlines():
mat = copy.match(line)
if mat is not None:
src_copy += mat.group(1)
mat = spdx.match(line)
if mat is not None:
found_spdx = mat.group(1)
mat = lunder.match(line)
if mat is not None:
found_lunder = mat.group(1)
if found_lunder:
src_lic = found_lunder
if found_spdx:
src_lic += '\n' + found_spdx
with outfile:
if format == 'html':
return gen_html.gen_html(obj, outfile)
elif format == 'cdh':
return gen_cheader.gen_cdefines(obj, outfile, src_lic, src_copy)
else:
return gen_json.gen_json(obj, outfile, format)
outfile.write('\n')
if __name__ == '__main__':
sys.exit(main())