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

85 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-29 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 

9import argparse 

10from shutil import which 

11import sys 

12from pathlib import Path 

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.resolve() 

16sys.path.insert(0, str(PATH_TO_TSFPGA)) 

17 

18from vunit import VUnitCLI, VUnit 

19from vunit.vivado.vivado import create_compile_order_file, add_from_compile_order_file 

20 

21import tsfpga 

22import tsfpga.create_vhdl_ls_config 

23from tsfpga.module import ModuleList 

24from tsfpga.vivado.ip_cores import VivadoIpCores 

25from tsfpga.vivado.simlib import VivadoSimlib 

26 

27 

28def get_arguments_cli(default_output_path): 

29 """ 

30 Get arguments for the simulation flow. 

31 

32 Arguments: 

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

34 (both VUnit files and Vivado files). 

35 """ 

36 cli = VUnitCLI() 

37 

38 # Print default values when doing --help 

39 cli.parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter 

40 

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

42 for action in cli.parser._actions: 

43 if action.dest == "output_path": 

44 action.default = default_output_path / "vunit_out" 

45 break 

46 else: 

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

48 

49 cli.parser.add_argument( 

50 "--output-path-vivado", 

51 type=Path, 

52 default=default_output_path, 

53 help=( 

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

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

56 ), 

57 ) 

58 

59 cli.parser.add_argument( 

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

61 ) 

62 

63 cli.parser.add_argument( 

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

65 ) 

66 

67 cli.parser.add_argument( 

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

69 ) 

70 

71 cli.parser.add_argument( 

72 "--vcs-minimal", 

73 action="store_true", 

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

75 ) 

76 

77 return cli 

78 

79 

80class SimulationProject: 

81 """ 

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

83 """ 

84 

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

86 """ 

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

88 

89 Arguments: 

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

91 be enabled. 

92 """ 

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

94 self.vunit_proj.add_verification_components() 

95 self.vunit_proj.add_random() 

96 

97 if enable_preprocessing: 

98 self.vunit_proj.enable_location_preprocessing() 

99 self.vunit_proj.enable_check_preprocessing() 

100 

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

102 

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

104 """ 

105 Add module source files to the VUnit project. 

106 

107 Arguments: 

108 args: Command line argument namespace. 

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

110 simulation project. 

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

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

113 setup_vunit_kwargs: Further arguments that will be sent to 

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

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

116 """ 

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

118 

119 include_unisim = not args.vivado_skip 

120 include_ip_cores = self.has_commercial_simulator and not args.vivado_skip 

121 

122 for module in modules + modules_no_sim: 

123 vunit_library = self.vunit_proj.add_library( 

124 library_name=module.library_name, allow_duplicate=True 

125 ) 

126 simulate_this_module = module not in modules_no_sim 

127 

128 for hdl_file in module.get_simulation_files( 

129 include_tests=simulate_this_module, 

130 include_unisim=include_unisim, 

131 include_ip_cores=include_ip_cores, 

132 ): 

133 if hdl_file.is_vhdl or hdl_file.is_verilog_source: 

134 vunit_library.add_source_file(hdl_file.path) 

135 else: 

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

137 

138 if simulate_this_module: 

139 module.setup_vunit( 

140 vunit_proj=self.vunit_proj, 

141 include_unisim=include_unisim, 

142 include_ip_cores=include_ip_cores, 

143 **setup_vunit_kwargs, 

144 ) 

145 

146 def add_vivado_simlib(self, args): 

147 """ 

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

149 Will compile simlib if necessary. 

150 

151 Arguments: 

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

153 

154 Return: 

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

156 """ 

157 if args.vivado_skip: 

158 return None 

159 

160 return self._add_simlib( 

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

162 ) 

163 

164 def _add_simlib(self, output_path, force_compile): 

165 """ 

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

167 

168 .. note:: 

169 

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

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

172 

173 Arguments: 

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

175 this path. 

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

177 

178 Return: 

179 VivadoSimlibCommon: The simlib object. 

180 """ 

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

182 if force_compile or vivado_simlib.compile_is_needed: 

183 vivado_simlib.compile() 

184 vivado_simlib.to_archive() 

185 

186 vivado_simlib.add_to_vunit_project() 

187 

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

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

190 # using unisim. 

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

192 

193 return vivado_simlib 

194 

195 def add_vivado_ip_cores( 

196 self, 

197 args, 

198 modules, 

199 vivado_part_name="xc7z020clg400-1", 

200 vivado_ip_core_project_class=None, 

201 ): 

202 """ 

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

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

205 

206 Arguments: 

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

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

209 simulation project. 

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

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

212 Vivado installation. 

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

214 at default in most cases. 

215 

216 Return: 

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

218 """ 

219 if args.vivado_skip: 

220 return None 

221 

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

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

224 ( 

225 ip_core_compile_order_file, 

226 ip_core_vivado_project_directory, 

227 ) = self._generate_ip_core_files( 

228 modules=modules, 

229 output_path=args.output_path_vivado, 

230 force_generate=args.ip_compile, 

231 part_name=vivado_part_name, 

232 vivado_project_class=vivado_ip_core_project_class, 

233 ) 

234 if self.has_commercial_simulator: 

235 add_from_compile_order_file( 

236 vunit_obj=self.vunit_proj, compile_order_file=ip_core_compile_order_file 

237 ) 

238 

239 return ip_core_vivado_project_directory 

240 

241 @staticmethod 

242 def _generate_ip_core_files( 

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

244 ): 

245 """ 

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

247 Create a new project to generate files if needed. 

248 

249 Arguments: 

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

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

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

253 part_name (str): Vivado part name. 

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

255 """ 

256 vivado_ip_cores = VivadoIpCores( 

257 modules=modules, 

258 output_path=output_path, 

259 part_name=part_name, 

260 vivado_project_class=vivado_project_class, 

261 ) 

262 

263 if force_generate: 

264 vivado_ip_cores.create_vivado_project() 

265 vivado_project_created = True 

266 else: 

267 vivado_project_created = vivado_ip_cores.create_vivado_project_if_needed() 

268 

269 if vivado_project_created: 

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

271 # a new compile order file 

272 create_compile_order_file( 

273 vivado_ip_cores.vivado_project_file, vivado_ip_cores.compile_order_file 

274 ) 

275 

276 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory 

277 

278 

279def create_vhdl_ls_configuration( 

280 output_path, temp_files_path, modules, ip_core_vivado_project_directory=None 

281): 

282 """ 

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

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

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

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

287 

288 Arguments: 

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

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

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

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

293 will be added. 

294 """ 

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

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

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

298 # via modules. 

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

300 vunit_proj.add_verification_components() 

301 vunit_proj.add_random() 

302 vunit_proj.add_osvvm() 

303 

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

305 tsfpga.create_vhdl_ls_config.create_configuration( 

306 output_path=output_path, 

307 modules=modules, 

308 vunit_proj=vunit_proj, 

309 vivado_location=vivado_location, 

310 ip_core_vivado_project_directory=ip_core_vivado_project_directory, 

311 )