Coverage for tsfpga/examples/build_fpga_utils.py: 0%
77 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-07 11:31 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-07 11:31 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import argparse
11from pathlib import Path
12from typing import TYPE_CHECKING, Callable, Optional
14# Third party libraries
15from hdl_registers.generator.c.header import CHeaderGenerator
16from hdl_registers.generator.cpp.header import CppHeaderGenerator
17from hdl_registers.generator.cpp.implementation import CppImplementationGenerator
18from hdl_registers.generator.cpp.interface import CppInterfaceGenerator
19from hdl_registers.generator.html.page import HtmlPageGenerator
20from hdl_registers.generator.python.pickle import PythonPickleGenerator
22# First party libraries
23from tsfpga.build_project_list import BuildProjectList
24from tsfpga.system_utils import create_directory
26if TYPE_CHECKING:
27 # First party libraries
28 from tsfpga.module_list import ModuleList
29 from tsfpga.vivado.project import VivadoProject
32def arguments(default_temp_dir: Path) -> argparse.Namespace:
33 """
34 Setup of arguments for the example build flow.
36 Arguments:
37 default_temp_dir (pathlib.Path): Default value for output paths.
38 """
39 parser = argparse.ArgumentParser(
40 "Create, synth and build an FPGA project",
41 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
42 )
44 group = parser.add_mutually_exclusive_group()
46 group.add_argument("--list-only", action="store_true", help="list the available projects")
48 group.add_argument(
49 "--generate-registers-only",
50 action="store_true",
51 help="only generate the register artifacts (C/C++ code, HTML, ...) for inspection",
52 )
54 group.add_argument("--create-only", action="store_true", help="only create projects")
56 group.add_argument("--synth-only", action="store_true", help="only synthesize projects")
58 group.add_argument(
59 "--from-impl",
60 action="store_true",
61 help="run impl and onwards on an existing synthesized projects",
62 )
64 group.add_argument("--open", action="store_true", help="open existing projects in the GUI")
66 group.add_argument(
67 "--collect-artifacts-only",
68 action="store_true",
69 help="collect artifacts of previously successful builds",
70 )
72 parser.add_argument(
73 "--use-existing-project",
74 action="store_true",
75 help="build existing projects, or create first if they do not exist",
76 )
78 parser.add_argument(
79 "--netlist-builds",
80 action="store_true",
81 help="use netlist build projects instead of top level build projects",
82 )
84 parser.add_argument(
85 "--projects-path",
86 type=Path,
87 default=default_temp_dir / "projects",
88 help="the FPGA build projects will be placed here",
89 )
91 parser.add_argument(
92 "--ip-cache-path",
93 type=Path,
94 default=default_temp_dir / "vivado_ip_cache",
95 help="location of Vivado IP cache",
96 )
98 parser.add_argument(
99 "--output-path",
100 type=Path,
101 required=False,
102 help="the output products (bit file, ...) will be placed here",
103 )
105 parser.add_argument(
106 "--num-parallel-builds", type=int, default=8, help="Number of parallel builds to launch"
107 )
109 parser.add_argument(
110 "--num-threads-per-build",
111 type=int,
112 default=4,
113 help="number of threads for each build process",
114 )
116 parser.add_argument("--no-color", action="store_true", help="disable color in printouts")
118 parser.add_argument(
119 "project_filters",
120 nargs="*",
121 help="filter for which projects to build. Can use wildcards. Leave empty for all.",
122 )
124 args = parser.parse_args()
126 assert (
127 args.use_existing_project or not args.from_impl
128 ), "Must set --use-existing-project when using --from-impl"
130 return args
133def setup_and_run( # pylint: disable=too-many-return-statements
134 modules: "ModuleList",
135 projects: BuildProjectList,
136 args: argparse.Namespace,
137 collect_artifacts_function: Optional[Callable[["VivadoProject", Path], bool]],
138) -> int:
139 """
140 Setup and execute build projects.
141 As instructed by the arguments.
143 Arguments:
144 modules: When running a register generation, registers from these
145 modules will be included.
146 projects: These build projects will be built.
147 args: Command line argument namespace.
148 collect_artifacts_function: Function pointer to a function that collects build artifacts.
149 Will be run after a successful implementation build.
150 The function must return ``True`` if successful and ``False`` otherwise.
151 It will receive the ``project`` and ``output_path`` as arguments.
152 Can be ``None`` if no special artifact collection operation shall be run.
154 Return:
155 0 if everything passed, otherwise non-zero.
156 Can be used for system exit code.
157 """
158 if args.list_only:
159 print(projects)
160 return 0
162 if args.generate_registers_only:
163 # Generate register output from all modules. Note that this is not used by the
164 # build flow or simulation flow, it is only for the user to inspect the artifacts.
165 generate_register_artifacts(
166 modules=modules, output_path=args.projects_path.parent / "registers"
167 )
168 return 0
170 if args.open:
171 projects.open(projects_path=args.projects_path)
172 return 0
174 if args.collect_artifacts_only:
175 # We have to assume that the project exists if the user sent this argument.
176 # The 'collect_artifacts_function' call below will probably fail if it does not.
177 create_ok = True
179 elif args.use_existing_project:
180 create_ok = projects.create_unless_exists(
181 projects_path=args.projects_path,
182 num_parallel_builds=args.num_parallel_builds,
183 ip_cache_path=args.ip_cache_path,
184 )
186 else:
187 create_ok = projects.create(
188 projects_path=args.projects_path,
189 num_parallel_builds=args.num_parallel_builds,
190 ip_cache_path=args.ip_cache_path,
191 )
193 if not create_ok:
194 return 1
196 if args.create_only:
197 return 0
199 # If doing only synthesis, there are no artifacts to collect.
200 collect_artifacts_function = (
201 None if (args.synth_only or args.netlist_builds) else collect_artifacts_function
202 )
204 if args.collect_artifacts_only:
205 assert collect_artifacts_function is not None, "No artifact collection available"
207 for project in projects.projects:
208 # Assign the arguments in the exact same way as the call to 'projects.build()' below.
209 # Ensures that the correct output path is used in all scenarios.
210 project_build_output_path = projects.get_build_project_output_path(
211 project=project, projects_path=args.projects_path, output_path=args.output_path
212 )
213 # Collect artifacts function must return True.
214 assert collect_artifacts_function(project, project_build_output_path)
216 return 0
218 build_ok = projects.build(
219 projects_path=args.projects_path,
220 num_parallel_builds=args.num_parallel_builds,
221 num_threads_per_build=args.num_threads_per_build,
222 output_path=args.output_path,
223 collect_artifacts=collect_artifacts_function,
224 synth_only=args.synth_only,
225 from_impl=args.from_impl,
226 )
228 if build_ok:
229 return 0
231 return 1
234def generate_register_artifacts(modules: "ModuleList", output_path: Path) -> None:
235 """
236 Generate register artifacts from the given modules.
238 Arguments:
239 modules: Registers from these modules will be included.
240 output_path: Register artifacts will be placed here.
241 """
242 print(f"Generating register artifacts in {output_path.resolve()}...")
244 # Empty the output directory so we don't have leftover old artifacts.
245 create_directory(directory=output_path, empty=True)
247 for module in modules:
248 if module.registers is not None:
249 CHeaderGenerator(module.registers, output_path / "c").create()
251 CppInterfaceGenerator(module.registers, output_path / "cpp" / "include").create()
252 CppHeaderGenerator(module.registers, output_path / "cpp" / "include").create()
253 CppImplementationGenerator(module.registers, output_path / "cpp").create()
255 HtmlPageGenerator(module.registers, output_path / "html").create()
257 PythonPickleGenerator(module.registers, output_path / "python").create()