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

89 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-10 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# -------------------------------------------------------------------------------------------------- 

8 

9# Standard libraries 

10import argparse 

11from pathlib import Path 

12from shutil import which 

13from typing import TYPE_CHECKING, Any, Optional, Type 

14 

15if TYPE_CHECKING: 

16 from tsfpga.vivado.simlib_common import VivadoSimlibCommon 

17 

18# Third party libraries 

19from vunit.ui import VUnit 

20from vunit.vivado.vivado import add_from_compile_order_file, create_compile_order_file 

21from vunit.vunit_cli import VUnitCLI 

22 

23# First party libraries 

24import tsfpga 

25import tsfpga.create_vhdl_ls_config 

26from tsfpga.module_list import ModuleList 

27from tsfpga.vivado.ip_cores import VivadoIpCores 

28from tsfpga.vivado.simlib import VivadoSimlib 

29 

30 

31def get_arguments_cli(default_output_path: Path) -> VUnitCLI: 

32 """ 

33 Get arguments for the simulation flow. 

34 

35 Arguments: 

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

37 (both VUnit files and Vivado files). 

38 """ 

39 cli = VUnitCLI() 

40 

41 # Print default values when doing --help 

42 cli.parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter 

43 

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

45 for action in cli.parser._actions: 

46 if action.dest == "output_path": 

47 action.default = default_output_path / "vunit_out" 

48 break 

49 else: 

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

51 

52 cli.parser.add_argument( 

53 "--output-path-vivado", 

54 type=Path, 

55 default=default_output_path, 

56 help=( 

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

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

59 ), 

60 ) 

61 

62 cli.parser.add_argument( 

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

64 ) 

65 

66 cli.parser.add_argument( 

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

68 ) 

69 

70 cli.parser.add_argument( 

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

72 ) 

73 

74 cli.parser.add_argument( 

75 "--vcs-minimal", 

76 action="store_true", 

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

78 ) 

79 

80 return cli 

81 

82 

83class SimulationProject: 

84 """ 

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

86 """ 

87 

88 def __init__(self, args: argparse.Namespace, enable_preprocessing: bool = False) -> None: 

89 """ 

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

91 

92 Arguments: 

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

94 enable_preprocessing: If ``True``, VUnit location/check preprocessing will be enabled. 

95 """ 

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

97 self.vunit_proj.add_vhdl_builtins() 

98 self.vunit_proj.add_verification_components() 

99 self.vunit_proj.add_random() 

100 

101 if enable_preprocessing: 

102 self.vunit_proj.enable_location_preprocessing() 

103 self.vunit_proj.enable_check_preprocessing() 

104 

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

106 

107 def add_modules( 

108 self, 

109 args: argparse.Namespace, 

110 modules: ModuleList, 

111 modules_no_sim: Optional[ModuleList] = None, 

112 **setup_vunit_kwargs: Any, 

113 ) -> None: 

114 """ 

115 Add module source files to the VUnit project. 

116 

117 Arguments: 

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

119 modules: These modules will be included in the simulation project. 

120 modules_no_sim: These modules will be included in the simulation project, 

121 but their test files will not be added. 

122 setup_vunit_kwargs: Further arguments that will be sent to 

123 :meth:`.BaseModule.setup_vunit` for each module. 

124 Note that this is a "kwargs" style argument; any number of named arguments can 

125 be sent. 

126 """ 

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

128 

129 include_unisim = not args.vivado_skip 

130 include_ip_cores = self.has_commercial_simulator and not args.vivado_skip 

131 

132 for module in modules + modules_no_sim: 

133 vunit_library = self.vunit_proj.add_library( 

134 library_name=module.library_name, allow_duplicate=True 

135 ) 

136 simulate_this_module = module not in modules_no_sim 

137 

138 for hdl_file in module.get_simulation_files( 

139 include_tests=simulate_this_module, 

140 include_unisim=include_unisim, 

141 include_ip_cores=include_ip_cores, 

142 ): 

143 if hdl_file.is_vhdl or hdl_file.is_verilog_source: 

144 vunit_library.add_source_file(hdl_file.path) 

145 else: 

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

147 

148 if simulate_this_module: 

149 module.setup_vunit( 

150 vunit_proj=self.vunit_proj, 

151 include_unisim=include_unisim, 

152 include_ip_cores=include_ip_cores, 

153 **setup_vunit_kwargs, 

154 ) 

155 

156 def add_vivado_simlib(self, args: argparse.Namespace) -> Optional["VivadoSimlibCommon"]: 

157 """ 

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

159 Will compile simlib if necessary. 

160 

161 Arguments: 

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

163 

164 Return: 

165 The simlib object, ``None`` if simlib was not added due to command line argument. 

166 """ 

167 if args.vivado_skip: 

168 return None 

169 

170 return self._add_simlib( 

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

172 ) 

