Coverage for tsfpga/examples/build.py: 0%

86 statements  

« 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# -------------------------------------------------------------------------------------------------- 

8 

9import argparse 

10from pathlib import Path 

11from shutil import copy2, make_archive 

12import sys 

13 

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)) 

17 

18from tsfpga.examples.example_env import get_tsfpga_example_modules, TSFPGA_EXAMPLES_TEMP_DIR 

19 

20from tsfpga.build_project_list import BuildProjectList 

21from tsfpga.system_utils import create_directory, delete 

22 

23 

24def arguments(default_temp_dir=TSFPGA_EXAMPLES_TEMP_DIR): 

25 """ 

26 Setup of arguments for the example build flow. 

27 

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 ) 

35 

36 group = parser.add_mutually_exclusive_group() 

37 

38 group.add_argument("--list-only", action="store_true", help="list the available projects") 

39 

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 ) 

45 

46 group.add_argument("--create-only", action="store_true", help="only create projects") 

47 

48 group.add_argument("--synth-only", action="store_true", help="only synthesize projects") 

49 

50 group.add_argument( 

51 "--from-impl", 

52 action="store_true", 

53 help="run impl and onwards on an existing synthesized projects", 

54 ) 

55 

56 group.add_argument("--open", action="store_true", help="open existing projects in the GUI") 

57 

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 ) 

63 

64 parser.add_argument( 

65 "--netlist-builds", 

66 action="store_true", 

67 help="use netlist build projects instead of top level build projects", 

68 ) 

69 

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 ) 

76 

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 ) 

83 

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 ) 

90 

91 parser.add_argument( 

92 "--num-parallel-builds", type=int, default=8, help="Number of parallel builds to launch" 

93 ) 

94 

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 ) 

101 

102 parser.add_argument("--no-color", action="store_true", help="disable color in printouts") 

103 

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 ) 

109 

110 args = parser.parse_args() 

111 

112 assert ( 

113 args.use_existing_project or not args.from_impl 

114 ), "Must set --use-existing-project when using --from-impl" 

115 

116 return args 

117 

118 

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 ) 

132 

133 sys.exit(setup_and_run(modules, projects, args)) 

134 

135 

136def setup_and_run(modules, projects, args): 

137 """ 

138 Setup build projects, and execute as instructed by the arguments. 

139 

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. 

145 

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 

152 

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 

158 

159 if args.open: 

160 projects.open(projects_path=args.projects_path) 

161 return 0 

162 

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 ) 

175 

176 if not create_ok: 

177 return 1 

178 

179 if args.create_only: 

180 return 0 

181 

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 ) 

186 

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 ) 

196 

197 if build_ok: 

198 return 0 

199 return 1 

200 

201 

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. 

206 

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") 

216 

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) 

222 

223 make_archive(release_dir, "zip", release_dir) 

224 

225 # Remove folder so that only zip remains 

226 delete(release_dir) 

227 

228 return True 

229 

230 

231def generate_registers(modules, output_path): 

232 """ 

233 Generate all register artifacts from the given modules. 

234 

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()}") 

240 

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) 

245 

246 module.registers.copy_source_definition(output_path / "toml") 

247 

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") 

256 

257 

258if __name__ == "__main__": 

259 main()