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

82 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-31 20:01 +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# -------------------------------------------------------------------------------------------------- 

8 

9# Standard libraries 

10import argparse 

11from pathlib import Path 

12from shutil import which 

13 

14# Third party libraries 

15from vunit import VUnit, VUnitCLI 

16from vunit.vivado.vivado import add_from_compile_order_file, create_compile_order_file 

17 

18# First party libraries 

19import tsfpga 

20import tsfpga.create_vhdl_ls_config 

21from tsfpga.module import ModuleList 

22from tsfpga.vivado.ip_cores import VivadoIpCores 

23from tsfpga.vivado.simlib import VivadoSimlib 

24 

25 

26def get_arguments_cli(default_output_path): 

27 """ 

28 Get arguments for the simulation flow. 

29 

30 Arguments: 

31 default_output_path (pathlib.Path): Will be set as default for output path arguments 

32 (both VUnit files and Vivado files). 

33 """ 

34 cli = VUnitCLI() 

35 

36 # Print default values when doing --help 

37 cli.parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter 

38 

39 # Set the supplied default value for VUnit output path. pylint: disable=protected-access 

40 for action in cli.parser._actions: 

41 if action.dest == "output_path": 

42 action.default = default_output_path / "vunit_out" 

43 break 

44 else: 

45 raise AssertionError("VUnit --output-path argument not found") 

46 

47 cli.parser.add_argument( 

48 "--output-path-vivado", 

49 type=Path, 

50 default=default_output_path, 

51 help=( 

52 "where to place Vivado IP core and simlib files. " 

53 "Note that --output-path is for VUnit files" 

54 ), 

55 ) 

56 

57 cli.parser.add_argument( 

58 "--vivado-skip", action="store_true", help="skip all steps that require Vivado" 

59 ) 

60 

61 cli.parser.add_argument( 

62 "--ip-compile", action="store_true", help="force (re)compile of IP cores" 

63 ) 

64 

65 cli.parser.add_argument( 

66 "--simlib-compile", action="store_true", help="force (re)compile of Vivado simlib" 

67 ) 

68 

69 cli.parser.add_argument( 

70 "--vcs-minimal", 

71 action="store_true", 

72 help="compile and run only a minimal set of tests based on Version Control System history", 

73 ) 

74 

75 return cli 

76 

77 

78class SimulationProject: 

79 """ 

80 Class for setting up and handling a VUnit simulation project. Should be reusable in most cases. 

81 """ 

82 

83 def __init__(self, args, enable_preprocessing=False): 

84 """ 

85 Create a VUnit project, configured according to the given arguments. 

86 

87 Arguments: 

88 enable_preprocessing (bool): If ``True``, VUnit location/check preprocessing will 

89 be enabled. 

90 """ 

91 self.vunit_proj = VUnit.from_args(args=args) 

92 self.vunit_proj.add_verification_components() 

93 self.vunit_proj.add_random() 

94 

95 if enable_preprocessing: 

96 self.vunit_proj.enable_location_preprocessing() 

97 self.vunit_proj.enable_check_preprocessing() 

98 

99 self.has_commercial_simulator = self.vunit_proj.get_simulator_name() != "ghdl" 

100 

101 def add_modules(self, args, modules, modules_no_sim=None, **setup_vunit_kwargs): 

102 """ 

103 Add module source files to the VUnit project. 

104 

105 Arguments: 

106 args: Command line argument namespace. 

107 modules (:class:`.ModuleList`): These modules will be included in the 

108 simulation project. 

109 modules_no_sim (:class:`.ModuleList`): These modules will be included in the simulation 

110 project, but their test files will not be added. 

111 setup_vunit_kwargs: Further arguments that will be sent to 

112 :meth:`.BaseModule.setup_vunit` for each module. Note that this is a "kwargs" style 

113 argument; any number of named arguments can be sent. 

114 """ 

115 modules_no_sim = ModuleList() if modules_no_sim is None else modules_no_sim 

116 

