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
« 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# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import argparse
11from pathlib import Path
12from shutil import which
14# Third party libraries
15from vunit import VUnit, VUnitCLI
16from vunit.vivado.vivado import add_from_compile_order_file, create_compile_order_file
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
26def get_arguments_cli(default_output_path):
27 """
28 Get arguments for the simulation flow.
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()
36 # Print default values when doing --help
37 cli.parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter
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")
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 )
57 cli.parser.add_argument(
58 "--vivado-skip", action="store_true", help="skip all steps that require Vivado"
59 )
61 cli.parser.add_argument(
62 "--ip-compile", action="store_true", help="force (re)compile of IP cores"
63 )
65 cli.parser.add_argument(
66 "--simlib-compile", action="store_true", help="force (re)compile of Vivado simlib"
67 )
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 )
75 return cli
78class SimulationProject:
79 """
80 Class for setting up and handling a VUnit simulation project. Should be reusable in most cases.
81 """
83 def __init__(self, args, enable_preprocessing=False):
84 """
85 Create a VUnit project, configured according to the given arguments.
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()
95 if enable_preprocessing:
96 self.vunit_proj.enable_location_preprocessing()
97 self.vunit_proj.enable_check_preprocessing()
99 self.has_commercial_simulator = self.vunit_proj.get_simulator_name() != "ghdl"
101 def add_modules(self, args, modules, modules_no_sim=None, **setup_vunit_kwargs):
102 """
103 Add module source files to the VUnit project.
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
117 include_unisim = not args.vivado_skip
118 include_ip_cores = self.has_commercial_simulator and not args.vivado_skip
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
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}"
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 )
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.
149 Arguments:
150 args: Command line argument namespace from ``simulate.py``.
152 Return:
153 :class:`.VivadoSimlibCommon`: The simlib object.
154 """
155 if args.vivado_skip:
156 return None
158 return self._add_simlib(
159 output_path=args.output_path_vivado, force_compile=args.simlib_compile
160 )
162 def _add_simlib(self, output_path, force_compile):
163 """
164 Add Vivado simlib to the VUnit project. Compile if needed.
166 .. note::
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.
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.
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()
184 vivado_simlib.add_to_vunit_project()
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"])
191 return vivado_simlib
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.
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.
214 Return:
215 pathlib.Path: Path to the Vivado IP core project's ``project`` directory.
216 """
217 if args.vivado_skip:
218 return None
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 )
237 return ip_core_vivado_project_directory
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.
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 )
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()
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 )
274 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory
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.
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()
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 )