Michael Schaffner | b5a88f2 | 2019-11-26 19:43:37 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright lowRISC contributors. |
| 3 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | r"""Generates boilerplate code for the FPV testbench setup. |
| 6 | """ |
| 7 | import argparse |
| 8 | import sys |
| 9 | from io import StringIO |
| 10 | from pathlib import Path |
| 11 | |
| 12 | from mako.template import Template |
| 13 | |
| 14 | import fpvgen.sv_parse as sv_parse |
| 15 | |
| 16 | |
| 17 | def main(): |
| 18 | parser = argparse.ArgumentParser( |
| 19 | prog="fpvgen", |
| 20 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 21 | description="""\ |
| 22 | Boilerplate code generator for FPV testbenches. Can be used for |
| 23 | comportable or non-comportable IPs. |
| 24 | |
| 25 | The generator creates the FuseSoC core file and two additional |
| 26 | subfolders 'tb' and 'vip' in the output directory. It will place stubs |
| 27 | for the testbench and bind files into the 'tb' subfolder, and a stub for |
| 28 | the FPV assertions into the 'vip' (verification IP) subfolder. |
| 29 | |
| 30 | The generator needs the path to the top-level module of the IP to be |
| 31 | tested. E.g., suppose we would like to generate an FPV testbench for a |
| 32 | FIFO primitive located at 'hw/ip/prim/rtl/prim_fifo_sync.sv' we can |
| 33 | invoke the generator as follows: |
| 34 | |
| 35 | util/fpvgen.py hw/ip/prim/rtl/prim_fifo_sync.sv |
| 36 | |
| 37 | By default, the output directory is assumed to be '../fpv' with respect |
| 38 | to the toplevel module, but this can be overriden using the -eo switch. |
| 39 | |
| 40 | Further if the IP is comportable, this can be indicated using the -c |
| 41 | switch, which causes the generator to add a bind statement for the CSR |
| 42 | FPV assertions in the testbench.""", |
| 43 | add_help=True) |
| 44 | parser.add_argument( |
| 45 | 'file', |
Michael Schaffner | 1acfee1 | 2022-02-01 06:42:57 +0000 | [diff] [blame] | 46 | type=Path, |
Michael Schaffner | b5a88f2 | 2019-11-26 19:43:37 -0800 | [diff] [blame] | 47 | help="""Relative path to the SystemVerilog file of the module for which |
| 48 | the code shall be generated. This can be a primitive or a comportable IP |
| 49 | (for which the -c switch should be set).""" |
| 50 | ) |
| 51 | |
| 52 | parser.add_argument( |
| 53 | '-o', |
| 54 | '--outdir', |
Michael Schaffner | 1acfee1 | 2022-02-01 06:42:57 +0000 | [diff] [blame] | 55 | type=Path, |
Michael Schaffner | b5a88f2 | 2019-11-26 19:43:37 -0800 | [diff] [blame] | 56 | default="", |
| 57 | help="""Path where to place the testbench code. This is defaults to |
| 58 | '../fpv' w.r.t. to the module path. For instance, if the module path is |
| 59 | 'hw/ip/mymod/rtl/mymod.sv', the FPV testbench would be generated under |
| 60 | hw/ip/mymod/fpv. """ |
| 61 | ) |
| 62 | parser.add_argument( |
| 63 | '-c', |
| 64 | '--is_cip', |
| 65 | action="store_true", |
| 66 | default=False, |
| 67 | help="""Indicates whether this is a comportable IP. If yes, FPV |
| 68 | assertions for the TL-UL interface and CSRs are automatically bound in |
| 69 | the testbench. Note however that these CSR assertions need to be |
| 70 | generated separately using the regtool automation.""" |
| 71 | ) |
| 72 | |
| 73 | args = parser.parse_args() |
| 74 | |
Michael Schaffner | 1acfee1 | 2022-02-01 06:42:57 +0000 | [diff] [blame] | 75 | mod_path = args.file |
Michael Schaffner | b5a88f2 | 2019-11-26 19:43:37 -0800 | [diff] [blame] | 76 | if not mod_path.is_file() or mod_path.suffix != ".sv": |
| 77 | print("Error: %s is not a module or does not exist" % str(mod_path)) |
| 78 | return 1 |
| 79 | |
| 80 | if not args.outdir: |
| 81 | # the default output path is ../fpv with |
| 82 | # respect to the module location |
| 83 | parentpath = mod_path.absolute().parent.parent |
| 84 | outpath = parentpath.joinpath("fpv") |
| 85 | else: |
| 86 | outpath = args.outdir |
| 87 | |
| 88 | print("Output path is: %s" % outpath) |
| 89 | |
| 90 | dut = sv_parse.parse_file(mod_path) |
| 91 | dut.is_cip = args.is_cip |
| 92 | |
| 93 | # always include the prims |
| 94 | dut.deps += ["lowrisc:prim:all"] |
| 95 | |
| 96 | if args.is_cip: |
| 97 | # for TL-UL assertions |
| 98 | dut.deps += ["lowrisc:ip:tlul"] |
| 99 | # in this case the parent directory is |
| 100 | # likely the correct basename of the IP |
| 101 | dut.deps += ["lowrisc:ip:" + parentpath.stem] |
| 102 | |
| 103 | # define template files to iterate over |
Cindy Chen | 9fdd8ed | 2021-10-13 13:41:40 -0700 | [diff] [blame] | 104 | template_files = [(Path(__file__).parent.joinpath("fpvgen/tb.sv.tpl"), \ |
| 105 | outpath.joinpath("tb").joinpath(mod_path.stem + "_tb.sv")), \ |
Michael Schaffner | b5a88f2 | 2019-11-26 19:43:37 -0800 | [diff] [blame] | 106 | (Path(__file__).parent.joinpath("fpvgen/bind_fpv.sv.tpl"), \ |
| 107 | outpath.joinpath("tb").joinpath(mod_path.stem + "_bind_fpv.sv")), \ |
| 108 | (Path(__file__).parent.joinpath("fpvgen/assert_fpv.sv.tpl"), \ |
| 109 | outpath.joinpath("vip").joinpath(mod_path.stem + "_assert_fpv.sv")), \ |
| 110 | (Path(__file__).parent.joinpath("fpvgen/fusesoc.core.tpl"), \ |
| 111 | outpath.joinpath(mod_path.stem + "_fpv.core"))] |
| 112 | |
| 113 | for (tpl_file, out_file) in template_files: |
| 114 | print("Generating %s" % str(out_file)) |
| 115 | out_file.parent.mkdir(parents=True, exist_ok=True) |
| 116 | tpl = Template(tpl_file.read_text()) |
| 117 | out_file.write_text(tpl.render(dut=dut)) |
| 118 | |
| 119 | return 0 |
| 120 | |
| 121 | |
| 122 | if __name__ == "__main__": |
| 123 | main() |