blob: 122a2c61ddfe856a7dc685d88e5fc7b0ee139c5b [file] [log] [blame]
#!/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"""Generates boilerplate code for the FPV testbench setup.
"""
import argparse
import sys
from io import StringIO
from pathlib import Path
from mako.template import Template
import fpvgen.sv_parse as sv_parse
def main():
parser = argparse.ArgumentParser(
prog="fpvgen",
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Boilerplate code generator for FPV testbenches. Can be used for
comportable or non-comportable IPs.
The generator creates the FuseSoC core file and two additional
subfolders 'tb' and 'vip' in the output directory. It will place stubs
for the testbench and bind files into the 'tb' subfolder, and a stub for
the FPV assertions into the 'vip' (verification IP) subfolder.
The generator needs the path to the top-level module of the IP to be
tested. E.g., suppose we would like to generate an FPV testbench for a
FIFO primitive located at 'hw/ip/prim/rtl/prim_fifo_sync.sv' we can
invoke the generator as follows:
util/fpvgen.py hw/ip/prim/rtl/prim_fifo_sync.sv
By default, the output directory is assumed to be '../fpv' with respect
to the toplevel module, but this can be overriden using the -eo switch.
Further if the IP is comportable, this can be indicated using the -c
switch, which causes the generator to add a bind statement for the CSR
FPV assertions in the testbench.""",
add_help=True)
parser.add_argument(
'file',
type=Path,
help="""Relative path to the SystemVerilog file of the module for which
the code shall be generated. This can be a primitive or a comportable IP
(for which the -c switch should be set)."""
)
parser.add_argument(
'-o',
'--outdir',
type=Path,
default="",
help="""Path where to place the testbench code. This is defaults to
'../fpv' w.r.t. to the module path. For instance, if the module path is
'hw/ip/mymod/rtl/mymod.sv', the FPV testbench would be generated under
hw/ip/mymod/fpv. """
)
parser.add_argument(
'-c',
'--is_cip',
action="store_true",
default=False,
help="""Indicates whether this is a comportable IP. If yes, FPV
assertions for the TL-UL interface and CSRs are automatically bound in
the testbench. Note however that these CSR assertions need to be
generated separately using the regtool automation."""
)
args = parser.parse_args()
mod_path = args.file
if not mod_path.is_file() or mod_path.suffix != ".sv":
print("Error: %s is not a module or does not exist" % str(mod_path))
return 1
if not args.outdir:
# the default output path is ../fpv with
# respect to the module location
parentpath = mod_path.absolute().parent.parent
outpath = parentpath.joinpath("fpv")
else:
outpath = args.outdir
print("Output path is: %s" % outpath)
dut = sv_parse.parse_file(mod_path)
dut.is_cip = args.is_cip
# always include the prims
dut.deps += ["lowrisc:prim:all"]
if args.is_cip:
# for TL-UL assertions
dut.deps += ["lowrisc:ip:tlul"]
# in this case the parent directory is
# likely the correct basename of the IP
dut.deps += ["lowrisc:ip:" + parentpath.stem]
# define template files to iterate over
template_files = [(Path(__file__).parent.joinpath("fpvgen/tb.sv.tpl"), \
outpath.joinpath("tb").joinpath(mod_path.stem + "_tb.sv")), \
(Path(__file__).parent.joinpath("fpvgen/bind_fpv.sv.tpl"), \
outpath.joinpath("tb").joinpath(mod_path.stem + "_bind_fpv.sv")), \
(Path(__file__).parent.joinpath("fpvgen/assert_fpv.sv.tpl"), \
outpath.joinpath("vip").joinpath(mod_path.stem + "_assert_fpv.sv")), \
(Path(__file__).parent.joinpath("fpvgen/fusesoc.core.tpl"), \
outpath.joinpath(mod_path.stem + "_fpv.core"))]
for (tpl_file, out_file) in template_files:
print("Generating %s" % str(out_file))
out_file.parent.mkdir(parents=True, exist_ok=True)
tpl = Template(tpl_file.read_text())
out_file.write_text(tpl.render(dut=dut))
return 0
if __name__ == "__main__":
main()