117 include_unisim = not args.vivado_skip 

118 include_ip_cores = self.has_commercial_simulator and not args.vivado_skip 

119 

120 for module in modules + modules_no_sim: 

121 vunit_library = self.vunit_proj.add_library( 

122 library_name=module.library_name, allow_duplicate=True 

123 ) 

124 simulate_this_module = module not in modules_no_sim 

125 

126 for hdl_file in module.get_simulation_files( 

127 include_tests=simulate_this_module, 

128 include_unisim=include_unisim, 

129 include_ip_cores=include_ip_cores, 

130 ): 

131 if hdl_file.is_vhdl or hdl_file.is_verilog_source: 

132 vunit_library.add_source_file(hdl_file.path) 

133 else: 

134 assert False, f"Can not handle this file: {hdl_file}" 

135 

136 if simulate_this_module: 

137 module.setup_vunit( 

138 vunit_proj=self.vunit_proj, 

139 include_unisim=include_unisim, 

140 include_ip_cores=include_ip_cores, 

141 **setup_vunit_kwargs, 

142 ) 

143 

144 def add_vivado_simlib(self, args): 

145 """ 

146 Add Vivado simlib to the VUnit project, unless instructed not to by ``args``. 

147 Will compile simlib if necessary. 

148 

149 Arguments: 

150 args: Command line argument namespace from ``simulate.py``. 

151 

152 Return: 

153 :class:`.VivadoSimlibCommon`: The simlib object. 

154 """ 

155 if args.vivado_skip: 

156 return None 

157 

158 return self._add_simlib( 

159 output_path=args.output_path_vivado, force_compile=args.simlib_compile 

160 ) 

161 

162 def _add_simlib(self, output_path, force_compile): 

163 """ 

164 Add Vivado simlib to the VUnit project. Compile if needed. 

165 

166 .. note:: 

167 

168 This method can be overloaded in a child class if you want to do something more 

169 advanced, e.g. fetching compiled simlib from Artifactory. 

170 

171 Arguments: 

172 output_path (pathlib.Path): Compiled simlib will be placed in sub-directory of 

173 this path. 

174 force_compile (bool): Will (re)-compile simlib even if compiled artifacts exist. 

175 

176 Return: 

177 VivadoSimlibCommon: The simlib object. 

178 """ 

179 vivado_simlib = VivadoSimlib.init(output_path, self.vunit_proj) 

180 if force_compile or vivado_simlib.compile_is_needed: 

181 vivado_simlib.compile() 

182 vivado_simlib.to_archive() 

183 

184 vivado_simlib.add_to_vunit_project() 

185 

186 # Code in the "vital2000" package gives GHDL errors such as "result subtype of a pure 

187 # function cannot have access subelements". Hence, relaxed rules need to be enabled when 

188 # using unisim. 

189 self.vunit_proj.set_sim_option("ghdl.elab_flags", ["-frelaxed-rules"]) 

190 

191 return vivado_simlib 

192 

193 def add_vivado_ip_cores( 

194 self, 

195 args, 

196 modules, 

197 vivado_part_name="xc7z020clg400-1", 

198 vivado_ip_core_project_class=None, 

199 ): 

200 """ 

201 Generate IP cores from the modules, unless instructed not to by ``args``. 

202 When running with a commercial simulator they will be added to the VUnit project. 

203 

204 Arguments: 

205 args: Command line argument namespace from ``simulate.py``. 

206 modules (:class:`.ModuleList`): IP cores from these modules will be included in the 

207 simulation project. 

208 vivado_part_name (str): Part name to be used for Vivado IP core project. Might have to 

209 change from default depending on what parts you have available in your 

210 Vivado installation. 

211 vivado_ip_core_project_class: Class to be used for Vivado IP core project. Can be left 

212 at default in most cases. 

213 

214 Return: 

215 pathlib.Path: Path to the Vivado IP core project's ``project`` directory. 

216 """ 

217 if args.vivado_skip: 

