blob: 122a2c61ddfe856a7dc685d88e5fc7b0ee139c5b [file] [log] [blame]
Michael Schaffnerb5a88f22019-11-26 19:43:37 -08001#!/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
5r"""Generates boilerplate code for the FPV testbench setup.
6"""
7import argparse
8import sys
9from io import StringIO
10from pathlib import Path
11
12from mako.template import Template
13
14import fpvgen.sv_parse as sv_parse
15
16
17def 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 Schaffner1acfee12022-02-01 06:42:57 +000046 type=Path,
Michael Schaffnerb5a88f22019-11-26 19:43:37 -080047 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 Schaffner1acfee12022-02-01 06:42:57 +000055 type=Path,
Michael Schaffnerb5a88f22019-11-26 19:43:37 -080056 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 Schaffner1acfee12022-02-01 06:42:57 +000075 mod_path = args.file
Michael Schaffnerb5a88f22019-11-26 19:43:37 -080076 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 Chen9fdd8ed2021-10-13 13:41:40 -0700104 template_files = [(Path(__file__).parent.joinpath("fpvgen/tb.sv.tpl"), \
105 outpath.joinpath("tb").joinpath(mod_path.stem + "_tb.sv")), \
Michael Schaffnerb5a88f22019-11-26 19:43:37 -0800106 (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
122if __name__ == "__main__":
123 main()