Coverage for tsfpga/examples/simulation_utils.py: 0%
89 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-28 20:52 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-28 20:52 +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# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import argparse
11from pathlib import Path
12from shutil import which
13from typing import TYPE_CHECKING, Any, Optional, Type
15if TYPE_CHECKING:
16 from tsfpga.vivado.simlib_common import VivadoSimlibCommon
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
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
31def get_arguments_cli(default_output_path: Path) -> VUnitCLI:
32 """
33 Get arguments for the simulation flow.
35 Arguments:
36 default_output_path: Will be set as default for output path arguments
37 (both VUnit files and Vivado files).
38 """
39 cli = VUnitCLI()
41 # Print default values when doing --help
42 cli.parser.formatter_class = argparse.ArgumentDefaultsHelpFormatter
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")
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 )
62 cli.parser.add_argument(
63 "--vivado-skip", action="store_true", help="skip all steps that require Vivado"
64 )
66 cli.parser.add_argument(
67 "--ip-compile", action="store_true", help="force (re)compile of IP cores"
68 )
70 cli.parser.add_argument(
71 "--simlib-compile", action="store_true", help="force (re)compile of Vivado simlib"
72 )
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 )
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 )
86 return cli
89class SimulationProject:
90 """
91 Class for setting up and handling a VUnit simulation project. Should be reusable in most cases.
92 """
94 def __init__(self, args: argparse.Namespace, enable_preprocessing: bool = False) -> None:
95 """
96 Create a VUnit project, configured according to the given arguments.
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
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()
109 if enable_preprocessing:
110 self.vunit_proj.enable_location_preprocessing()
111 self.vunit_proj.enable_check_preprocessing()
113 self.has_commercial_simulator = self.vunit_proj.get_simulator_name() != "ghdl"
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.
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
144 include_unisim = not self.args.vivado_skip
145 include_ip_cores = self.has_commercial_simulator and not self.args.vivado_skip
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
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)
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 )
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.
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
183 return self._add_simlib(
184 output_path=self.args.output_path_vivado, force_compile=self.args.simlib_compile
185 )
187 def _add_simlib(self, output_path: Path, force_compile: bool) -> "VivadoSimlibCommon":
188 """
189 Add Vivado simlib to the VUnit project. Compile if needed.
191 .. note::
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.
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.
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()
208 vivado_simlib.add_to_vunit_project()
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"])
215 return vivado_simlib
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.
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.
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
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 )
259 return ip_core_vivado_project_directory
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.
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 )
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()
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 )
301 return vivado_ip_cores.compile_order_file, vivado_ip_cores.project_directory
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.
316 Arguments:
317 output_path: Config file will be placed in this directory.
318 temp_files_path: Some temporary files will be stored in a folder within this directory.
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()
332 which_vivado = which("vivado")
333 vivado_location = None if which_vivado is None else Path(which_vivado)
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 )