218 return None 

219 

220 # Generate IP core simulation files. Might be used for the vhdl_ls config, 

221 # even if they are not added to the simulation project. 

222 ( 

223 ip_core_compile_order_file, 

224 ip_core_vivado_project_directory, 

225 ) = self._generate_ip_core_files( 

226 modules=modules, 

227 output_path=args.output_path_vivado, 

228 force_generate=args.ip_compile, 

229 part_name=vivado_part_name, 

230 vivado_project_class=vivado_ip_core_project_class, 

231 ) 

232 if self.has_commercial_simulator: 

233 add_from_compile_order_file( 

234 vunit_obj=self.vunit_proj, compile_order_file=ip_core_compile_order_file 

235 ) 

236 

237 return ip_core_vivado_project_directory 

238 

239 @staticmethod 

240 def _generate_ip_core_files( 

241 modules, output_path, force_generate, part_name, vivado_project_class=None 

242 ): 

243 """ 

244 Generate Vivado IP core files that are to be added to the VUnit project. 

245 Create a new project to generate files if needed. 

246 

247 Arguments: 

248 modules (:class:`.ModuleList`): IP cores from these modules will be included. 

249 output_path (pathlib.Path): IP core files will be placed in sub-directory of this path. 

250 force_generate (bool): Will (re)-generate files even if they exist. 

251 part_name (str): Vivado part name. 

252 vivado_project_class: Class to be used for Vivado IP core project. 

253 """ 

254 vivado_ip_cores = VivadoIpCores( 

255 modules=modules, 

256 output_path=output_path, 

257 part_name=part_name, 

258 vivado_project_class=vivado_project_class, 

259 ) 

260 

261 if force_generate: 

262 vivado_ip_cores.create_vivado_project() 

263 vivado_project_created = True 

264 else: 

265 vivado_project_created = vivado_ip_cores.create_vivado_project_if_needed() 

266 

267 if vivado_project_created: 

268 # If the IP core Vivado project has been (re)created we need to create 

269 # a new compile order file 

270 create_compile_order_file( 

271 vivado_ip_cores.vivado_project_file, vivado_ip_cores.compile_order_file 

272 ) 

273 

274 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory 

275 

276 

277def create_vhdl_ls_configuration( 

278 output_path, temp_files_path, modules, ip_core_vivado_project_directory=None 

279): 

280 """ 

281 Create config for vhdl_ls (https://github.com/VHDL-LS/rust_hdl). 

282 Granted this might no be the "correct" place for this functionality. 

283 But since the call is somewhat quick (~10 ms), and simulate.py is run "often" it seems an 

284 appropriate place in order to always have an up-to-date vhdl_ls config. 

285 

286 Arguments: 

287 output_path (pathlib.Path): Config file will be placed here. 

288 temp_files_path (pathlib.Path): Some temporary files will be placed here. 

289 modules (:class:`.ModuleList`): These modules will be added. 

290 ip_core_vivado_project_directory (pathlib.Path): Vivado IP core files in this location 

291 will be added. 

292 """ 

293 # Create an empty VUnit project to add files from VUnit and OSVVM library. 

294 # If we were to use the "real" VUnit project that we set up above instead, all the files would 

295 # be in the "preprocessed" folder. Hence we use an "empty" VUnit project, and add all files 

296 # via modules. 

297 vunit_proj = VUnit.from_argv(argv=["--output-path", str(temp_files_path / "vhdl_ls_vunit_out")]) 

298 vunit_proj.add_verification_components() 

299 vunit_proj.add_random() 

300 vunit_proj.add_osvvm() 

301 

302 vivado_location = None if which("vivado") is None else Path(which("vivado")) 

303 tsfpga.create_vhdl_ls_config.create_configuration( 

304 output_path=output_path, 

305 modules=modules, 

306 vunit_proj=vunit_proj, 

307 vivado_location=vivado_location, 

308 ip_core_vivado_project_directory=ip_core_vivado_project_directory, 

309 )