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