Coverage for pyEDAA / ProjectModel / __init__.py: 70%
1072 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:26 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:26 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ _ __ __ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ |__/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany #
15# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture #
16# #
17# Licensed under the Apache License, Version 2.0 (the "License"); #
18# you may not use this file except in compliance with the License. #
19# You may obtain a copy of the License at #
20# #
21# http://www.apache.org/licenses/LICENSE-2.0 #
22# #
23# Unless required by applicable law or agreed to in writing, software #
24# distributed under the License is distributed on an "AS IS" BASIS, #
25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
26# See the License for the specific language governing permissions and #
27# limitations under the License. #
28# #
29# SPDX-License-Identifier: Apache-2.0 #
30# ==================================================================================================================== #
31#
32"""An abstract model of EDA tool projects."""
33__author__ = "Patrick Lehmann"
34__email__ = "Paebbels@gmail.com"
35__copyright__ = "2014-2026, Patrick Lehmann, Unai Martinez-Corral"
36__license__ = "Apache License, Version 2.0"
37__version__ = "0.6.2"
38__keywords__ = ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"]
40from os.path import relpath as path_relpath
41from pathlib import Path as pathlib_Path
42from sys import version_info
43from typing import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, Any as typing_Any, Type, Set, Self
45from pyTooling.Common import getFullyQualifiedName
46from pyTooling.Decorators import export
47from pyTooling.MetaClasses import ExtendedType
48from pyTooling.Graph import Graph, Vertex
49from pySVModel import SystemVerilogVersion
50from pyVHDLModel import VHDLVersion
51from pySystemRDLModel import SystemRDLVersion
54@export
55class Attribute(metaclass=ExtendedType):
56 KEY: str
57 VALUE_TYPE: typing_Any
59 @staticmethod
60 def resolve(obj: typing_Any, key: Type['Attribute']):
61 if isinstance(obj, File):
62 return obj._fileSet[key]
63 elif isinstance(obj, FileSet):
64 return obj._design[key]
65 elif isinstance(obj, Design): 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true
66 return obj._project[key]
67 else:
68 raise Exception("Resolution error")
71@export
72class FileType(ExtendedType):
73 """
74 A :term:`meta-class` to construct *FileType* classes.
76 Modifications done by this meta-class:
77 * Register all classes of type :class:`FileType` or derived variants in a class field :attr:`FileType.FileTypes` in this meta-class.
78 """
80 FileTypes: Dict[str, 'FileType'] = {} #: Dictionary of all classes of type :class:`FileType` or derived variants
81 Any: 'FileType'
83 def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typing_Any], **kwargs) -> None:
84 super().__init__(name, bases, dictionary, **kwargs)
85 cls.Any = cls
87 def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs) -> Self:
88 fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs)
89 cls.FileTypes[className] = fileType
90 return fileType
92 def __getattr__(cls, item) -> 'FileType':
93 if item[:2] != "__" and item[-2:] != "__":
94 return cls.FileTypes[item]
95 else:
96 return super().__getattribute__(item)
98 def __contains__(cls, item) -> bool:
99 return issubclass(item, cls)
102@export
103class File(metaclass=FileType, slots=True):
104 """
105 A :term:`File` represents a file in a design. This :term:`base-class` is used
106 for all derived file classes.
108 A file can be created standalone and later associated to a fileset, design and
109 project. Or a fileset, design and/or project can be associated immediately
110 while creating a file.
112 :arg path: Relative or absolute path to the file.
113 :arg project: Project the file is associated with.
114 :arg design: Design the file is associated with.
115 :arg fileSet: Fileset the file is associated with.
116 """
118 _path: pathlib_Path
119 _fileType: 'FileType'
120 _project: Nullable['Project']
121 _design: Nullable['Design']
122 _fileSet: Nullable['FileSet']
123 _attributes: Dict[Type[Attribute], typing_Any]
125 def __init__(
126 self,
127 path: pathlib_Path,
128 project: Nullable["Project"] = None,
129 design: Nullable["Design"] = None,
130 fileSet: Nullable["FileSet"] = None
131 ) -> None:
132 self._fileType = getattr(FileTypes, self.__class__.__name__)
133 self._path = path
134 if project is not None:
135 self._project = project
136 self._design = design
137 if fileSet is not None: 137 ↛ 138line 137 didn't jump to line 138 because the condition on line 137 was never true
138 self.FileSet = fileSet
139 elif design is not None:
140 self._project = design._project
141 self._design = design
142 self.FileSet = design.DefaultFileSet if fileSet is None else fileSet
143 elif fileSet is not None:
144 design = fileSet._design
145 if design is not None:
146 self._project = design._project
147 else:
148 self._project = None
149 self._design = design
150 self.FileSet = fileSet
151 else:
152 self._project = None
153 self._design = None
154 self._fileSet = None
156 self._attributes = {}
157 self._registerAttributes()
159 def _registerAttributes(self) -> None:
160 pass
162 @property
163 def FileType(self) -> 'FileType':
164 """Read-only property to return the file type of this file."""
165 return self._fileType
167 @property
168 def Path(self) -> pathlib_Path:
169 """
170 Read-only property to access the path to the file.
172 :returns: The file's path.
173 """
174 return self._path
176 # TODO: setter?
178 @property
179 def ResolvedPath(self) -> pathlib_Path:
180 """Read-only property returning the resolved path of this file."""
181 if self._path.is_absolute(): 181 ↛ 182line 181 didn't jump to line 182 because the condition on line 181 was never true
182 return self._path.resolve()
183 elif self._fileSet is not None: 183 ↛ 193line 183 didn't jump to line 193 because the condition on line 183 was always true
184 path = (self._fileSet.ResolvedPath / self._path).resolve()
186 if path.is_absolute(): 186 ↛ 190line 186 didn't jump to line 190 because the condition on line 186 was always true
187 return path
188 else:
189 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
190 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
191 else:
192 # TODO: message and exception type
193 raise Exception("")
195 @property
196 def Project(self) -> Nullable['Project']:
197 """Property setting or returning the project this file is used in."""
198 return self._project
200 @Project.setter
201 def Project(self, value: 'Project') -> None:
202 self._project = value
204 if self._fileSet is None: 204 ↛ exitline 204 didn't return from function 'Project' because the condition on line 204 was always true
205 self._project.DefaultDesign.DefaultFileSet.AddFile(self)
207 @property
208 def Design(self) -> Nullable['Design']:
209 """Property setting or returning the design this file is used in."""
210 return self._design
212 @Design.setter
213 def Design(self, value: 'Design') -> None:
214 self._design = value
216 if self._fileSet is None: 216 ↛ 219line 216 didn't jump to line 219 because the condition on line 216 was always true
217 self._design.DefaultFileSet.AddFile(self)
219 if self._project is None: 219 ↛ 221line 219 didn't jump to line 221 because the condition on line 219 was always true
220 self._project = value._project
221 elif self._project is not value._project:
222 raise Exception("The design's project is not identical to the already assigned project.")
224 @property
225 def FileSet(self) -> Nullable['FileSet']:
226 """Property setting or returning the fileset this file is used in."""
227 return self._fileSet
229 @FileSet.setter
230 def FileSet(self, value: 'FileSet') -> None:
231 self._fileSet = value
232 value._files.append(self)
234 def Validate(self) -> None:
235 """Validate this file."""
236 if self._path is None: 236 ↛ 237line 236 didn't jump to line 237 because the condition on line 236 was never true
237 raise Exception("Validation: File has no path.")
238 try:
239 path = self.ResolvedPath
240 except Exception as ex:
241 raise Exception(f"Validation: File '{self._path}' could not compute resolved path.") from ex
242 if not path.exists(): 242 ↛ 243line 242 didn't jump to line 243 because the condition on line 242 was never true
243 raise Exception(f"Validation: File '{self._path}' (={path}) does not exist.")
244 if not path.is_file(): 244 ↛ 245line 244 didn't jump to line 245 because the condition on line 244 was never true
245 raise Exception(f"Validation: File '{self._path}' (={path}) is not a file.")
247 if self._fileSet is None: 247 ↛ 248line 247 didn't jump to line 248 because the condition on line 247 was never true
248 raise Exception(f"Validation: File '{self._path}' has no fileset.")
249 if self._design is None: 249 ↛ 250line 249 didn't jump to line 250 because the condition on line 249 was never true
250 raise Exception(f"Validation: File '{self._path}' has no design.")
251 if self._project is None: 251 ↛ 252line 251 didn't jump to line 252 because the condition on line 251 was never true
252 raise Exception(f"Validation: File '{self._path}' has no project.")
254 def __len__(self) -> int:
255 """
256 Returns number of attributes set on this file.
258 :returns: The number of attributes set on this file.
259 """
260 return len(self._attributes)
262 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
263 """Index access for returning attributes on this file.
265 :param key: The attribute type.
266 :returns: The attribute's value.
267 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
268 """
269 if not issubclass(key, Attribute): 269 ↛ 270line 269 didn't jump to line 270 because the condition on line 269 was never true
270 raise TypeError("Parameter 'key' is not an 'Attribute'.")
272 try:
273 return self._attributes[key]
274 except KeyError:
275 try:
276 return key.resolve(self, key)
277 except KeyError:
278 attribute = key()
279 self._attributes[key] = attribute
280 return attribute
282 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
283 """
284 Index access for adding or setting attributes on this file.
286 :param key: The attribute type.
287 :param value: The attributes value.
288 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
289 """
290 if not issubclass(key, Attribute): 290 ↛ 291line 290 didn't jump to line 291 because the condition on line 290 was never true
291 raise TypeError("Parameter 'key' is not an 'Attribute'.")
293 self._attributes[key] = value
295 def __delitem__(self, key: Type[Attribute]) -> None:
296 """
297 Index access for deleting attributes on this file.
299 :param key: The attribute type.
300 """
301 if not issubclass(key, Attribute): 301 ↛ 302line 301 didn't jump to line 302 because the condition on line 301 was never true
302 raise TypeError("Parameter 'key' is not an 'Attribute'.")
304 del self._attributes[key]
306 def __str__(self) -> str:
307 return f"{self._path}"
310FileTypes = File
313@export
314class HumanReadableContent(metaclass=ExtendedType, mixin=True):
315 """A file type representing human-readable contents."""
318@export
319class XMLContent(HumanReadableContent, mixin=True):
320 """A file type representing XML contents."""
323@export
324class YAMLContent(HumanReadableContent, mixin=True):
325 """A file type representing YAML contents."""
328@export
329class JSONContent(HumanReadableContent, mixin=True):
330 """A file type representing JSON contents."""
333@export
334class INIContent(HumanReadableContent, mixin=True):
335 """A file type representing INI contents."""
338@export
339class TOMLContent(HumanReadableContent, mixin=True):
340 """A file type representing TOML contents."""
343@export
344class TCLContent(HumanReadableContent, mixin=True):
345 """A file type representing content in TCL code."""
348@export
349class SDCContent(TCLContent, mixin=True):
350 """A file type representing contents as Synopsys Design Constraints (SDC)."""
353@export
354class PythonContent(HumanReadableContent, mixin=True):
355 """A file type representing contents as Python source code."""
358@export
359class TextFile(File, HumanReadableContent):
360 """A text file (``*.txt``)."""
363@export
364class LogFile(File, HumanReadableContent):
365 """A log file (``*.log``)."""
368@export
369class XMLFile(File, XMLContent):
370 """An XML file (``*.xml``)."""
373@export
374class SourceFile(File):
375 """Base-class of all source files."""
378@export
379class HDLSourceFile(SourceFile):
380 """Base-class of all HDL source files."""
383@export
384class RDLSourceFile(SourceFile):
385 """Base-class of all RDL source files."""
388@export
389class NetlistFile(SourceFile):
390 """Base-class of all netlist source files."""
393@export
394class EDIFNetlistFile(NetlistFile):
395 """Netlist file in EDIF (Electronic Design Interchange Format)."""
398@export
399class TCLSourceFile(SourceFile, TCLContent):
400 """A TCL source file."""
403@export
404class VHDLSourceFile(HDLSourceFile, HumanReadableContent):
405 """
406 A VHDL source file (of any language version).
408 :arg path: Relative or absolute path to the file.
409 :arg vhdlLibrary: VHDLLibrary this VHDL source file is associated wih.
410 :arg vhdlVersion: VHDLVersion this VHDL source file is associated wih.
411 :arg project: Project the file is associated with.
412 :arg design: Design the file is associated with.
413 :arg fileSet: Fileset the file is associated with.
414 """
416 _vhdlLibrary: Nullable['VHDLLibrary']
417 _vhdlVersion: VHDLVersion
419 def __init__(self, path: pathlib_Path, vhdlLibrary: Union[str, 'VHDLLibrary'] = None, vhdlVersion: Nullable[VHDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None:
420 super().__init__(path, project, design, fileSet)
422 if isinstance(vhdlLibrary, str): 422 ↛ 423line 422 didn't jump to line 423 because the condition on line 422 was never true
423 if design is not None:
424 try:
425 vhdlLibrary = design.VHDLLibraries[vhdlLibrary]
426 except KeyError as ex:
427 raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex
428 elif project is not None:
429 try:
430 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary]
431 except KeyError as ex:
432 raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex
433 else:
434 raise Exception(f"Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.")
435 elif isinstance(vhdlLibrary, VHDLLibrary):
436 self._vhdlLibrary = vhdlLibrary
437 vhdlLibrary.AddFile(self)
438 elif vhdlLibrary is None: 438 ↛ 441line 438 didn't jump to line 441 because the condition on line 438 was always true
439 self._vhdlLibrary = None
440 else:
441 ex = TypeError(f"Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.")
442 if version_info >= (3, 11): # pragma: no cover
443 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.")
444 raise ex
446 self._vhdlVersion = vhdlVersion
448 def Validate(self) -> None:
449 """Validate this VHDL source file."""
450 super().Validate()
452 try:
453 _ = self.VHDLLibrary
454 except Exception as ex:
455 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLLibrary assigned.") from ex
456 try:
457 _ = self.VHDLVersion
458 except Exception as ex:
459 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLVersion assigned.") from ex
461 @property
462 def VHDLLibrary(self) -> 'VHDLLibrary':
463 """Property setting or returning the VHDL library this VHDL source file is used in."""
464 if self._vhdlLibrary is not None:
465 return self._vhdlLibrary
466 elif self._fileSet is not None:
467 return self._fileSet.VHDLLibrary
468 else:
469 raise Exception("VHDLLibrary was neither set locally nor globally.")
471 @VHDLLibrary.setter
472 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
473 self._vhdlLibrary = value
474 value._files.append(self)
476 @property
477 def VHDLVersion(self) -> VHDLVersion:
478 """Property setting or returning the VHDL version this VHDL source file is used in."""
479 if self._vhdlVersion is not None:
480 return self._vhdlVersion
481 elif self._fileSet is not None:
482 return self._fileSet.VHDLVersion
483 else:
484 raise Exception("VHDLVersion was neither set locally nor globally.")
486 @VHDLVersion.setter
487 def VHDLVersion(self, value: VHDLVersion) -> None:
488 self._vhdlVersion = value
490 def __repr__(self) -> str:
491 return f"<VHDL file: '{self.ResolvedPath}'; lib: '{self.VHDLLibrary}'; version: {self.VHDLVersion}>"
494class VerilogMixIn(metaclass=ExtendedType, mixin=True):
495 @property
496 def VerilogVersion(self) -> SystemVerilogVersion:
497 """Property setting or returning the Verilog version this Verilog source file is used in."""
498 if self._version is not None:
499 return self._version
500 elif self._fileSet is not None:
501 return self._fileSet.VerilogVersion
502 else:
503 raise Exception("VerilogVersion was neither set locally nor globally.")
505 @VerilogVersion.setter
506 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
507 self._version = value
510class SystemVerilogMixIn(metaclass=ExtendedType, mixin=True):
511 @property
512 def SVVersion(self) -> SystemVerilogVersion:
513 """Property setting or returning the SystemVerilog version this SystemVerilog source file is used in."""
514 if self._version is not None:
515 return self._version
516 elif self._fileSet is not None:
517 return self._fileSet.SVVersion
518 else:
519 raise Exception("SVVersion was neither set locally nor globally.")
521 @SVVersion.setter
522 def SVVersion(self, value: SystemVerilogVersion) -> None:
523 self._version = value
526@export
527class VerilogBaseFile(HDLSourceFile, HumanReadableContent):
528 _version: SystemVerilogVersion
530 def __init__(self, path: pathlib_Path, version: Nullable[SystemVerilogVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None:
531 super().__init__(path, project, design, fileSet)
533 self._version = version
536@export
537class VerilogSourceFile(VerilogBaseFile, VerilogMixIn):
538 """A Verilog source file (of any language version)."""
541@export
542class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn):
543 """A Verilog header file (of any language version)."""
546@export
547class SystemVerilogBaseFile(VerilogBaseFile):
548 ...
551@export
552class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn):
553 """A SystemVerilog source file (of any language version)."""
556@export
557class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn):
558 """A SystemVerilog header file (of any language version)."""
561@export
562class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent):
563 """A SystemRDL source file (of any language version)."""
565 _srdlVersion: SystemRDLVersion
567 def __init__(self, path: pathlib_Path, srdlVersion: Nullable[SystemRDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None:
568 super().__init__(path, project, design, fileSet)
570 self._srdlVersion = srdlVersion
572 @property
573 def SystemRDLVersion(self) -> SystemRDLVersion:
574 """Property setting or returning the SystemRDL version this SystemRDL source file is used in."""
575 if self._srdlVersion is not None:
576 return self._srdlVersion
577 elif self._fileSet is not None:
578 return self._fileSet.SRDLVersion
579 else:
580 raise Exception("SRDLVersion was neither set locally nor globally.")
582 @SystemRDLVersion.setter
583 def SystemRDLVersion(self, value: SystemRDLVersion) -> None:
584 self._srdlVersion = value
587@export
588class PythonSourceFile(SourceFile, PythonContent):
589 """A Python source file."""
592# TODO: move to a Cocotb module
593@export
594class CocotbPythonFile(PythonSourceFile):
595 """A Python source file used by Cocotb."""
598@export
599class ConstraintFile(File, HumanReadableContent):
600 """Base-class of all constraint files."""
603@export
604class ProjectFile(File):
605 """Base-class of all tool-specific project files."""
608@export
609class CSourceFile(SourceFile):
610 """Base-class of all ANSI-C source files."""
613@export
614class CppSourceFile(SourceFile):
615 """Base-class of all ANSI-C++ source files."""
618@export
619class SettingFile(File):
620 """Base-class of all tool-specific setting files."""
623@export
624class SimulationAnalysisFile(File):
625 """Base-class of all tool-specific analysis files."""
628@export
629class SimulationElaborationFile(File):
630 """Base-class of all tool-specific elaboration files."""
633@export
634class SimulationStartFile(File):
635 """Base-class of all tool-specific simulation start-up files."""
638@export
639class SimulationRunFile(File):
640 """Base-class of all tool-specific simulation run (execution) files."""
643@export
644class WaveformConfigFile(File):
645 """Base-class of all tool-specific waveform configuration files."""
648@export
649class WaveformDatabaseFile(File):
650 """Base-class of all tool-specific waveform database files."""
653@export
654class WaveformExchangeFile(File):
655 """Base-class of all tool-independent waveform exchange files."""
658@export
659class FileSet(metaclass=ExtendedType, slots=True):
660 """
661 A :term:`FileSet` represents a group of files. Filesets can have sub-filesets.
663 The order of insertion is preserved. A fileset can be created standalone and
664 later associated to another fileset, design and/or project. Or a fileset,
665 design and/or project can be associated immediately while creating the
666 fileset.
668 :arg name: Name of this fileset.
669 :arg topLevel: Name of the fileset's toplevel.
670 :arg directory: Path of this fileset (absolute or relative to a parent fileset or design).
671 :arg project: Project the file is associated with.
672 :arg design: Design the file is associated with.
673 :arg parent: Parent fileset if this fileset is nested.
674 :arg vhdlLibrary: Default VHDL library for files in this fileset, if not specified for the file itself.
675 :arg vhdlVersion: Default VHDL version for files in this fileset, if not specified for the file itself.
676 :arg verilogVersion: Default Verilog version for files in this fileset, if not specified for the file itself.
677 :arg svVersion: Default SystemVerilog version for files in this fileset, if not specified for the file itself.
678 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself.
679 """
681 _name: str
682 _topLevel: Nullable[str]
683 _project: Nullable['Project']
684 _design: Nullable['Design']
685 _directory: pathlib_Path
686 _parent: Nullable['FileSet']
687 _fileSets: Dict[str, 'FileSet']
688 _files: List[File]
689 _set: Set
690 _attributes: Dict[Type[Attribute], typing_Any]
691 _vhdlLibraries: Dict[str, 'VHDLLibrary']
692 _vhdlLibrary: 'VHDLLibrary'
693 _vhdlVersion: VHDLVersion
694 _verilogVersion: SystemVerilogVersion
695 _svVersion: SystemVerilogVersion
696 _srdlVersion: SystemRDLVersion
698 def __init__(
699 self,
700 name: str,
701 topLevel: Nullable[str] = None,
702 directory: pathlib_Path = pathlib_Path("."),
703 project: Nullable["Project"] = None,
704 design: Nullable["Design"] = None,
705 parent: Nullable['FileSet'] = None,
706 vhdlLibrary: Union[str, 'VHDLLibrary'] = None,
707 vhdlVersion: Nullable[VHDLVersion] = None,
708 verilogVersion: Nullable[SystemVerilogVersion] = None,
709 svVersion: Nullable[SystemVerilogVersion] = None,
710 srdlVersion: Nullable[SystemRDLVersion] = None
711 ) -> None:
712 self._name = name
713 self._topLevel = topLevel
714 if project is not None:
715 self._project = project
716 self._design = design if design is not None else project.DefaultDesign
718 elif design is not None:
719 self._project = design._project
720 self._design = design
721 else:
722 self._project = None
723 self._design = None
724 self._directory = directory
725 self._parent = parent
726 self._fileSets = {}
727 self._files = []
728 self._set = set()
730 if design is not None:
731 design._fileSets[name] = self
733 self._attributes = {}
734 self._vhdlLibraries = {}
736 # TODO: handle if vhdlLibrary is a string
737 self._vhdlLibrary = vhdlLibrary
738 self._vhdlVersion = vhdlVersion
739 self._verilogVersion = verilogVersion
740 self._svVersion = svVersion
741 self._srdlVersion = srdlVersion
743 @property
744 def Name(self) -> str:
745 """Property setting or returning the fileset's name."""
746 return self._name
748 @Name.setter
749 def Name(self, value: str) -> None:
750 self._name = value
752 @property
753 def TopLevel(self) -> str:
754 """Property setting or returning the fileset's toplevel."""
755 return self._topLevel
757 @TopLevel.setter
758 def TopLevel(self, value: str) -> None:
759 self._topLevel = value
761 @property
762 def Project(self) -> Nullable['Project']:
763 """Property setting or returning the project this fileset is used in."""
764 return self._project
766 @Project.setter
767 def Project(self, value: 'Project') -> None:
768 self._project = value
770 @property
771 def Design(self) -> Nullable['Design']:
772 """Property setting or returning the design this fileset is used in."""
773 if self._design is not None:
774 return self._design
775 elif self._parent is not None: 775 ↛ 776line 775 didn't jump to line 776 because the condition on line 775 was never true
776 return self._parent.Design
777 else:
778 return None
779 # TODO: raise exception instead
780 # QUESTION: how to handle if design and parent is set?
782 @Design.setter
783 def Design(self, value: 'Design') -> None:
784 self._design = value
785 if self._project is None: 785 ↛ 787line 785 didn't jump to line 787 because the condition on line 785 was always true
786 self._project = value._project
787 elif self._project is not value._project:
788 raise Exception("The design's project is not identical to the already assigned project.")
790 @property
791 def Directory(self) -> pathlib_Path:
792 """Property setting or returning the directory this fileset is located in."""
793 return self._directory
795 @Directory.setter
796 def Directory(self, value: pathlib_Path) -> None:
797 self._directory = value
799 @property
800 def ResolvedPath(self) -> pathlib_Path:
801 """Read-only property returning the resolved path of this fileset."""
802 if self._directory.is_absolute(): 802 ↛ 803line 802 didn't jump to line 803 because the condition on line 802 was never true
803 return self._directory.resolve()
804 else:
805 if self._parent is not None: 805 ↛ 806line 805 didn't jump to line 806 because the condition on line 805 was never true
806 directory = self._parent.ResolvedPath
807 elif self._design is not None: 807 ↛ 809line 807 didn't jump to line 809 because the condition on line 807 was always true
808 directory = self._design.ResolvedPath
809 elif self._project is not None:
810 directory = self._project.ResolvedPath
811 else:
812 # TODO: message and exception type
813 raise Exception("")
815 directory = (directory / self._directory).resolve()
816 if directory.is_absolute(): 816 ↛ 820line 816 didn't jump to line 820 because the condition on line 816 was always true
817 return directory
818 else:
819 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
820 return pathlib_Path(path_relpath(directory, pathlib_Path.cwd()))
822 @property
823 def Parent(self) -> Nullable['FileSet']:
824 """Property setting or returning the parent fileset this fileset is used in."""
825 return self._parent
827 @Parent.setter
828 def Parent(self, value: 'FileSet') -> None:
829 self._parent = value
830 value._fileSets[self._name] = self
831 # TODO: check it it already exists
832 # QUESTION: make an Add fileset method?
834 @property
835 def FileSets(self) -> Dict[str, 'FileSet']:
836 """Read-only property returning the dictionary of sub-filesets."""
837 return self._fileSets
839 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'FileSet'] = None) -> Generator[File, None, None]:
840 """
841 Method returning the files of this fileset.
843 :arg fileType: A filter for file types. Default: ``Any``.
844 :arg fileSet: Specifies how to handle sub-filesets.
845 """
846 if fileSet is False: 846 ↛ 847line 846 didn't jump to line 847 because the condition on line 846 was never true
847 for file in self._files:
848 if file.FileType in fileType:
849 yield file
850 elif fileSet is None: 850 ↛ 858line 850 didn't jump to line 858 because the condition on line 850 was always true
851 for fileSet in self._fileSets.values(): 851 ↛ 852line 851 didn't jump to line 852 because the loop on line 851 never started
852 for file in fileSet.Files(fileType):
853 yield file
854 for file in self._files:
855 if file.FileType in fileType:
856 yield file
857 else:
858 if isinstance(fileSet, str):
859 fileSetName = fileSet
860 try:
861 fileSet = self._fileSets[fileSetName]
862 except KeyError as ex:
863 raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex
864 elif not isinstance(fileSet, FileSet):
865 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.")
867 for file in fileSet.Files(fileType):
868 yield file
870 def AddFileSet(self, fileSet: "FileSet") -> None:
871 """
872 Method to add a single sub-fileset to this fileset.
874 :arg fileSet: A fileset to add to this fileset as sub-fileset.
875 """
876 if not isinstance(fileSet, FileSet): 876 ↛ 877line 876 didn't jump to line 877 because the condition on line 876 was never true
877 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.")
878 elif fileSet in self._fileSets: 878 ↛ 879line 878 didn't jump to line 879 because the condition on line 878 was never true
879 raise Exception("Sub-fileset already contains this fileset.")
880 elif fileSet.Name in self._fileSets.keys(): 880 ↛ 881line 880 didn't jump to line 881 because the condition on line 880 was never true
881 raise Exception(f"Fileset already contains a sub-fileset named '{fileSet.Name}'.")
883 self._fileSets[fileSet.Name] = fileSet
884 fileSet._parent = self
886 def AddFileSets(self, fileSets: Iterable["FileSet"]) -> None:
887 """
888 Method to add a multiple sub-filesets to this fileset.
890 :arg fileSets: An iterable of filesets to add each to the fileset.
891 """
892 for fileSet in fileSets:
893 self.AddFileSet(fileSet)
895 @property
896 def FileSetCount(self) -> int:
897 """Returns number of file sets excl. sub-filesets."""
898 return len(self._fileSets)
900 @property
901 def TotalFileSetCount(self) -> int:
902 """Returns number of file sets incl. sub-filesets."""
903 fileSetCount = len(self._fileSets)
904 for fileSet in self._fileSets.values():
905 fileSetCount += fileSet.TotalFileSetCount
907 return fileSetCount
909 def AddFile(self, file: File) -> None:
910 """
911 Method to add a single file to this fileset.
913 :arg file: A file to add to this fileset.
914 """
915 if not isinstance(file, File):
916 raise TypeError("Parameter 'file' is not of type ProjectModel.File.")
917 elif file._fileSet is not None:
918 ex = ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}'.")
919 if version_info >= (3, 11): # pragma: no cover
920 ex.add_note(f"A file can't be assigned to another fileset.")
921 raise ex
922 elif file in self._set: 922 ↛ 923line 922 didn't jump to line 923 because the condition on line 922 was never true
923 ex = ValueError(f"File '{file.Path!s}' is already part of this fileset.")
924 if version_info >= (3, 11): # pragma: no cover
925 ex.add_note(f"A file can't be added twice to a fileset.")
926 raise ex
928 self._files.append(file)
929 self._set.add(file)
930 file._fileSet = self
932 def AddFiles(self, files: Iterable[File]) -> None:
933 """
934 Method to add a multiple files to this fileset.
936 :arg files: An iterable of files to add each to the fileset.
937 """
938 for file in files:
939 self.AddFile(file)
941 @property
942 def FileCount(self) -> int:
943 """Returns number of files excl. sub-filesets."""
944 return len(self._files)
946 @property
947 def TotalFileCount(self) -> int:
948 """Returns number of files incl. the files in sub-filesets."""
949 fileCount = len(self._files)
950 for fileSet in self._fileSets.values():
951 fileCount += fileSet.FileCount
953 return fileCount
955 def Validate(self) -> None:
956 """Validate this fileset."""
957 if self._name is None or self._name == "": 957 ↛ 958line 957 didn't jump to line 958 because the condition on line 957 was never true
958 raise Exception("Validation: FileSet has no name.")
960 if self._directory is None: 960 ↛ 961line 960 didn't jump to line 961 because the condition on line 960 was never true
961 raise Exception(f"Validation: FileSet '{self._name}' has no directory.")
962 try:
963 path = self.ResolvedPath
964 except Exception as ex:
965 raise Exception(f"Validation: FileSet '{self._name}' could not compute resolved path.") from ex
966 if not path.exists(): 966 ↛ 967line 966 didn't jump to line 967 because the condition on line 966 was never true
967 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' does not exist.")
968 if not path.is_dir(): 968 ↛ 969line 968 didn't jump to line 969 because the condition on line 968 was never true
969 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' is not a directory.")
971 if self._design is None: 971 ↛ 972line 971 didn't jump to line 972 because the condition on line 971 was never true
972 raise Exception(f"Validation: FileSet '{self._directory}' has no design.")
973 if self._project is None: 973 ↛ 974line 973 didn't jump to line 974 because the condition on line 973 was never true
974 raise Exception(f"Validation: FileSet '{self._directory}' has no project.")
976 for fileSet in self._fileSets.values(): 976 ↛ 977line 976 didn't jump to line 977 because the loop on line 976 never started
977 fileSet.Validate()
978 for file in self._files: 978 ↛ 979line 978 didn't jump to line 979 because the loop on line 978 never started
979 file.Validate()
981 def GetOrCreateVHDLLibrary(self, name) -> 'VHDLLibrary':
982 if name in self._vhdlLibraries:
983 return self._vhdlLibraries[name]
984 elif name in self._design._vhdlLibraries:
985 library = self._design._vhdlLibraries[name]
986 self._vhdlLibraries[name] = library
987 return library
988 else:
989 library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion)
990 self._vhdlLibraries[name] = library
991 return library
993 @property
994 def VHDLLibrary(self) -> 'VHDLLibrary':
995 """Property setting or returning the VHDL library of this fileset."""
996 if self._vhdlLibrary is not None:
997 return self._vhdlLibrary
998 elif self._parent is not None: 998 ↛ 1000line 998 didn't jump to line 1000 because the condition on line 998 was always true
999 return self._parent.VHDLLibrary
1000 elif self._design is not None:
1001 return self._design.VHDLLibrary # FIXME: no library property. add a DefaultVHDLLibrary property
1002 else:
1003 raise Exception("VHDLLibrary was neither set locally nor globally.")
1005 @VHDLLibrary.setter
1006 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
1007 self._vhdlLibrary = value
1009 @property
1010 def VHDLVersion(self) -> VHDLVersion:
1011 """Property setting or returning the VHDL version of this fileset."""
1012 if self._vhdlVersion is not None:
1013 return self._vhdlVersion
1014 elif self._parent is not None:
1015 return self._parent.VHDLVersion
1016 elif self._design is not None: 1016 ↛ 1019line 1016 didn't jump to line 1019 because the condition on line 1016 was always true
1017 return self._design.VHDLVersion
1018 else:
1019 raise Exception("VHDLVersion was neither set locally nor globally.")
1021 @VHDLVersion.setter
1022 def VHDLVersion(self, value: VHDLVersion) -> None:
1023 self._vhdlVersion = value
1025 @property
1026 def VerilogVersion(self) -> SystemVerilogVersion:
1027 """Property setting or returning the Verilog version of this fileset."""
1028 if self._verilogVersion is not None:
1029 return self._verilogVersion
1030 elif self._parent is not None:
1031 return self._parent.VerilogVersion
1032 elif self._design is not None: 1032 ↛ 1035line 1032 didn't jump to line 1035 because the condition on line 1032 was always true
1033 return self._design.VerilogVersion
1034 else:
1035 raise Exception("VerilogVersion was neither set locally nor globally.")
1037 @VerilogVersion.setter
1038 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1039 self._verilogVersion = value
1041 @property
1042 def SVVersion(self) -> SystemVerilogVersion:
1043 """Property setting or returning the SystemVerilog version of this fileset."""
1044 if self._svVersion is not None:
1045 return self._svVersion
1046 elif self._parent is not None:
1047 return self._parent.SVVersion
1048 elif self._design is not None: 1048 ↛ 1051line 1048 didn't jump to line 1051 because the condition on line 1048 was always true
1049 return self._design.SVVersion
1050 else:
1051 raise Exception("SVVersion was neither set locally nor globally.")
1053 @SVVersion.setter
1054 def SVVersion(self, value: SystemVerilogVersion) -> None:
1055 self._svVersion = value
1057 @property
1058 def SRDLVersion(self) -> SystemRDLVersion:
1059 if self._srdlVersion is not None:
1060 return self._srdlVersion
1061 elif self._parent is not None:
1062 return self._parent.SRDLVersion
1063 elif self._design is not None:
1064 return self._design.SRDLVersion
1065 else:
1066 raise Exception("SRDLVersion was neither set locally nor globally.")
1068 @SRDLVersion.setter
1069 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1070 self._srdlVersion = value
1072 def __len__(self) -> int:
1073 """
1074 Returns number of attributes set on this fileset.
1076 :returns: The number of attributes set on this fileset.
1077 """
1078 return len(self._attributes)
1080 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1081 """Index access for returning attributes on this fileset.
1083 :param key: The attribute type.
1084 :returns: The attribute's value.
1085 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1086 """
1087 if not issubclass(key, Attribute): 1087 ↛ 1088line 1087 didn't jump to line 1088 because the condition on line 1087 was never true
1088 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1090 try:
1091 return self._attributes[key]
1092 except KeyError:
1093 return key.resolve(self, key)
1095 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1096 """
1097 Index access for adding or setting attributes on this fileset.
1099 :param key: The attribute type.
1100 :param value: The attributes value.
1101 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1102 """
1103 if not issubclass(key, Attribute): 1103 ↛ 1104line 1103 didn't jump to line 1104 because the condition on line 1103 was never true
1104 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1106 self._attributes[key] = value
1108 def __delitem__(self, key: Type[Attribute]) -> None:
1109 """
1110 Index access for deleting attributes on this fileset.
1112 :param key: The attribute type.
1113 """
1114 if not issubclass(key, Attribute): 1114 ↛ 1115line 1114 didn't jump to line 1115 because the condition on line 1114 was never true
1115 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1117 del self._attributes[key]
1119 def __str__(self) -> str:
1120 """Returns the fileset's name."""
1121 return self._name
1124@export
1125class VHDLLibrary(metaclass=ExtendedType, slots=True):
1126 """
1127 A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library.
1129 :arg name: The VHDL libraries' name.
1130 :arg project: Project the VHDL library is associated with.
1131 :arg design: Design the VHDL library is associated with.
1132 :arg vhdlVersion: Default VHDL version for files in this VHDL library, if not specified for the file itself.
1133 """
1135 _name: str
1136 _project: Nullable['Project']
1137 _design: Nullable['Design']
1138 _files: List[File]
1139 _vhdlVersion: VHDLVersion
1141 _attributes: Dict[Attribute, typing_Any]
1142 _dependencyNode: Vertex
1144 def __init__(
1145 self,
1146 name: str,
1147 project: Nullable["Project"] = None,
1148 design: Nullable["Design"] = None,
1149 vhdlVersion: Nullable[VHDLVersion] = None
1150 ) -> None:
1151 self._name = name
1152 self._attributes = {}
1154 if project is not None:
1155 self._project = project
1156 self._design = project._defaultDesign if design is None else design
1157 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1159 if name in self._design._vhdlLibraries: 1159 ↛ 1160line 1159 didn't jump to line 1160 because the condition on line 1159 was never true
1160 raise Exception(f"Library '{name}' already in design '{self._design.Name}'.")
1161 else:
1162 self._design._vhdlLibraries[name] = self
1164 elif design is not None:
1165 self._project = design._project
1166 self._design = design
1167 self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph)
1169 if name in design._vhdlLibraries: 1169 ↛ 1170line 1169 didn't jump to line 1170 because the condition on line 1169 was never true
1170 raise Exception(f"Library '{name}' already in design '{design.Name}'.")
1171 else:
1172 design._vhdlLibraries[name] = self
1174 else:
1175 self._project = None
1176 self._design = None
1177 self._dependencyNode = None
1179 self._files = []
1180 self._vhdlVersion = vhdlVersion
1182 @property
1183 def Name(self) -> str:
1184 return self._name
1186 @property
1187 def Project(self) -> Nullable['Project']:
1188 """Property setting or returning the project this VHDL library is used in."""
1189 return self._project
1191 @Project.setter
1192 def Project(self, value: 'Project') -> None:
1193 if not isinstance(value, Project): 1193 ↛ 1194line 1193 didn't jump to line 1194 because the condition on line 1193 was never true
1194 raise TypeError("Parameter 'value' is not of type 'Project'.")
1196 if value is None: 1196 ↛ 1198line 1196 didn't jump to line 1198 because the condition on line 1196 was never true
1197 # TODO: unlink VHDLLibrary from project
1198 self._project = None
1199 else:
1200 self._project = value
1201 if self._design is None: 1201 ↛ exitline 1201 didn't return from function 'Project' because the condition on line 1201 was always true
1202 self._design = value._defaultDesign
1204 @property
1205 def Design(self) -> Nullable['Design']:
1206 """Property setting or returning the design this VHDL library is used in."""
1207 return self._design
1209 @Design.setter
1210 def Design(self, value: 'Design') -> None:
1211 if not isinstance(value, Design):
1212 raise TypeError("Parameter 'value' is not of type 'Design'.")
1214 if value is None:
1215 # TODO: unlink VHDLLibrary from design
1216 self._design = None
1217 else:
1218 if self._design is None:
1219 self._design = value
1220 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1221 elif self._design is not value:
1222 # TODO: move VHDLLibrary to other design
1223 # TODO: create new vertex in dependency graph and remove vertex from old graph
1224 self._design = value
1225 else:
1226 pass
1228 if self._project is None:
1229 self._project = value._project
1230 elif self._project is not value._project:
1231 raise Exception("The design's project is not identical to the already assigned project.")
1233 @property
1234 def Files(self) -> Generator[File, None, None]:
1235 """Read-only property to return all files in this VHDL library."""
1236 for file in self._files:
1237 yield file
1239 @property
1240 def VHDLVersion(self) -> VHDLVersion:
1241 """Property setting or returning the VHDL version of this VHDL library."""
1242 if self._vhdlVersion is not None:
1243 return self._vhdlVersion
1244 elif self._design is not None: 1244 ↛ 1247line 1244 didn't jump to line 1247 because the condition on line 1244 was always true
1245 return self._design.VHDLVersion
1246 else:
1247 raise Exception("VHDLVersion is not set on VHDLLibrary nor parent object.")
1249 @VHDLVersion.setter
1250 def VHDLVersion(self, value: VHDLVersion) -> None:
1251 self._vhdlVersion = value
1253 def AddDependency(self, library: 'VHDLLibrary') -> None:
1254 library.parent = self
1256 def AddFile(self, vhdlFile: VHDLSourceFile) -> None:
1257 if not isinstance(vhdlFile, VHDLSourceFile): 1257 ↛ 1258line 1257 didn't jump to line 1258 because the condition on line 1257 was never true
1258 ex = TypeError(f"Parameter 'vhdlFile' is not a 'VHDLSourceFile'.")
1259 if version_info >= (3, 11): # pragma: no cover
1260 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlFile)}'.")
1261 raise ex
1263 self._files.append(vhdlFile)
1265 def AddFiles(self, vhdlFiles: Iterable[VHDLSourceFile]) -> None:
1266 for vhdlFile in vhdlFiles:
1267 if not isinstance(vhdlFile, VHDLSourceFile):
1268 raise TypeError(f"Item '{vhdlFile}' in parameter 'vhdlFiles' is not a 'VHDLSourceFile'.")
1270 self._files.append(vhdlFile)
1272 @property
1273 def FileCount(self) -> int:
1274 """Returns number of files."""
1275 return len(self._files)
1277 def __len__(self) -> int:
1278 """
1279 Returns number of attributes set on this VHDL library.
1281 :returns: The number of attributes set on this VHDL library.
1282 """
1283 return len(self._attributes)
1285 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1286 """Index access for returning attributes on this VHDL library.
1288 :param key: The attribute type.
1289 :returns: The attribute's value.
1290 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1291 """
1292 if not issubclass(key, Attribute):
1293 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1295 try:
1296 return self._attributes[key]
1297 except KeyError:
1298 return key.resolve(self, key)
1300 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1301 """
1302 Index access for adding or setting attributes on this VHDL library.
1304 :param key: The attribute type.
1305 :param value: The attributes value.
1306 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1307 """
1308 if not issubclass(key, Attribute):
1309 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1311 self._attributes[key] = value
1313 def __delitem__(self, key: Type[Attribute]) -> None:
1314 """
1315 Index access for deleting attributes on this VHDL library.
1317 :param key: The attribute type.
1318 """
1319 if not issubclass(key, Attribute):
1320 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1322 del self._attributes[key]
1324 def __str__(self) -> str:
1325 """Returns the VHDL library's name."""
1326 return self._name
1329@export
1330class Design(metaclass=ExtendedType, slots=True):
1331 """
1332 A :term:`Design` represents a group of filesets and the source files therein.
1334 Each design contains at least one fileset - the :term:`default fileset`. For
1335 designs with VHDL source files, a independent `VHDLLibraries` overlay structure
1336 exists.
1338 :arg name: The design's name.
1339 :arg topLevel: Name of the design's toplevel.
1340 :arg directory: Path of this design (absolute or relative to the project).
1341 :arg project: Project the design is associated with.
1342 :arg vhdlVersion: Default VHDL version for files in this design, if not specified for the file itself.
1343 :arg verilogVersion: Default Verilog version for files in this design, if not specified for the file itself.
1344 :arg svVersion: Default SystemVerilog version for files in this design, if not specified for the file itself.
1345 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself.
1346 """
1348 _name: str
1349 _topLevel: Nullable[str]
1350 _project: Nullable['Project']
1351 _directory: pathlib_Path
1352 _fileSets: Dict[str, FileSet]
1353 _defaultFileSet: Nullable[FileSet]
1354 _attributes: Dict[Type[Attribute], typing_Any]
1356 _vhdlLibraries: Dict[str, VHDLLibrary]
1357 _vhdlVersion: VHDLVersion
1358 _verilogVersion: SystemVerilogVersion
1359 _svVersion: SystemVerilogVersion
1360 _srdlVersion: SystemRDLVersion
1361 _externalVHDLLibraries: List
1363 _vhdlLibraryDependencyGraph: Graph
1364 _fileDependencyGraph: Graph
1366 def __init__(
1367 self,
1368 name: str,
1369 topLevel: Nullable[str] = None,
1370 directory: pathlib_Path = pathlib_Path("."),
1371 project: Nullable["Project"] = None,
1372 vhdlVersion: Nullable[VHDLVersion] = None,
1373 verilogVersion: Nullable[SystemVerilogVersion] = None,
1374 svVersion: Nullable[SystemVerilogVersion] = None,
1375 srdlVersion: Nullable[SystemRDLVersion] = None
1376 ) -> None:
1377 self._name = name
1378 self._topLevel = topLevel
1379 self._project = project
1380 if project is not None:
1381 project._designs[name] = self
1382 self._directory = directory
1383 self._fileSets = {}
1384 self._defaultFileSet = FileSet("default", project=project, design=self)
1385 self._attributes = {}
1386 self._vhdlLibraries = {}
1387 self._vhdlVersion = vhdlVersion
1388 self._verilogVersion = verilogVersion
1389 self._svVersion = svVersion
1390 self._srdlVersion = srdlVersion
1391 self._externalVHDLLibraries = []
1393 self._vhdlLibraryDependencyGraph = Graph()
1394 self._fileDependencyGraph = Graph()
1396 @property
1397 def Name(self) -> str:
1398 """Property setting or returning the design's name."""
1399 return self._name
1401 @Name.setter
1402 def Name(self, value: str) -> None:
1403 self._name = value
1405 @property
1406 def TopLevel(self) -> str:
1407 """Property setting or returning the fileset's toplevel."""
1408 return self._topLevel
1410 @TopLevel.setter
1411 def TopLevel(self, value: str) -> None:
1412 self._topLevel = value
1414 @property
1415 def Project(self) -> Nullable['Project']:
1416 """Property setting or returning the project this design is used in."""
1417 return self._project
1419 @Project.setter
1420 def Project(self, value: 'Project') -> None:
1421 self._project = value
1423 @property
1424 def Directory(self) -> pathlib_Path:
1425 """Property setting or returning the directory this design is located in."""
1426 return self._directory
1428 @Directory.setter
1429 def Directory(self, value: pathlib_Path) -> None:
1430 self._directory = value
1432 @property
1433 def ResolvedPath(self) -> pathlib_Path:
1434 """Read-only property returning the resolved path of this fileset."""
1435 if self._directory.is_absolute(): 1435 ↛ 1436line 1435 didn't jump to line 1436 because the condition on line 1435 was never true
1436 return self._directory.resolve()
1437 elif self._project is not None: 1437 ↛ 1447line 1437 didn't jump to line 1447 because the condition on line 1437 was always true
1438 path = (self._project.ResolvedPath / self._directory).resolve()
1440 if path.is_absolute(): 1440 ↛ 1444line 1440 didn't jump to line 1444 because the condition on line 1440 was always true
1441 return path
1442 else:
1443 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
1444 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
1445 else:
1446 # TODO: message and exception type
1447 raise Exception("")
1449 @property
1450 def DefaultFileSet(self) -> FileSet:
1451 """Property setting or returning the default fileset of this design."""
1452 return self._defaultFileSet
1454 @DefaultFileSet.setter
1455 def DefaultFileSet(self, value: Union[str, FileSet]) -> None:
1456 if isinstance(value, str):
1457 if value not in self._fileSets.keys():
1458 raise Exception(f"Fileset '{value}' is not in this design.")
1460 self._defaultFileSet = self._fileSets[value]
1461 elif isinstance(value, FileSet):
1462 if value not in self.FileSets:
1463 raise Exception(f"Fileset '{value}' is not associated to this design.")
1465 self._defaultFileSet = value
1466 else:
1467 raise ValueError("Unsupported parameter type for 'value'.")
1469 # TODO: return generator with another method
1470 @property
1471 def FileSets(self) -> Dict[str, FileSet]:
1472 """Read-only property returning the dictionary of filesets."""
1473 return self._fileSets
1475 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[str, FileSet] = None) -> Generator[File, None, None]:
1476 """
1477 Method returning the files of this design.
1479 :arg fileType: A filter for file types. Default: ``Any``.
1480 :arg fileSet: Specifies if all files from all filesets (``fileSet=None``) are files from a single fileset are returned.
1481 """
1482 if fileSet is None:
1483 for fileSet in self._fileSets.values():
1484 for file in fileSet.Files(fileType):
1485 yield file
1486 else:
1487 if isinstance(fileSet, str): 1487 ↛ 1492line 1487 didn't jump to line 1492 because the condition on line 1487 was always true
1488 try:
1489 fileSet = self._fileSets[fileSet]
1490 except KeyError as ex:
1491 raise Exception(f"Fileset {fileSet.Name} not bound to design {self.Name}.") from ex
1492 elif not isinstance(fileSet, FileSet):
1493 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.")
1495 for file in fileSet.Files(fileType):
1496 yield file
1498 def Validate(self) -> None:
1499 """Validate this design."""
1500 if self._name is None or self._name == "": 1500 ↛ 1501line 1500 didn't jump to line 1501 because the condition on line 1500 was never true
1501 raise Exception("Validation: Design has no name.")
1503 if self._directory is None: 1503 ↛ 1504line 1503 didn't jump to line 1504 because the condition on line 1503 was never true
1504 raise Exception(f"Validation: Design '{self._name}' has no directory.")
1505 try:
1506 path = self.ResolvedPath
1507 except Exception as ex:
1508 raise Exception(f"Validation: Design '{self._name}' could not compute resolved path.") from ex
1509 if not path.exists(): 1509 ↛ 1510line 1509 didn't jump to line 1510 because the condition on line 1509 was never true
1510 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' does not exist.")
1511 if not path.is_dir(): 1511 ↛ 1512line 1511 didn't jump to line 1512 because the condition on line 1511 was never true
1512 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' is not a directory.")
1514 if len(self._fileSets) == 0: 1514 ↛ 1515line 1514 didn't jump to line 1515 because the condition on line 1514 was never true
1515 raise Exception(f"Validation: Design '{self._name}' has no fileset.")
1516 try:
1517 if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]: 1517 ↛ 1518line 1517 didn't jump to line 1518 because the condition on line 1517 was never true
1518 raise Exception(f"Validation: Design '{self._name}'s default fileset is the same as listed in filesets.")
1519 except KeyError as ex:
1520 raise Exception(f"Validation: Design '{self._name}'s default fileset is not in list of filesets.") from ex
1521 if self._project is None: 1521 ↛ 1522line 1521 didn't jump to line 1522 because the condition on line 1521 was never true
1522 raise Exception(f"Validation: Design '{self._path}' has no project.")
1524 for fileSet in self._fileSets.values():
1525 fileSet.Validate()
1527 @property
1528 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]:
1529 return self._vhdlLibraries
1531 @property
1532 def VHDLVersion(self) -> VHDLVersion:
1533 if self._vhdlVersion is not None:
1534 return self._vhdlVersion
1535 elif self._project is not None: 1535 ↛ 1538line 1535 didn't jump to line 1538 because the condition on line 1535 was always true
1536 return self._project.VHDLVersion
1537 else:
1538 raise Exception("VHDLVersion was neither set locally nor globally.")
1540 @VHDLVersion.setter
1541 def VHDLVersion(self, value: VHDLVersion) -> None:
1542 self._vhdlVersion = value
1544 @property
1545 def VerilogVersion(self) -> SystemVerilogVersion:
1546 if self._verilogVersion is not None:
1547 return self._verilogVersion
1548 elif self._project is not None: 1548 ↛ 1551line 1548 didn't jump to line 1551 because the condition on line 1548 was always true
1549 return self._project.VerilogVersion
1550 else:
1551 raise Exception("VerilogVersion was neither set locally nor globally.")
1553 @VerilogVersion.setter
1554 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1555 self._verilogVersion = value
1557 @property
1558 def SVVersion(self) -> SystemVerilogVersion:
1559 if self._svVersion is not None:
1560 return self._svVersion
1561 elif self._project is not None: 1561 ↛ 1564line 1561 didn't jump to line 1564 because the condition on line 1561 was always true
1562 return self._project.SVVersion
1563 else:
1564 raise Exception("SVVersion was neither set locally nor globally.")
1566 @SVVersion.setter
1567 def SVVersion(self, value: SystemVerilogVersion) -> None:
1568 self._svVersion = value
1570 @property
1571 def SRDLVersion(self) -> SystemRDLVersion:
1572 if self._srdlVersion is not None:
1573 return self._srdlVersion
1574 elif self._project is not None:
1575 return self._project.SRDLVersion
1576 else:
1577 raise Exception("SRDLVersion was neither set locally nor globally.")
1579 @SRDLVersion.setter
1580 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1581 self._srdlVersion = value
1583 @property
1584 def ExternalVHDLLibraries(self) -> List:
1585 return self._externalVHDLLibraries
1587 def AddFileSet(self, fileSet: FileSet) -> None:
1588 if not isinstance(fileSet, FileSet):
1589 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.")
1590 elif fileSet in self._fileSets:
1591 raise Exception("Design already contains this fileset.")
1592 elif fileSet.Name in self._fileSets.keys():
1593 raise Exception(f"Design already contains a fileset named '{fileSet.Name}'.")
1595 self._fileSets[fileSet.Name] = fileSet
1596 fileSet.Design = self
1597 fileSet._parent = self
1599 def AddFileSets(self, fileSets: Iterable[FileSet]) -> None:
1600 for fileSet in fileSets:
1601 self.AddFileSet(fileSet)
1603 @property
1604 def FileSetCount(self) -> int:
1605 """Returns number of file sets excl. sub-filesets."""
1606 return len(self._fileSets)
1608 @property
1609 def TotalFileSetCount(self) -> int:
1610 """Returns number of file sets incl. sub-filesets."""
1611 fileSetCount = len(self._fileSets)
1612 for fileSet in self._fileSets.values():
1613 fileSetCount += fileSet.TotalFileSetCount
1615 return fileSetCount
1617 def AddFile(self, file: File) -> None:
1618 if file.FileSet is None: 1618 ↛ 1621line 1618 didn't jump to line 1621 because the condition on line 1618 was always true
1619 self._defaultFileSet.AddFile(file)
1620 else:
1621 raise ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}' and can't be assigned via Design to a default fileset.")
1623 def AddFiles(self, files: Iterable[File]) -> None:
1624 for file in files:
1625 self.AddFile(file)
1627 def AddVHDLLibrary(self, vhdlLibrary: VHDLLibrary) -> None:
1628 if vhdlLibrary.Name in self._vhdlLibraries:
1629 if self._vhdlLibraries[vhdlLibrary.Name] is vhdlLibrary:
1630 raise Exception(f"The VHDLLibrary '{vhdlLibrary.Name}' was already added to the design.")
1631 else:
1632 raise Exception(f"A VHDLLibrary with same name ('{vhdlLibrary.Name}') already exists for this design.")
1635 def __len__(self) -> int:
1636 """
1637 Returns number of attributes set on this design.
1639 :returns: The number of attributes set on this design.
1640 """
1641 return len(self._attributes)
1643 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1644 """Index access for returning attributes on this design.
1646 :param key: The attribute type.
1647 :returns: The attribute's value.
1648 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1649 """
1650 if not issubclass(key, Attribute): 1650 ↛ 1651line 1650 didn't jump to line 1651 because the condition on line 1650 was never true
1651 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1653 try:
1654 return self._attributes[key]
1655 except KeyError:
1656 return key.resolve(self, key)
1658 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1659 """
1660 Index access for adding or setting attributes on this design.
1662 :param key: The attribute type.
1663 :param value: The attributes value.
1664 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1665 """
1666 if not issubclass(key, Attribute): 1666 ↛ 1667line 1666 didn't jump to line 1667 because the condition on line 1666 was never true
1667 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1669 self._attributes[key] = value
1671 def __delitem__(self, key: Type[Attribute]) -> None:
1672 """
1673 Index access for deleting attributes on this design.
1675 :param key: The attribute type.
1676 """
1677 if not issubclass(key, Attribute): 1677 ↛ 1678line 1677 didn't jump to line 1678 because the condition on line 1677 was never true
1678 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1680 del self._attributes[key]
1682 def __str__(self) -> str:
1683 return self._name
1686@export
1687class Project(metaclass=ExtendedType, slots=True):
1688 """
1689 A :term:`Project` represents a group of designs and the source files therein.
1691 :arg name: The project's name.
1692 :arg rootDirectory: Base-path to the project.
1693 :arg vhdlVersion: Default VHDL version for files in this project, if not specified for the file itself.
1694 :arg verilogVersion: Default Verilog version for files in this project, if not specified for the file itself.
1695 :arg svVersion: Default SystemVerilog version for files in this project, if not specified for the file itself.
1696 """
1698 _name: str
1699 _rootDirectory: pathlib_Path
1700 _designs: Dict[str, Design]
1701 _defaultDesign: Design
1702 _attributes: Dict[Type[Attribute], typing_Any]
1704 _vhdlVersion: VHDLVersion
1705 _verilogVersion: SystemVerilogVersion
1706 _svVersion: SystemVerilogVersion
1707 _srdlVersion: SystemRDLVersion
1709 def __init__(
1710 self,
1711 name: str,
1712 rootDirectory: pathlib_Path = pathlib_Path("."),
1713 vhdlVersion: Nullable[VHDLVersion] = None,
1714 verilogVersion: Nullable[SystemVerilogVersion] = None,
1715 svVersion: Nullable[SystemVerilogVersion] = None
1716 ) -> None:
1717 self._name = name
1718 self._rootDirectory = rootDirectory
1719 self._designs = {}
1720 self._defaultDesign = Design("default", project=self)
1721 self._attributes = {}
1722 self._vhdlVersion = vhdlVersion
1723 self._verilogVersion = verilogVersion
1724 self._svVersion = svVersion
1726 @property
1727 def Name(self) -> str:
1728 """Property setting or returning the project's name."""
1729 return self._name
1731 @property
1732 def RootDirectory(self) -> pathlib_Path:
1733 """Property setting or returning the root directory this project is located in."""
1734 return self._rootDirectory
1736 @RootDirectory.setter
1737 def RootDirectory(self, value: pathlib_Path) -> None:
1738 self._rootDirectory = value
1740 @property
1741 def ResolvedPath(self) -> pathlib_Path:
1742 """Read-only property returning the resolved path of this fileset."""
1743 path = self._rootDirectory.resolve()
1744 if self._rootDirectory.is_absolute():
1745 return path
1746 else:
1747 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
1748 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
1750 # TODO: return generator with another method
1751 @property
1752 def Designs(self) -> Dict[str, Design]:
1753 return self._designs
1755 @property
1756 def DefaultDesign(self) -> Design:
1757 return self._defaultDesign
1759 def Validate(self) -> None:
1760 """Validate this project."""
1761 if self._name is None or self._name == "": 1761 ↛ 1762line 1761 didn't jump to line 1762 because the condition on line 1761 was never true
1762 raise Exception("Validation: Project has no name.")
1764 if self._rootDirectory is None: 1764 ↛ 1765line 1764 didn't jump to line 1765 because the condition on line 1764 was never true
1765 raise Exception(f"Validation: Project '{self._name}' has no root directory.")
1766 try:
1767 path = self.ResolvedPath
1768 except Exception as ex:
1769 raise Exception(f"Validation: Project '{self._name}' could not compute resolved path.") from ex
1770 if not path.exists(): 1770 ↛ 1771line 1770 didn't jump to line 1771 because the condition on line 1770 was never true
1771 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' does not exist.")
1772 if not path.is_dir(): 1772 ↛ 1773line 1772 didn't jump to line 1773 because the condition on line 1772 was never true
1773 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' is not a directory.")
1775 if len(self._designs) == 0: 1775 ↛ 1776line 1775 didn't jump to line 1776 because the condition on line 1775 was never true
1776 raise Exception(f"Validation: Project '{self._name}' has no design.")
1777 try:
1778 if self._defaultDesign is not self._designs[self._defaultDesign.Name]: 1778 ↛ 1779line 1778 didn't jump to line 1779 because the condition on line 1778 was never true
1779 raise Exception(f"Validation: Project '{self._name}'s default design is the same as listed in designs.")
1780 except KeyError as ex:
1781 raise Exception(f"Validation: Project '{self._name}'s default design is not in list of designs.") from ex
1783 for design in self._designs.values():
1784 design.Validate()
1786 @property
1787 def DesignCount(self) -> int:
1788 """Returns number of designs."""
1789 return len(self._designs)
1791 @property
1792 def VHDLVersion(self) -> VHDLVersion:
1793 # TODO: check for None and return exception
1794 return self._vhdlVersion
1796 @VHDLVersion.setter
1797 def VHDLVersion(self, value: VHDLVersion) -> None:
1798 self._vhdlVersion = value
1800 @property
1801 def VerilogVersion(self) -> SystemVerilogVersion:
1802 # TODO: check for None and return exception
1803 return self._verilogVersion
1805 @VerilogVersion.setter
1806 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1807 self._verilogVersion = value
1809 @property
1810 def SVVersion(self) -> SystemVerilogVersion:
1811 # TODO: check for None and return exception
1812 return self._svVersion
1814 @SVVersion.setter
1815 def SVVersion(self, value: SystemVerilogVersion) -> None:
1816 self._svVersion = value
1818 @property
1819 def SRDLVersion(self) -> SystemRDLVersion:
1820 # TODO: check for None and return exception
1821 return self._srdlVersion
1823 @SRDLVersion.setter
1824 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1825 self._srdlVersion = value
1827 def __len__(self) -> int:
1828 """
1829 Returns number of attributes set on this project.
1831 :returns: The number of attributes set on this project.
1832 """
1833 return len(self._attributes)
1835 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1836 """Index access for returning attributes on this project.
1838 :param key: The attribute type.
1839 :returns: The attribute's value.
1840 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1841 """
1842 if not issubclass(key, Attribute): 1842 ↛ 1843line 1842 didn't jump to line 1843 because the condition on line 1842 was never true
1843 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1845 try:
1846 return self._attributes[key]
1847 except KeyError:
1848 return key.resolve(self, key)
1850 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1851 """
1852 Index access for adding or setting attributes on this project.
1854 :param key: The attribute type.
1855 :param value: The attributes value.
1856 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1857 """
1858 if not issubclass(key, Attribute): 1858 ↛ 1859line 1858 didn't jump to line 1859 because the condition on line 1858 was never true
1859 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1861 self._attributes[key] = value
1863 def __delitem__(self, key: Type[Attribute]) -> None:
1864 """
1865 Index access for deleting attributes on this project.
1867 :param key: The attribute type.
1868 """
1869 if not issubclass(key, Attribute): 1869 ↛ 1870line 1869 didn't jump to line 1870 because the condition on line 1869 was never true
1870 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1872 del self._attributes[key]
1874 def __str__(self) -> str:
1875 return self._name