Coverage for tsfpga/examples/build.py: 0%
86 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-28 04:01 +0000
« prev ^ index » next coverage.py v6.4, created at 2022-05-28 04:01 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project.
5# https://tsfpga.com
6# https://gitlab.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9import argparse
10from pathlib import Path
11from shutil import copy2, make_archive
12import sys
14# Do PYTHONPATH insert() instead of append() to prefer any local repo checkout over any pip install
15PATH_TO_TSFPGA = Path(__file__).parent.parent.parent.resolve()
16sys.path.insert(0, str(PATH_TO_TSFPGA))
18from tsfpga.examples.example_env import get_tsfpga_example_modules, TSFPGA_EXAMPLES_TEMP_DIR
20from tsfpga.build_project_list import BuildProjectList
21from tsfpga.system_utils import create_directory, delete
24def arguments(default_temp_dir=TSFPGA_EXAMPLES_TEMP_DIR):
25 """
26 Setup of arguments for the example build flow.
28 Arguments:
29 default_temp_dir (pathlib.Path): Default value for output paths.
30 """
31 parser = argparse.ArgumentParser(
32 "Create, synth and build an FPGA project",
33 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
34 )
36 group = parser.add_mutually_exclusive_group()
38 group.add_argument("--list-only", action="store_true", help="list the available projects")
40 group.add_argument(
41 "--generate-registers-only",
42 action="store_true",
43 help="only generate the register artifacts (C/C++ code, HTML, ...) for inspection",
44 )
46 group.add_argument("--create-only", action="store_true", help="only create projects")
48 group.add_argument("--synth-only", action="store_true", help="only synthesize projects")
50 group.add_argument(
51 "--from-impl",
52 action="store_true",
53 help="run impl and onwards on an existing synthesized projects",
54 )
56 group.add_argument("--open", action="store_true", help="open existing projects in the GUI")
58 parser.add_argument(
59 "--use-existing-project",
60 action="store_true",
61 help="build existing projects, or create first if they do not exist",
62 )
64 parser.add_argument(
65 "--netlist-builds",
66 action="store_true",
67 help="use netlist build projects instead of top level build projects",
68 )
70 parser.add_argument(
71 "--projects-path",
72 type=Path,
73 default=default_temp_dir / "projects",
74 help="the FPGA build projects will be placed here",
75 )
77 parser.add_argument(
78 "--ip-cache-path",
79 type=Path,
80 default=default_temp_dir / "vivado_ip_cache",
81 help="location of Vivado IP cache",
82 )
84 parser.add_argument(
85 "--output-path",
86 type=Path,
87 required=False,
88 help="the output products (bit file, ...) will be placed here",
89 )
91 parser.add_argument(
92 "--num-parallel-builds", type=int, default=8, help="Number of parallel builds to launch"
93 )
95 parser.add_argument(
96 "--num-threads-per-build",
97 type=int,
98 default=4,
99 help="number of threads for each build process",
100 )
102 parser.add_argument("--no-color", action="store_true", help="disable color in printouts")
104 parser.add_argument(
105 "project_filters",
106 nargs="*",
107 help="filter for which projects to build. Can use wildcards. Leave empty for all.",
108 )
110 args = parser.parse_args()
112 assert (
113 args.use_existing_project or not args.from_impl
114 ), "Must set --use-existing-project when using --from-impl"
116 return args
119def main():
120 """
121 Main function for building FPGA projects. If you are setting up a new build flow from scratch,
122 you probably want to copy and modify this function, and reuse the others.
123 """
124 args = arguments()
125 modules = get_tsfpga_example_modules()
126 projects = BuildProjectList(
127 modules=modules,
128 project_filters=args.project_filters,
129 include_netlist_not_top_builds=args.netlist_builds,
130 no_color=args.no_color,
131 )
133 sys.exit(setup_and_run(modules, projects, args))
136def setup_and_run(modules, projects, args):
137 """
138 Setup build projects, and execute as instructed by the arguments.
140 Arguments:
141 modules (:class:`.ModuleList`): When running a register generation, registers from these
142 modules will be included.
143 projects (:class:`.BuildProjectList`): These build projects will be built.
144 args: Command line argument namespace.
146 Return:
147 int: 0 if everything passed, otherwise non-zero. Can be used for system exit code.
148 """
149 if args.list_only:
150 print(projects)
151 return 0
153 if args.generate_registers_only:
154 # Generate register output from all modules. Note that this is not used by the
155 # build flow or simulation flow, it is only for the user to inspect the artifacts.
156 generate_registers(modules=modules, output_path=args.projects_path.parent / "registers")
157 return 0
159 if args.open:
160 projects.open(projects_path=args.projects_path)
161 return 0
163 if args.use_existing_project:
164 create_ok = projects.create_unless_exists(
165 projects_path=args.projects_path,
166 num_parallel_builds=args.num_parallel_builds,
167 ip_cache_path=args.ip_cache_path,
168 )
169 else:
170 create_ok = projects.create(
171 projects_path=args.projects_path,
172 num_parallel_builds=args.num_parallel_builds,
173 ip_cache_path=args.ip_cache_path,
174 )
176 if not create_ok:
177 return 1
179 if args.create_only:
180 return 0
182 # If doing only synthesis there are no artifacts to collect
183 collect_artifacts_function = (
184 None if (args.synth_only or args.netlist_builds) else collect_artifacts
185 )
187 build_ok = projects.build(
188 projects_path=args.projects_path,
189 collect_artifacts=collect_artifacts_function,
190 num_parallel_builds=args.num_parallel_builds,
191 output_path=args.output_path,
192 synth_only=args.synth_only,
193 from_impl=args.from_impl,
194 num_threads_per_build=args.num_threads_per_build,
195 )
197 if build_ok:
198 return 0
199 return 1
202def collect_artifacts(project, output_path):
203 """
204 Example of a method to collect build artifacts. Will create a zip file with the bitstream,
205 hardware definition (.xsa) and register documentation.
207 Arguments:
208 project (:class:`.VivadoProject`): Project object that has been built,
209 and who's artifacts shall now be collected.
210 output_path (pathlib.Path): Path to the build output. Artifact zip will be placed here
211 as well.
212 """
213 version = "0.0.0.0"
214 release_dir = create_directory(output_path / f"{project.name}-{version}", empty=True)
215 print(f"Creating release in {release_dir.resolve()}.zip")
217 generate_registers(project.modules, release_dir / "registers")
218 copy2(output_path / f"{project.name }.bit", release_dir)
219 copy2(output_path / f"{project.name}.bin", release_dir)
220 if (output_path / f"{project.name}.xsa").exists():
221 copy2(output_path / f"{project.name}.xsa", release_dir)
223 make_archive(release_dir, "zip", release_dir)
225 # Remove folder so that only zip remains
226 delete(release_dir)
228 return True
231def generate_registers(modules, output_path):
232 """
233 Generate all register artifacts from the given modules.
235 Arguments:
236 modules (:class:`.ModuleList`): Registers from these modules will be included.
237 output_path (pathlib.Path): Register artifacts will be placed here.
238 """
239 print(f"Generating registers in {output_path.resolve()}")
241 for module in modules:
242 if module.registers is not None:
243 vhdl_path = create_directory(output_path / "vhdl", empty=False)
244 module.registers.create_vhdl_package(vhdl_path)
246 module.registers.copy_source_definition(output_path / "toml")
248 module.registers.create_c_header(output_path / "c")
249 module.registers.create_cpp_interface(output_path / "cpp" / "include")
250 module.registers.create_cpp_header(output_path / "cpp" / "include")
251 module.registers.create_cpp_implementation(output_path / "cpp")
252 module.registers.create_html_page(output_path / "html")
253 module.registers.create_html_register_table(output_path / "html" / "tables")
254 module.registers.create_html_constant_table(output_path / "html" / "tables")
255 module.registers.create_python_class(output_path / "python")
258if __name__ == "__main__":
259 main()