Coverage for tsfpga/examples/build_fpga_utils.py: 0%
79 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-07 20:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-07 20:51 +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.accessor import PythonAccessorGenerator
21from hdl_registers.generator.python.pickle import PythonPickleGenerator
23# First party libraries
24from tsfpga.build_project_list import BuildProjectList
25from tsfpga.system_utils import create_directory
27if TYPE_CHECKING:
28 # First party libraries
29 from tsfpga.module_list import ModuleList
30 from tsfpga.vivado.project import VivadoProject
33def arguments(default_temp_dir: Path) -> argparse.Namespace:
34 """
35 Setup of arguments for the example build flow.
37 Arguments:
38 default_temp_dir: Default value for output paths.
39 """
40 parser = argparse.ArgumentParser(
41 "Create, synth and build an FPGA project",
42 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
43 )
45 group = parser.add_mutually_exclusive_group()
47 group.add_argument("--list-only", action="store_true", help="list the available projects")
49 group.add_argument(
50 "--generate-registers-only",
51 action="store_true",
52 help="only generate the register artifacts (C/C++ code, HTML, ...) for inspection",
53 )
55 group.add_argument("--create-only", action="store_true", help="only create projects")
57 group.add_argument("--synth-only", action="store_true", help="only synthesize projects")
59 group.add_argument(
60 "--from-impl",
61 action="store_true",
62 help="run impl and onwards on an existing synthesized projects",
63 )
65 group.add_argument("--open", action="store_true", help="open existing projects in the GUI")
67 group.add_argument(
68 "--collect-artifacts-only",
69 action="store_true",
70 help="collect artifacts of previously successful builds",
71 )
73 parser.add_argument(
74 "--use-existing-project",
75 action="store_true",
76 help="build existing projects, or create first if they do not exist",
77 )
79 parser.add_argument(
80 "--netlist-builds",
81 action="store_true",
82 help="use netlist build projects instead of top level build projects",
83 )
85 parser.add_argument(
86 "--projects-path",
87 type=Path,
88 default=default_temp_dir / "projects",
89 help="the FPGA build projects will be placed here",
90 )
92 parser.add_argument(
93 "--ip-cache-path",
94 type=Path,
95 default=default_temp_dir / "vivado_ip_cache",
96 help="location of Vivado IP cache",
97 )
99 parser.add_argument(
100 "--output-path",
101 type=Path,
102 required=False,
103 help="the output products (bit file, ...) will be placed here",
104 )
106 parser.add_argument(
107 "--num-parallel-builds", type=int, default=8, help="Number of parallel builds to launch"
108 )
110 parser.add_argument(
111 "--num-threads-per-build",
112 type=int,
113 default=4,
114 help="number of threads for each build process",
115 )
117 parser.add_argument("--no-color", action="store_true", help="disable color in printouts")
119 parser.add_argument(
120 "project_filters",
121 nargs="*",
122 help="filter for which projects to build. Can use wildcards. Leave empty for all.",
123 )
125 args = parser.parse_args()
127 assert (
128 args.use_existing_project or not args.from_impl
129 ), "Must set --use-existing-project when using --from-impl"
131 return args
134def setup_and_run( # pylint: disable=too-many-return-statements
135 modules: "ModuleList",
136 projects: BuildProjectList,
137 args: argparse.Namespace,
138 collect_artifacts_function: Optional[Callable[["VivadoProject", Path], bool]],
139) -> int:
140 """
141 Setup and execute build projects.
142 As instructed by the arguments.
144 Arguments:
145 modules: When running a register generation, registers from these
146 modules will be included.
147 projects: These build projects will be built.
148 args: Command line argument namespace.
149 collect_artifacts_function: Function pointer to a function that collects build artifacts.
150 Will be run after a successful implementation build.
151 The function must return ``True`` if successful and ``False`` otherwise.
152 It will receive the ``project`` and ``output_path`` as arguments.
153 Can be ``None`` if no special artifact collection operation shall be run.
155 Return:
156 0 if everything passed, otherwise non-zero.
157 Can be used for system exit code.
158 """
159 if args.list_only:
160 print(projects)
161 return 0
163 if args.generate_registers_only:
164 # Generate register output from all modules. Note that this is not used by the
165 # build flow or simulation flow, it is only for the user to inspect the artifacts.
166 generate_register_artifacts(
167 modules=modules, output_path=args.projects_path.parent / "registers"
168 )
169 return 0
171 if args.open:
172 projects.open(projects_path=args.projects_path)
173 return 0
175 if args.collect_artifacts_only:
176 # We have to assume that the project exists if the user sent this argument.
177 # The 'collect_artifacts_function' call below will probably fail if it does not.
178 create_ok = True
180 elif args.use_existing_project:
181 create_ok = projects.create_unless_exists(
182 projects_path=args.projects_path,
183 num_parallel_builds=args.num_parallel_builds,
184 ip_cache_path=args.ip_cache_path,
185 )
187 else:
188 create_ok = projects.create(
189 projects_path=args.projects_path,
190 num_parallel_builds=args.num_parallel_builds,
191 ip_cache_path=args.ip_cache_path,
192 )
194 if not create_ok:
195 return 1
197 if args.create_only:
198 return 0
200 # If doing only synthesis, there are no artifacts to collect.
201 collect_artifacts_function = (
202 None if (args.synth_only or args.netlist_builds) else collect_artifacts_function
203 )
205 if args.collect_artifacts_only:
206 assert collect_artifacts_function is not None, "No artifact collection available"
208 for project in projects.projects:
209 # Assign the arguments in the exact same way as the call to 'projects.build()' below.
210 # Ensures that the correct output path is used in all scenarios.
211 project_build_output_path = projects.get_build_project_output_path(
212 project=project, projects_path=args.projects_path, output_path=args.output_path
213 )
214 # Collect artifacts function must return True.
215 assert collect_artifacts_function(project, project_build_output_path)
217 return 0
219 build_ok = projects.build(
220 projects_path=args.projects_path,
221 num_parallel_builds=args.num_parallel_builds,
222 num_threads_per_build=args.num_threads_per_build,
223 output_path=args.output_path,
224 collect_artifacts=collect_artifacts_function,
225 synth_only=args.synth_only,
226 from_impl=args.from_impl,
227 )
229 if build_ok:
230 return 0
232 return 1
235def generate_register_artifacts(modules: "ModuleList", output_path: Path) -> None:
236 """
237 Generate register artifacts from the given modules.
239 Arguments:
240 modules: Registers from these modules will be included.
241 output_path: Register artifacts will be placed here.
242 """
243 print(f"Generating register artifacts in {output_path.resolve()}...")
245 # Empty the output directory so we don't have leftover old artifacts.
246 create_directory(directory=output_path, empty=True)
248 for module in modules:
249 if module.registers is not None:
250 CHeaderGenerator(module.registers, output_path / "c").create()
252 CppInterfaceGenerator(module.registers, output_path / "cpp" / "include").create()
253 CppHeaderGenerator(module.registers, output_path / "cpp" / "include").create()
254 CppImplementationGenerator(module.registers, output_path / "cpp").create()
256 HtmlPageGenerator(module.registers, output_path / "html").create()
258 PythonPickleGenerator(module.registers, output_path / "python").create()
259 PythonAccessorGenerator(module.registers, output_path / "python").create()