|  | #!/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() |