Coverage for tsfpga/examples/build.py: 0%
87 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-05-31 20:00 +0000
« prev ^ index » next coverage.py v7.2.1, created at 2023-05-31 20:00 +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://gitlab.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import argparse
11import sys
12from pathlib import Path
13from shutil import copy2, make_archive
15# Do PYTHONPATH insert() instead of append() to prefer any local repo checkout over any pip install
16REPO_ROOT = Path(__file__).parent.parent.parent.resolve()
17sys.path.insert(0, str(REPO_ROOT))
19# Import before others since it modifies PYTHONPATH. pylint: disable=unused-import
20import tsfpga.examples.example_pythonpath # noqa: F401
22# First party libraries
23from tsfpga.build_project_list import BuildProjectList
24from tsfpga.examples.example_env import TSFPGA_EXAMPLES_TEMP_DIR, get_tsfpga_example_modules
25from tsfpga.system_utils import create_directory, delete
28def arguments(default_temp_dir=TSFPGA_EXAMPLES_TEMP_DIR):
29 """
30 Setup of arguments for the example build flow.
32 Arguments:
33 default_temp_dir (pathlib.Path): Default value for output paths.
34 """
35 parser = argparse.ArgumentParser(
36 "Create, synth and build an FPGA project",
37 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
38 )
40 group = parser.add_mutually_exclusive_group()
42 group.add_argument("--list-only", action="store_true", help="list the available projects")
44 group.add_argument(
45 "--generate-registers-only",
46 action="store_true",
47 help="only generate the register artifacts (C/C++ code, HTML, ...) for inspection",
48 )
50 group.add_argument("--create-only", action="store_true", help="only create projects")
52 group.add_argument("--synth-only", action="store_true", help="only synthesize projects")
54 group.add_argument(
55 "--from-impl",
56 action="store_true",
57 help="run impl and onwards on an existing synthesized projects",
58 )
60 group.add_argument("--open", action="store_true", help="open existing projects in the GUI")
62 parser.add_argument(
63 "--use-existing-project",
64 action="store_true",
65 help="build existing projects, or create first if they do not exist",
66 )
68 parser.add_argument(
69 "--netlist-builds",
70 action="store_true",
71 help="use netlist build projects instead of top level build projects",
72 )
74 parser.add_argument(
75 "--projects-path",
76 type=Path,
77 default=default_temp_dir / "projects",
78 help="the FPGA build projects will be placed here",
79 )
81 parser.add_argument(
82 "--ip-cache-path",
83 type=Path,
84 default=default_temp_dir / "vivado_ip_cache",
85 help="location of Vivado IP cache",
86 )
88 parser.add_argument(
89 "--output-path",
90 type=Path,
91 required=False,
92 help="the output products (bit file, ...) will be placed here",
93 )
95 parser.add_argument(
96 "--num-parallel-builds", type=int, default=8, help="Number of parallel builds to launch"
97 )
99 parser.add_argument(
100 "--num-threads-per-build",
101 type=int,
102 default=4,
103 help="number of threads for each build process",
104 )
106 parser.add_argument("--no-color", action="store_true", help="disable color in printouts")
108 parser.add_argument(
109 "project_filters",
110 nargs="*",
111 help="filter for which projects to build. Can use wildcards. Leave empty for all.",
112 )
114 args = parser.parse_args()
116 assert (
117 args.use_existing_project or not args.from_impl
118 ), "Must set --use-existing-project when using --from-impl"
120 return args
123def main():
124 """
125 Main function for building FPGA projects. If you are setting up a new build flow from scratch,
126 you probably want to copy and modify this function, and reuse the others.
127 """
128 args = arguments()
129 modules = get_tsfpga_example_modules()
130 projects = BuildProjectList(
131 modules=modules,
132 project_filters=args.project_filters,
133 include_netlist_not_top_builds=args.netlist_builds,
134 no_color=args.no_color,
135 )
137 sys.exit(setup_and_run(modules, projects, args))
140def collect_artifacts(project, output_path):
141 """
142 Example of a method to collect build artifacts. Will create a zip file with the bitstream,
143 hardware definition (.xsa) and register documentation.
145 Arguments:
146 project (:class:`.VivadoProject`): Project object that has been built,
147 and who's artifacts shall now be collected.
148 output_path (pathlib.Path): Path to the build output. Artifact zip will be placed here
149 as well.
150 """
151 version = "0.0.0"
152 release_dir = create_directory(output_path / f"{project.name}-{version}", empty=True)
153 print(f"Creating release in {release_dir.resolve()}.zip")
155 generate_registers(project.modules, release_dir / "registers")
156 copy2(output_path / f"{project.name }.bit", release_dir)
157 copy2(output_path / f"{project.name}.bin", release_dir)
158 if (output_path / f"{project.name}.xsa").exists():
159 copy2(output_path / f"{project.name}.xsa", release_dir)
161 make_archive(release_dir, "zip", release_dir)
163 # Remove folder so that only zip remains
164 delete(release_dir)
166 return True
169def setup_and_run(modules, projects, args, collect_artifacts_function=collect_artifacts):
170 """
171 Setup build projects, and execute as instructed by the arguments.
173 Arguments:
174 modules (:class:`.ModuleList`): When running a register generation, registers from these
175 modules will be included.
176 projects (:class:`.BuildProjectList`): These build projects will be built.
177 args: Command line argument namespace.
178 collect_artifacts_function: Function pointer to a function that collects build artifacts.
179 Will be run after a successful implementation build.
180 The function must return ``True`` if successful and ``False`` otherwise.
181 It will receive the ``project`` and ``output_path`` as arguments.
183 Return:
184 int: 0 if everything passed, otherwise non-zero. Can be used for system exit code.
185 """
186 if args.list_only:
187 print(projects)
188 return 0
190 if args.generate_registers_only:
191 # Generate register output from all modules. Note that this is not used by the
192 # build flow or simulation flow, it is only for the user to inspect the artifacts.
193 generate_registers(modules=modules, output_path=args.projects_path.parent / "registers")
194 return 0
196 if args.open:
197 projects.open(projects_path=args.projects_path)
198 return 0
200 if args.use_existing_project:
201 create_ok = projects.create_unless_exists(
202 projects_path=args.projects_path,
203 num_parallel_builds=args.num_parallel_builds,
204 ip_cache_path=args.ip_cache_path,
205 )
206 else:
207 create_ok = projects.create(
208 projects_path=args.projects_path,
209 num_parallel_builds=args.num_parallel_builds,
210 ip_cache_path=args.ip_cache_path,
211 )
213 if not create_ok:
214 return 1
216 if args.create_only:
217 return 0
219 # If doing only synthesis there are no artifacts to collect
220 collect_artifacts_function = (
221 None if (args.synth_only or args.netlist_builds) else collect_artifacts_function
222 )
224 build_ok = projects.build(
225 projects_path=args.projects_path,
226 collect_artifacts=collect_artifacts_function,
227 num_parallel_builds=args.num_parallel_builds,
228 output_path=args.output_path,
229 synth_only=args.synth_only,
230 from_impl=args.from_impl,
231 num_threads_per_build=args.num_threads_per_build,
232 )
234 if build_ok:
235 return 0
236 return 1
239def generate_registers(modules, output_path):
240 """
241 Generate all register artifacts from the given modules.
243 Arguments:
244 modules (:class:`.ModuleList`): Registers from these modules will be included.
245 output_path (pathlib.Path): Register artifacts will be placed here.
246 """
247 print(f"Generating registers in {output_path.resolve()}")
249 for module in modules:
250 if module.registers is not None:
251 vhdl_path = create_directory(output_path / "vhdl", empty=False)
252 module.registers.create_vhdl_package(vhdl_path)
254 module.registers.copy_source_definition(output_path / "toml")
256 module.registers.create_c_header(output_path / "c")
257 module.registers.create_cpp_interface(output_path / "cpp" / "include")
258 module.registers.create_cpp_header(output_path / "cpp" / "include")
259 module.registers.create_cpp_implementation(output_path / "cpp")
260 module.registers.create_html_page(output_path / "html")
261 module.registers.create_html_register_table(output_path / "html" / "tables")
262 module.registers.create_html_constant_table(output_path / "html" / "tables")
263 module.registers.create_python_class(output_path / "python")
266if __name__ == "__main__":
267 main()