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

89 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-07 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 cli.parser.add_argument( 

81 "--inspect", 

82 action="store_true", 

83 help="optionally inspect some simulation result. Is only available for some modules", 

84 ) 

85 

86 return cli 

87 

88 

89class SimulationProject: 

90 """ 

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

92 """ 

93 

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

95 """ 

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

97 

98 Arguments: 

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

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

101 """ 

102 self.args = args 

103 

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

105 self.vunit_proj.add_vhdl_builtins() 

106 self.vunit_proj.add_verification_components() 

107 self.vunit_proj.add_random() 

108 

109 if enable_preprocessing: 

110 self.vunit_proj.enable_location_preprocessing() 

111 self.vunit_proj.enable_check_preprocessing() 

112 

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

114 

115 def add_modules( 

116 self, 

117 modules: ModuleList, 

118 modules_no_sim: Optional[ModuleList] = None, 

119 include_vhdl_files: bool = True, 

120 include_verilog_files: bool = True, 

121 include_systemverilog_files: bool = True, 

122 **setup_vunit_kwargs: Any, 

123 ) -> None: 

124 """ 

125 Add module source files to the VUnit project. 

126 

127 Arguments: 

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

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

130 but their test files will not be added. 

131 include_vhdl_files: Optionally disable inclusion of VHDL files from 

132 the modules. 

133 include_verilog_files: Optionally disable inclusion of Verilog files from 

134 the modules. 

135 include_systemverilog_files: Optionally disable inclusion of SystemVerilog files from 

136 the modules. 

137 setup_vunit_kwargs: Further arguments that will be sent to 

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

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

140 be sent. 

141 """ 

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

143 

144 include_unisim = not self.args.vivado_skip 

145 include_ip_cores = self.has_commercial_simulator and not self.args.vivado_skip 

146 

147 for module in modules + modules_no_sim: 

148 vunit_library = self.vunit_proj.add_library( 

149 library_name=module.library_name, allow_duplicate=True 

150 ) 

151 simulate_this_module = module not in modules_no_sim 

152 

153 for hdl_file in module.get_simulation_files( 

154 include_tests=simulate_this_module, 

155 include_unisim=include_unisim, 

156 include_ip_cores=include_ip_cores, 

157 include_vhdl_files=include_vhdl_files, 

158 include_verilog_files=include_verilog_files, 

159 include_systemverilog_files=include_systemverilog_files, 

160 ): 

161 vunit_library.add_source_file(hdl_file.path) 

162 

163 if simulate_this_module: 

164 module.setup_vunit( 

165 vunit_proj=self.vunit_proj, 

166 include_unisim=include_unisim, 

167 include_ip_cores=include_ip_cores, 

168 inspect=self.args.inspect, 

169 **setup_vunit_kwargs, 

170 ) 

171 

172 def add_vivado_simlib(self) -> Optional["VivadoSimlibCommon"]: 

173 """ 

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

175 Will compile simlib if necessary. 

176 

177 Return: 

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

179 """ 

180 if self.args.vivado_skip: 

181 return None 

182 

183 return self._add_simlib( 

184 output_path=self.args.output_path_vivado, force_compile=self.args.simlib_compile 

185 ) 

186 

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

188 """ 

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

190 

191 .. note:: 

192 

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

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

195 

196 Arguments: 

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

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

199 

200 Return: 

201 The simlib object. 

202 """ 

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

204 if force_compile or vivado_simlib.compile_is_needed: 

205 vivado_simlib.compile() 

206 vivado_simlib.to_archive() 

207 

208 vivado_simlib.add_to_vunit_project() 

209 

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

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

212 # using unisim. 

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

214 

215 return vivado_simlib 

216 

217 def add_vivado_ip_cores( 

218 self, 

219 modules: ModuleList, 

220 vivado_part_name: str = "xc7z020clg400-1", 

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

222 ) -> Optional[Path]: 

223 """ 

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

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

226 

227 Arguments: 

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

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

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

231 Vivado installation. 

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

233 Can be left at default in most cases. 

234 

235 Return: 

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

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

238 """ 

239 if self.args.vivado_skip: 

240 return None 

241 

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

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

244 ( 

245 ip_core_compile_order_file, 

246 ip_core_vivado_project_directory, 

247 ) = self._generate_ip_core_files( 

248 modules=modules, 

249 output_path=self.args.output_path_vivado, 

250 force_generate=self.args.ip_compile, 

251 part_name=vivado_part_name, 

252 vivado_project_class=vivado_ip_core_project_class, 

253 ) 

254 if self.has_commercial_simulator: 

255 add_from_compile_order_file( 

256 vunit_obj=self.vunit_proj, compile_order_file=ip_core_compile_order_file 

257 ) 

258 

259 return ip_core_vivado_project_directory 

260 

261 @staticmethod 

262 def _generate_ip_core_files( 

263 modules: ModuleList, 

264 output_path: Path, 

265 force_generate: bool, 

266 part_name: str, 

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

268 ) -> tuple[Path, Path]: 

269 """ 

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

271 Create a new project to generate files if needed. 

272 

273 Arguments: 

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

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

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

277 part_name: Vivado part name. 

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

279 """ 

280 vivado_ip_cores = VivadoIpCores( 

281 modules=modules, 

282 output_path=output_path, 

283 part_name=part_name, 

284 vivado_project_class=vivado_project_class, 

285 ) 

286 

287 if force_generate: 

288 vivado_ip_cores.create_vivado_project() 

289 vivado_project_created = True 

290 else: 

291 vivado_project_created = vivado_ip_cores.create_vivado_project_if_needed() 

292 

293 if vivado_project_created: 

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

295 # a new compile order file 

296 create_compile_order_file( 

297 project_file=vivado_ip_cores.vivado_project_file, 

298 compile_order_file=vivado_ip_cores.compile_order_file, 

299 ) 

300 

301 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory 

302 

303 

304def create_vhdl_ls_configuration( 

305 output_path: Path, 

306 temp_files_path: Path, 

307 modules: ModuleList, 

308 ip_core_vivado_project_directory: Optional[Path] = None, 

309) -> None: 

310 """ 

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

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

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

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

315 

316 Arguments: 

317 output_path: Config file will be placed here. 

318 temp_files_path: Some temporary files will be placed here. 

319 modules: These modules will be added. 

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

321 """ 

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

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

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

325 # via modules. 

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

327 vunit_proj.add_vhdl_builtins() 

328 vunit_proj.add_verification_components() 

329 vunit_proj.add_random() 

330 vunit_proj.add_osvvm() 

331 

332 which_vivado = which("vivado") 

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

334 

335 tsfpga.create_vhdl_ls_config.create_configuration( 

336 output_path=output_path, 

337 modules=modules, 

338 vunit_proj=vunit_proj, 

339 vivado_location=vivado_location, 

340 ip_core_vivado_project_directory=ip_core_vivado_project_directory, 

341 )