173 

174 def _add_simlib(self, output_path: Path, force_compile: bool) -> "VivadoSimlibCommon": 

175 """ 

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

177 

178 .. note:: 

179 

180 This method can be overloaded in a subclass if you want to do something more 

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

182 

183 Arguments: 

184 output_path: Compiled simlib will be placed in sub-directory of this path. 

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

186 

187 Return: 

188 The simlib object. 

189 """ 

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

191 if force_compile or vivado_simlib.compile_is_needed: 

192 vivado_simlib.compile() 

193 vivado_simlib.to_archive() 

194 

195 vivado_simlib.add_to_vunit_project() 

196 

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

198 # function cannot have access sub-elements". Hence, relaxed rules need to be enabled when 

199 # using unisim. 

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

201 

202 return vivado_simlib 

203 

204 def add_vivado_ip_cores( 

205 self, 

206 args: argparse.Namespace, 

207 modules: ModuleList, 

208 vivado_part_name: str = "xc7z020clg400-1", 

209 vivado_ip_core_project_class: Optional[Type[Any]] = None, 

210 ) -> Optional[Path]: 

211 """ 

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

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

214 

215 Arguments: 

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

217 modules: IP cores from these modules will be included in the simulation project. 

218 vivado_part_name: Part name to be used for Vivado IP core project. 

219 Might have to change from default depending on what parts you have available in your 

220 Vivado installation. 

221 vivado_ip_core_project_class: Class to be used for Vivado IP core project. 

222 Can be left at default in most cases. 

223 

224 Return: 

225 Path to the Vivado IP core project's ``project`` directory. 

226 ``None`` if Vivado IP cores were not added due to command line argument. 

227 """ 

228 if args.vivado_skip: 

229 return None 

230 

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

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

233 ( 

234 ip_core_compile_order_file, 

235 ip_core_vivado_project_directory, 

236 ) = self._generate_ip_core_files( 

237 modules=modules, 

238 output_path=args.output_path_vivado, 

239 force_generate=args.ip_compile, 

240 part_name=vivado_part_name, 

241 vivado_project_class=vivado_ip_core_project_class, 

242 ) 

243 if self.has_commercial_simulator: 

244 add_from_compile_order_file( 

245 vunit_obj=self.vunit_proj, compile_order_file=ip_core_compile_order_file 

246 ) 

247 

248 return ip_core_vivado_project_directory 

249 

250 @staticmethod 

251 def _generate_ip_core_files( 

252 modules: ModuleList, 

253 output_path: Path, 

254 force_generate: bool, 

255 part_name: str, 

256 vivado_project_class: Optional[Type[Any]] = None, 

257 ) -> tuple[Path, Path]: 

258 """ 

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

260 Create a new project to generate files if needed. 

261 

262 Arguments: 

263 modules: IP cores from these modules will be included. 

264 output_path: IP core files will be placed in sub-directory of this path. 

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

266 part_name: Vivado part name. 

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

268 """ 

269 vivado_ip_cores = VivadoIpCores( 

270 modules=modules, 

271 output_path=output_path, 

272 part_name=part_name, 

273 vivado_project_class=vivado_project_class, 

274 ) 

275 

276 if force_generate: 

277 vivado_ip_cores.create_vivado_project() 

278 vivado_project_created = True 

279 else: 

280 vivado_project_created = vivado_ip_cores.create_vivado_project_if_needed() 

281 

282 if vivado_project_created: 

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

284 # a new compile order file 

285 create_compile_order_file( 

286 vivado_ip_cores.vivado_project_file, vivado_ip_cores.compile_order_file 

287 ) 

288 

289 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory 

290 

291 

292def create_vhdl_ls_configuration( 

293 output_path: Path, 

294 temp_files_path: Path, 

295 modules: ModuleList, 

296 ip_core_vivado_project_directory: Optional[Path] = None, 

297) -> None: 

298 """ 

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

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

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

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

303 

304 Arguments: 

305 output_path: Config file will be placed here. 

306 temp_files_path: Some temporary files will be placed here. 

307 modules: These modules will be added. 

308 ip_core_vivado_project_directory: Vivado IP core files in this location will be added. 

309 """ 

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

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

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

313 # via modules. 

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

315 vunit_proj.add_vhdl_builtins() 

316 vunit_proj.add_verification_components() 

317 vunit_proj.add_random() 

318 vunit_proj.add_osvvm() 

319 

320 which_vivado = which("vivado") 

321 vivado_location = None if which_vivado is None else Path(which_vivado) 

322 

323 tsfpga.create_vhdl_ls_config.create_configuration( 

324 output_path=output_path, 

325 modules=modules, 

326 vunit_proj=vunit_proj, 

327 vivado_location=vivado_location, 

328 ip_core_vivado_project_directory=ip_core_vivado_project_directory, 

329 )