__init__.py
Summary
- Lines:
763 of 1018 covered (75.0%)
- Branches:
179 of 352 taken (50.9%), 81 lines partially
- Coverage:
68.8%
- Directory:
- Missed lines:
72, 142, 186, 194-197, 225-226, 241, 244-245, 247, 249, 252, 254, 256, 264, 274, 281-284, 295, 306, 311, 427-438, 445-447, 457-458, 461-462, 494, 571-573, 578-583, 587, 753, 758, 779, 790-791, 806, 809, 812-816, 823, 828, 850-852, 855-856, 861-871, 880, 882, 884, 901, 906-910, 925-927, 945, 950-954, 959, 962, 965-966, 968, 970, 973, 975, 978, 980, 1001-1004, 1020, 1036, 1052, 1060-1067, 1071, 1079, 1089, 1105, 1116, 1122, 1161, 1171, 1195, 1199, 1212-1232, 1237-1238, 1248, 1255, 1259-1261, 1266-1270, 1275, 1283, 1292-1298, 1308-1311, 1319-1322, 1326, 1403, 1408, 1412, 1436, 1444-1447, 1456-1467, 1490-1493, 1501, 1504, 1507-1508, 1510, 1512, 1515, 1518-1520, 1522, 1538, 1551, 1564, 1572-1577, 1581, 1585, 1588-1597, 1600-1601, 1606, 1611-1615, 1621, 1624-1625, 1628-1632, 1641, 1651, 1667, 1678, 1683, 1762, 1765, 1768-1769, 1771, 1773, 1776, 1779-1781, 1789, 1821, 1825, 1833, 1843, 1847-1848, 1859, 1870, 1875
Source
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ _ __ __ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ |__/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) #
15# Copyright 2017-2020 Patrick Lehmann - Boetzingen, Germany #
16# Copyright 2014-2016 Technische Universitaet Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture #
17# #
18# Licensed under the Apache License, Version 2.0 (the "License"); #
19# you may not use this file except in compliance with the License. #
20# You may obtain a copy of the License at #
21# #
22# http://www.apache.org/licenses/LICENSE-2.0 #
23# #
24# Unless required by applicable law or agreed to in writing, software #
25# distributed under the License is distributed on an "AS IS" BASIS, #
26# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
27# See the License for the specific language governing permissions and #
28# limitations under the License. #
29# #
30# SPDX-License-Identifier: Apache-2.0 #
31# ==================================================================================================================== #
32#
33"""An abstract model of EDA tool projects."""
34__author__ = "Patrick Lehmann"
35__email__ = "Paebbels@gmail.com"
36__copyright__ = "2021-2026, Electronic Design Automation Abstraction (EDA²)"
37__license__ = "Apache License, Version 2.0"
38__version__ = "0.7.0"
39__keywords__ = ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"]
40__project_url__ = "https://github.com/edaa-org/pyEDAA.ProjectModel"
41__documentation_url__ = "https://edaa-org.github.io/pyEDAA.ProjectModel"
42__issue_tracker_url__ = "https://GitHub.com/edaa-org/pyEDAA.ProjectModel/issues"
43
44from os.path import relpath as path_relpath
45from pathlib import Path as pathlib_Path
46from typing import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, \
47 Any as typing_Any, Type, Set, Self
48
49from pyTooling.Common import getFullyQualifiedName
50from pyTooling.Decorators import export
51from pyTooling.MetaClasses import ExtendedType
52from pyTooling.Graph import Graph, Vertex
53from pySVModel import SystemVerilogVersion
54from pyVHDLModel import VHDLVersion
55from pySystemRDLModel import SystemRDLVersion
56
57
58@export
59class Attribute(metaclass=ExtendedType):
60 KEY: str
61 VALUE_TYPE: typing_Any
62
63 @staticmethod
64 def resolve(obj: typing_Any, key: Type['Attribute']):
65 if isinstance(obj, File):
66 return obj._fileSet[key]
67 elif isinstance(obj, FileSet):
68 return obj._design[key]
69 elif isinstance(obj, Design):
70 return obj._project[key]
71 else:
72 raise Exception("Resolution error")
73
74
75@export
76class FileType(ExtendedType):
77 """
78 A :term:`meta-class` to construct *FileType* classes.
79
80 Modifications done by this meta-class:
81 * Register all classes of type :class:`FileType` or derived variants in a class field :attr:`FileType.FileTypes` in this meta-class.
82 """
83
84 FileTypes: Dict[str, 'FileType'] = {} #: Dictionary of all classes of type :class:`FileType` or derived variants
85 Any: 'FileType'
86
87 def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typing_Any], **kwargs) -> None:
88 super().__init__(name, bases, dictionary, **kwargs)
89 cls.Any = cls
90
91 def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs) -> Self:
92 fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs)
93 cls.FileTypes[className] = fileType
94 return fileType
95
96 def __getattr__(cls, item) -> 'FileType':
97 if item[:2] != "__" and item[-2:] != "__":
98 return cls.FileTypes[item]
99 else:
100 return super().__getattribute__(item)
101
102 def __contains__(cls, item) -> bool:
103 return issubclass(item, cls)
104
105
106@export
107class File(metaclass=FileType, slots=True):
108 """
109 A :term:`File` represents a file in a design. This :term:`base-class` is used
110 for all derived file classes.
111
112 A file can be created standalone and later associated to a fileset, design and
113 project. Or a fileset, design and/or project can be associated immediately
114 while creating a file.
115
116 :arg path: Relative or absolute path to the file.
117 :arg project: Project the file is associated with.
118 :arg design: Design the file is associated with.
119 :arg fileSet: Fileset the file is associated with.
120 """
121
122 _path: pathlib_Path
123 _fileType: 'FileType'
124 _project: Nullable['Project']
125 _design: Nullable['Design']
126 _fileSet: Nullable['FileSet']
127 _attributes: Dict[Type[Attribute], typing_Any]
128
129 def __init__(
130 self,
131 path: pathlib_Path,
132 project: Nullable["Project"] = None,
133 design: Nullable["Design"] = None,
134 fileSet: Nullable["FileSet"] = None
135 ) -> None:
136 self._fileType = getattr(FileTypes, self.__class__.__name__)
137 self._path = path
138 if project is not None:
139 self._project = project
140 self._design = design
141 if fileSet is not None:
142 self.FileSet = fileSet
143 elif design is not None:
144 self._project = design._project
145 self._design = design
146 self.FileSet = design.DefaultFileSet if fileSet is None else fileSet
147 elif fileSet is not None:
148 design = fileSet._design
149 if design is not None:
150 self._project = design._project
151 else:
152 self._project = None
153 self._design = design
154 self.FileSet = fileSet
155 else:
156 self._project = None
157 self._design = None
158 self._fileSet = None
159
160 self._attributes = {}
161 self._registerAttributes()
162
163 def _registerAttributes(self) -> None:
164 pass
165
166 @property
167 def FileType(self) -> 'FileType':
168 """Read-only property to return the file type of this file."""
169 return self._fileType
170
171 @property
172 def Path(self) -> pathlib_Path:
173 """
174 Read-only property to access the path to the file.
175
176 :returns: The file's path.
177 """
178 return self._path
179
180 # TODO: setter?
181
182 @property
183 def ResolvedPath(self) -> pathlib_Path:
184 """Read-only property returning the resolved path of this file."""
185 if self._path.is_absolute():
186 return self._path.resolve()
187 elif self._fileSet is not None:
188 path = (self._fileSet.ResolvedPath / self._path).resolve()
189
190 if path.is_absolute():
191 return path
192 else:
193 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
194 return pathlib_Path(path_relpath(path, pathlib_Path.cwd()))
195 else:
196 # TODO: message and exception type
197 raise Exception("")
198
199 @property
200 def Project(self) -> Nullable['Project']:
201 """Property setting or returning the project this file is used in."""
202 return self._project
203
204 @Project.setter
205 def Project(self, value: 'Project') -> None:
206 self._project = value
207
208 if self._fileSet is None:
209 self._project.DefaultDesign.DefaultFileSet.AddFile(self)
210
211 @property
212 def Design(self) -> Nullable['Design']:
213 """Property setting or returning the design this file is used in."""
214 return self._design
215
216 @Design.setter
217 def Design(self, value: 'Design') -> None:
218 self._design = value
219
220 if self._fileSet is None:
221 self._design.DefaultFileSet.AddFile(self)
222
223 if self._project is None:
224 self._project = value._project
225 elif self._project is not value._project:
226 raise Exception("The design's project is not identical to the already assigned project.")
227
228 @property
229 def FileSet(self) -> Nullable['FileSet']:
230 """Property setting or returning the fileset this file is used in."""
231 return self._fileSet
232
233 @FileSet.setter
234 def FileSet(self, value: 'FileSet') -> None:
235 self._fileSet = value
236 value._files.append(self)
237
238 def Validate(self) -> None:
239 """Validate this file."""
240 if self._path is None:
241 raise Exception("Validation: File has no path.")
242 try:
243 path = self.ResolvedPath
244 except Exception as ex:
245 raise Exception(f"Validation: File '{self._path}' could not compute resolved path.") from ex
246 if not path.exists():
247 raise Exception(f"Validation: File '{self._path}' (={path}) does not exist.")
248 if not path.is_file():
249 raise Exception(f"Validation: File '{self._path}' (={path}) is not a file.")
250
251 if self._fileSet is None:
252 raise Exception(f"Validation: File '{self._path}' has no fileset.")
253 if self._design is None:
254 raise Exception(f"Validation: File '{self._path}' has no design.")
255 if self._project is None:
256 raise Exception(f"Validation: File '{self._path}' has no project.")
257
258 def __len__(self) -> int:
259 """
260 Returns number of attributes set on this file.
261
262 :returns: The number of attributes set on this file.
263 """
264 return len(self._attributes)
265
266 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
267 """Index access for returning attributes on this file.
268
269 :param key: The attribute type.
270 :returns: The attribute's value.
271 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
272 """
273 if not issubclass(key, Attribute):
274 raise TypeError("Parameter 'key' is not an 'Attribute'.")
275
276 try:
277 return self._attributes[key]
278 except KeyError:
279 try:
280 return key.resolve(self, key)
281 except KeyError:
282 attribute = key()
283 self._attributes[key] = attribute
284 return attribute
285
286 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
287 """
288 Index access for adding or setting attributes on this file.
289
290 :param key: The attribute type.
291 :param value: The attributes value.
292 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
293 """
294 if not issubclass(key, Attribute):
295 raise TypeError("Parameter 'key' is not an 'Attribute'.")
296
297 self._attributes[key] = value
298
299 def __delitem__(self, key: Type[Attribute]) -> None:
300 """
301 Index access for deleting attributes on this file.
302
303 :param key: The attribute type.
304 """
305 if not issubclass(key, Attribute):
306 raise TypeError("Parameter 'key' is not an 'Attribute'.")
307
308 del self._attributes[key]
309
310 def __str__(self) -> str:
311 return f"{self._path}"
312
313
314FileTypes = File
315
316
317@export
318class HumanReadableContent(metaclass=ExtendedType, mixin=True):
319 """A file type representing human-readable contents."""
320
321
322@export
323class XMLContent(HumanReadableContent, mixin=True):
324 """A file type representing XML contents."""
325
326
327@export
328class YAMLContent(HumanReadableContent, mixin=True):
329 """A file type representing YAML contents."""
330
331
332@export
333class JSONContent(HumanReadableContent, mixin=True):
334 """A file type representing JSON contents."""
335
336
337@export
338class INIContent(HumanReadableContent, mixin=True):
339 """A file type representing INI contents."""
340
341
342@export
343class TOMLContent(HumanReadableContent, mixin=True):
344 """A file type representing TOML contents."""
345
346
347@export
348class TCLContent(HumanReadableContent, mixin=True):
349 """A file type representing content in TCL code."""
350
351
352@export
353class SDCContent(TCLContent, mixin=True):
354 """A file type representing contents as Synopsys Design Constraints (SDC)."""
355
356
357@export
358class PythonContent(HumanReadableContent, mixin=True):
359 """A file type representing contents as Python source code."""
360
361
362@export
363class TextFile(File, HumanReadableContent):
364 """A text file (``*.txt``)."""
365
366
367@export
368class LogFile(File, HumanReadableContent):
369 """A log file (``*.log``)."""
370
371
372@export
373class XMLFile(File, XMLContent):
374 """An XML file (``*.xml``)."""
375
376
377@export
378class SourceFile(File):
379 """Base-class of all source files."""
380
381
382@export
383class HDLSourceFile(SourceFile):
384 """Base-class of all HDL source files."""
385
386
387@export
388class RDLSourceFile(SourceFile):
389 """Base-class of all RDL source files."""
390
391
392@export
393class NetlistFile(SourceFile):
394 """Base-class of all netlist source files."""
395
396
397@export
398class EDIFNetlistFile(NetlistFile):
399 """Netlist file in EDIF (Electronic Design Interchange Format)."""
400
401
402@export
403class TCLSourceFile(SourceFile, TCLContent):
404 """A TCL source file."""
405
406
407@export
408class VHDLSourceFile(HDLSourceFile, HumanReadableContent):
409 """
410 A VHDL source file (of any language version).
411
412 :arg path: Relative or absolute path to the file.
413 :arg vhdlLibrary: VHDLLibrary this VHDL source file is associated wih.
414 :arg vhdlVersion: VHDLVersion this VHDL source file is associated wih.
415 :arg project: Project the file is associated with.
416 :arg design: Design the file is associated with.
417 :arg fileSet: Fileset the file is associated with.
418 """
419
420 _vhdlLibrary: Nullable['VHDLLibrary']
421 _vhdlVersion: VHDLVersion
422
423 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:
424 super().__init__(path, project, design, fileSet)
425
426 if isinstance(vhdlLibrary, str):
427 if design is not None:
428 try:
429 vhdlLibrary = design.VHDLLibraries[vhdlLibrary]
430 except KeyError as ex:
431 raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex
432 elif project is not None:
433 try:
434 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary]
435 except KeyError as ex:
436 raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex
437 else:
438 raise Exception("Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.")
439 elif isinstance(vhdlLibrary, VHDLLibrary):
440 self._vhdlLibrary = vhdlLibrary
441 vhdlLibrary.AddFile(self)
442 elif vhdlLibrary is None:
443 self._vhdlLibrary = None
444 else:
445 ex = TypeError("Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.")
446 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.")
447 raise ex
448
449 self._vhdlVersion = vhdlVersion
450
451 def Validate(self) -> None:
452 """Validate this VHDL source file."""
453 super().Validate()
454
455 try:
456 _ = self.VHDLLibrary
457 except Exception as ex:
458 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLLibrary assigned.") from ex
459 try:
460 _ = self.VHDLVersion
461 except Exception as ex:
462 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLVersion assigned.") from ex
463
464 @property
465 def VHDLLibrary(self) -> 'VHDLLibrary':
466 """Property setting or returning the VHDL library this VHDL source file is used in."""
467 if self._vhdlLibrary is not None:
468 return self._vhdlLibrary
469 elif self._fileSet is not None:
470 return self._fileSet.VHDLLibrary
471 else:
472 raise Exception("VHDLLibrary was neither set locally nor globally.")
473
474 @VHDLLibrary.setter
475 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
476 self._vhdlLibrary = value
477 value._files.append(self)
478
479 @property
480 def VHDLVersion(self) -> VHDLVersion:
481 """Property setting or returning the VHDL version this VHDL source file is used in."""
482 if self._vhdlVersion is not None:
483 return self._vhdlVersion
484 elif self._fileSet is not None:
485 return self._fileSet.VHDLVersion
486 else:
487 raise Exception("VHDLVersion was neither set locally nor globally.")
488
489 @VHDLVersion.setter
490 def VHDLVersion(self, value: VHDLVersion) -> None:
491 self._vhdlVersion = value
492
493 def __repr__(self) -> str:
494 return f"<VHDL file: '{self.ResolvedPath}'; lib: '{self.VHDLLibrary}'; version: {self.VHDLVersion}>"
495
496
497class VerilogMixIn(metaclass=ExtendedType, mixin=True):
498 @property
499 def VerilogVersion(self) -> SystemVerilogVersion:
500 """Property setting or returning the Verilog version this Verilog source file is used in."""
501 if self._version is not None:
502 return self._version
503 elif self._fileSet is not None:
504 return self._fileSet.VerilogVersion
505 else:
506 raise Exception("VerilogVersion was neither set locally nor globally.")
507
508 @VerilogVersion.setter
509 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
510 self._version = value
511
512
513class SystemVerilogMixIn(metaclass=ExtendedType, mixin=True):
514 @property
515 def SVVersion(self) -> SystemVerilogVersion:
516 """Property setting or returning the SystemVerilog version this SystemVerilog source file is used in."""
517 if self._version is not None:
518 return self._version
519 elif self._fileSet is not None:
520 return self._fileSet.SVVersion
521 else:
522 raise Exception("SVVersion was neither set locally nor globally.")
523
524 @SVVersion.setter
525 def SVVersion(self, value: SystemVerilogVersion) -> None:
526 self._version = value
527
528
529@export
530class VerilogBaseFile(HDLSourceFile, HumanReadableContent):
531 _version: SystemVerilogVersion
532
533 def __init__(self, path: pathlib_Path, version: Nullable[SystemVerilogVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None:
534 super().__init__(path, project, design, fileSet)
535
536 self._version = version
537
538
539@export
540class VerilogSourceFile(VerilogBaseFile, VerilogMixIn):
541 """A Verilog source file (of any language version)."""
542
543
544@export
545class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn):
546 """A Verilog header file (of any language version)."""
547
548
549@export
550class SystemVerilogBaseFile(VerilogBaseFile):
551 ...
552
553
554@export
555class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn):
556 """A SystemVerilog source file (of any language version)."""
557
558
559@export
560class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn):
561 """A SystemVerilog header file (of any language version)."""
562
563
564@export
565class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent):
566 """A SystemRDL source file (of any language version)."""
567
568 _srdlVersion: SystemRDLVersion
569
570 def __init__(self, path: pathlib_Path, srdlVersion: Nullable[SystemRDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None) -> None:
571 super().__init__(path, project, design, fileSet)
572
573 self._srdlVersion = srdlVersion
574
575 @property
576 def SystemRDLVersion(self) -> SystemRDLVersion:
577 """Property setting or returning the SystemRDL version this SystemRDL source file is used in."""
578 if self._srdlVersion is not None:
579 return self._srdlVersion
580 elif self._fileSet is not None:
581 return self._fileSet.SRDLVersion
582 else:
583 raise Exception("SRDLVersion was neither set locally nor globally.")
584
585 @SystemRDLVersion.setter
586 def SystemRDLVersion(self, value: SystemRDLVersion) -> None:
587 self._srdlVersion = value
588
589
590@export
591class PythonSourceFile(SourceFile, PythonContent):
592 """A Python source file."""
593
594
595# TODO: move to a Cocotb module
596@export
597class CocotbPythonFile(PythonSourceFile):
598 """A Python source file used by Cocotb."""
599
600
601@export
602class ConstraintFile(File, HumanReadableContent):
603 """Base-class of all constraint files."""
604
605
606@export
607class ProjectFile(File):
608 """Base-class of all tool-specific project files."""
609
610
611@export
612class CSourceFile(SourceFile):
613 """Base-class of all ANSI-C source files."""
614
615
616@export
617class CppSourceFile(SourceFile):
618 """Base-class of all ANSI-C++ source files."""
619
620
621@export
622class SettingFile(File):
623 """Base-class of all tool-specific setting files."""
624
625
626@export
627class SimulationAnalysisFile(File):
628 """Base-class of all tool-specific analysis files."""
629
630
631@export
632class SimulationElaborationFile(File):
633 """Base-class of all tool-specific elaboration files."""
634
635
636@export
637class SimulationStartFile(File):
638 """Base-class of all tool-specific simulation start-up files."""
639
640
641@export
642class SimulationRunFile(File):
643 """Base-class of all tool-specific simulation run (execution) files."""
644
645
646@export
647class WaveformConfigFile(File):
648 """Base-class of all tool-specific waveform configuration files."""
649
650
651@export
652class WaveformDatabaseFile(File):
653 """Base-class of all tool-specific waveform database files."""
654
655
656@export
657class WaveformExchangeFile(File):
658 """Base-class of all tool-independent waveform exchange files."""
659
660
661@export
662class FileSet(metaclass=ExtendedType, slots=True):
663 """
664 A :term:`FileSet` represents a group of files. Filesets can have sub-filesets.
665
666 The order of insertion is preserved. A fileset can be created standalone and
667 later associated to another fileset, design and/or project. Or a fileset,
668 design and/or project can be associated immediately while creating the
669 fileset.
670
671 :arg name: Name of this fileset.
672 :arg topLevel: Name of the fileset's toplevel.
673 :arg directory: Path of this fileset (absolute or relative to a parent fileset or design).
674 :arg project: Project the file is associated with.
675 :arg design: Design the file is associated with.
676 :arg parent: Parent fileset if this fileset is nested.
677 :arg vhdlLibrary: Default VHDL library for files in this fileset, if not specified for the file itself.
678 :arg vhdlVersion: Default VHDL version for files in this fileset, if not specified for the file itself.
679 :arg verilogVersion: Default Verilog version for files in this fileset, if not specified for the file itself.
680 :arg svVersion: Default SystemVerilog version for files in this fileset, if not specified for the file itself.
681 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself.
682 """
683
684 _name: str
685 _topLevel: Nullable[str]
686 _project: Nullable['Project']
687 _design: Nullable['Design']
688 _directory: pathlib_Path
689 _parent: Nullable['FileSet']
690 _fileSets: Dict[str, 'FileSet']
691 _files: List[File]
692 _set: Set
693 _attributes: Dict[Type[Attribute], typing_Any]
694 _vhdlLibraries: Dict[str, 'VHDLLibrary']
695 _vhdlLibrary: 'VHDLLibrary'
696 _vhdlVersion: VHDLVersion
697 _verilogVersion: SystemVerilogVersion
698 _svVersion: SystemVerilogVersion
699 _srdlVersion: SystemRDLVersion
700
701 def __init__(
702 self,
703 name: str,
704 topLevel: Nullable[str] = None,
705 directory: pathlib_Path = pathlib_Path("."),
706 project: Nullable["Project"] = None,
707 design: Nullable["Design"] = None,
708 parent: Nullable['FileSet'] = None,
709 vhdlLibrary: Union[str, 'VHDLLibrary'] = None,
710 vhdlVersion: Nullable[VHDLVersion] = None,
711 verilogVersion: Nullable[SystemVerilogVersion] = None,
712 svVersion: Nullable[SystemVerilogVersion] = None,
713 srdlVersion: Nullable[SystemRDLVersion] = None
714 ) -> None:
715 self._name = name
716 self._topLevel = topLevel
717 if project is not None:
718 self._project = project
719 self._design = design if design is not None else project.DefaultDesign
720
721 elif design is not None:
722 self._project = design._project
723 self._design = design
724 else:
725 self._project = None
726 self._design = None
727 self._directory = directory
728 self._parent = parent
729 self._fileSets = {}
730 self._files = []
731 self._set = set()
732
733 if design is not None:
734 design._fileSets[name] = self
735
736 self._attributes = {}
737 self._vhdlLibraries = {}
738
739 # TODO: handle if vhdlLibrary is a string
740 self._vhdlLibrary = vhdlLibrary
741 self._vhdlVersion = vhdlVersion
742 self._verilogVersion = verilogVersion
743 self._svVersion = svVersion
744 self._srdlVersion = srdlVersion
745
746 @property
747 def Name(self) -> str:
748 """Property setting or returning the fileset's name."""
749 return self._name
750
751 @Name.setter
752 def Name(self, value: str) -> None:
753 self._name = value
754
755 @property
756 def TopLevel(self) -> str:
757 """Property setting or returning the fileset's toplevel."""
758 return self._topLevel
759
760 @TopLevel.setter
761 def TopLevel(self, value: str) -> None:
762 self._topLevel = value
763
764 @property
765 def Project(self) -> Nullable['Project']:
766 """Property setting or returning the project this fileset is used in."""
767 return self._project
768
769 @Project.setter
770 def Project(self, value: 'Project') -> None:
771 self._project = value
772
773 @property
774 def Design(self) -> Nullable['Design']:
775 """Property setting or returning the design this fileset is used in."""
776 if self._design is not None:
777 return self._design
778 elif self._parent is not None:
779 return self._parent.Design
780 else:
781 return None
782 # TODO: raise exception instead
783 # QUESTION: how to handle if design and parent is set?
784
785 @Design.setter
786 def Design(self, value: 'Design') -> None:
787 self._design = value
788 if self._project is None:
789 self._project = value._project
790 elif self._project is not value._project:
791 raise Exception("The design's project is not identical to the already assigned project.")
792
793 @property
794 def Directory(self) -> pathlib_Path:
795 """Property setting or returning the directory this fileset is located in."""
796 return self._directory
797
798 @Directory.setter
799 def Directory(self, value: pathlib_Path) -> None:
800 self._directory = value
801
802 @property
803 def ResolvedPath(self) -> pathlib_Path:
804 """Read-only property returning the resolved path of this fileset."""
805 if self._directory.is_absolute():
806 return self._directory.resolve()
807 else:
808 if self._parent is not None:
809 directory = self._parent.ResolvedPath
810 elif self._design is not None:
811 directory = self._design.ResolvedPath
812 elif self._project is not None:
813 directory = self._project.ResolvedPath
814 else:
815 # TODO: message and exception type
816 raise Exception("")
817
818 directory = (directory / self._directory).resolve()
819 if directory.is_absolute():
820 return directory
821 else:
822 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath
823 return pathlib_Path(path_relpath(directory, pathlib_Path.cwd()))
824
825 @property
826 def Parent(self) -> Nullable['FileSet']:
827 """Property setting or returning the parent fileset this fileset is used in."""
828 return self._parent
829
830 @Parent.setter
831 def Parent(self, value: 'FileSet') -> None:
832 self._parent = value
833 value._fileSets[self._name] = self
834 # TODO: check it it already exists
835 # QUESTION: make an Add fileset method?
836
837 @property
838 def FileSets(self) -> Dict[str, 'FileSet']:
839 """Read-only property returning the dictionary of sub-filesets."""
840 return self._fileSets
841
842 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'FileSet'] = None) -> Generator[File, None, None]:
843 """
844 Method returning the files of this fileset.
845
846 :arg fileType: A filter for file types. Default: ``Any``.
847 :arg fileSet: Specifies how to handle sub-filesets.
848 """
849 if fileSet is False:
850 for file in self._files:
851 if file.FileType in fileType:
852 yield file
853 elif fileSet is None:
854 for fileSet in self._fileSets.values():
855 for file in fileSet.Files(fileType):
856 yield file
857 for file in self._files:
858 if file.FileType in fileType:
859 yield file
860 else:
861 if isinstance(fileSet, str):
862 fileSetName = fileSet
863 try:
864 fileSet = self._fileSets[fileSetName]
865 except KeyError as ex:
866 raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex
867 elif not isinstance(fileSet, FileSet):
868 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.")
869
870 for file in fileSet.Files(fileType):
871 yield file
872
873 def AddFileSet(self, fileSet: "FileSet") -> None:
874 """
875 Method to add a single sub-fileset to this fileset.
876
877 :arg fileSet: A fileset to add to this fileset as sub-fileset.
878 """
879 if not isinstance(fileSet, FileSet):
880 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.")
881 elif fileSet in self._fileSets:
882 raise Exception("Sub-fileset already contains this fileset.")
883 elif fileSet.Name in self._fileSets.keys():
884 raise Exception(f"Fileset already contains a sub-fileset named '{fileSet.Name}'.")
885
886 self._fileSets[fileSet.Name] = fileSet
887 fileSet._parent = self
888
889 def AddFileSets(self, fileSets: Iterable["FileSet"]) -> None:
890 """
891 Method to add a multiple sub-filesets to this fileset.
892
893 :arg fileSets: An iterable of filesets to add each to the fileset.
894 """
895 for fileSet in fileSets:
896 self.AddFileSet(fileSet)
897
898 @property
899 def FileSetCount(self) -> int:
900 """Returns number of file sets excl. sub-filesets."""
901 return len(self._fileSets)
902
903 @property
904 def TotalFileSetCount(self) -> int:
905 """Returns number of file sets incl. sub-filesets."""
906 fileSetCount = len(self._fileSets)
907 for fileSet in self._fileSets.values():
908 fileSetCount += fileSet.TotalFileSetCount
909
910 return fileSetCount
911
912 def AddFile(self, file: File) -> None:
913 """
914 Method to add a single file to this fileset.
915
916 :arg file: A file to add to this fileset.
917 """
918 if not isinstance(file, File):
919 raise TypeError("Parameter 'file' is not of type ProjectModel.File.")
920 elif file._fileSet is not None:
921 ex = ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}'.")
922 ex.add_note("A file can't be assigned to another fileset.")
923 raise ex
924 elif file in self._set:
925 ex = ValueError(f"File '{file.Path!s}' is already part of this fileset.")
926 ex.add_note("A file can't be added twice to a fileset.")
927 raise ex
928
929 self._files.append(file)
930 self._set.add(file)
931 file._fileSet = self
932
933 def AddFiles(self, files: Iterable[File]) -> None:
934 """
935 Method to add a multiple files to this fileset.
936
937 :arg files: An iterable of files to add each to the fileset.
938 """
939 for file in files:
940 self.AddFile(file)
941
942 @property
943 def FileCount(self) -> int:
944 """Returns number of files excl. sub-filesets."""
945 return len(self._files)
946
947 @property
948 def TotalFileCount(self) -> int:
949 """Returns number of files incl. the files in sub-filesets."""
950 fileCount = len(self._files)
951 for fileSet in self._fileSets.values():
952 fileCount += fileSet.FileCount
953
954 return fileCount
955
956 def Validate(self) -> None:
957 """Validate this fileset."""
958 if self._name is None or self._name == "":
959 raise Exception("Validation: FileSet has no name.")
960
961 if self._directory is None:
962 raise Exception(f"Validation: FileSet '{self._name}' has no directory.")
963 try:
964 path = self.ResolvedPath
965 except Exception as ex:
966 raise Exception(f"Validation: FileSet '{self._name}' could not compute resolved path.") from ex
967 if not path.exists():
968 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' does not exist.")
969 if not path.is_dir():
970 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' is not a directory.")
971
972 if self._design is None:
973 raise Exception(f"Validation: FileSet '{self._directory}' has no design.")
974 if self._project is None:
975 raise Exception(f"Validation: FileSet '{self._directory}' has no project.")
976
977 for fileSet in self._fileSets.values():
978 fileSet.Validate()
979 for file in self._files:
980 file.Validate()
981
982 def GetOrCreateVHDLLibrary(self, name) -> 'VHDLLibrary':
983 if name in self._vhdlLibraries:
984 return self._vhdlLibraries[name]
985 elif name in self._design._vhdlLibraries:
986 library = self._design._vhdlLibraries[name]
987 self._vhdlLibraries[name] = library
988 return library
989 else:
990 library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion)
991 self._vhdlLibraries[name] = library
992 return library
993
994 @property
995 def VHDLLibrary(self) -> 'VHDLLibrary':
996 """Property setting or returning the VHDL library of this fileset."""
997 if self._vhdlLibrary is not None:
998 return self._vhdlLibrary
999 elif self._parent is not None:
1000 return self._parent.VHDLLibrary
1001 elif self._design is not None:
1002 return self._design.VHDLLibrary # FIXME: no library property. add a DefaultVHDLLibrary property
1003 else:
1004 raise Exception("VHDLLibrary was neither set locally nor globally.")
1005
1006 @VHDLLibrary.setter
1007 def VHDLLibrary(self, value: 'VHDLLibrary') -> None:
1008 self._vhdlLibrary = value
1009
1010 @property
1011 def VHDLVersion(self) -> VHDLVersion:
1012 """Property setting or returning the VHDL version of this fileset."""
1013 if self._vhdlVersion is not None:
1014 return self._vhdlVersion
1015 elif self._parent is not None:
1016 return self._parent.VHDLVersion
1017 elif self._design is not None:
1018 return self._design.VHDLVersion
1019 else:
1020 raise Exception("VHDLVersion was neither set locally nor globally.")
1021
1022 @VHDLVersion.setter
1023 def VHDLVersion(self, value: VHDLVersion) -> None:
1024 self._vhdlVersion = value
1025
1026 @property
1027 def VerilogVersion(self) -> SystemVerilogVersion:
1028 """Property setting or returning the Verilog version of this fileset."""
1029 if self._verilogVersion is not None:
1030 return self._verilogVersion
1031 elif self._parent is not None:
1032 return self._parent.VerilogVersion
1033 elif self._design is not None:
1034 return self._design.VerilogVersion
1035 else:
1036 raise Exception("VerilogVersion was neither set locally nor globally.")
1037
1038 @VerilogVersion.setter
1039 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1040 self._verilogVersion = value
1041
1042 @property
1043 def SVVersion(self) -> SystemVerilogVersion:
1044 """Property setting or returning the SystemVerilog version of this fileset."""
1045 if self._svVersion is not None:
1046 return self._svVersion
1047 elif self._parent is not None:
1048 return self._parent.SVVersion
1049 elif self._design is not None:
1050 return self._design.SVVersion
1051 else:
1052 raise Exception("SVVersion was neither set locally nor globally.")
1053
1054 @SVVersion.setter
1055 def SVVersion(self, value: SystemVerilogVersion) -> None:
1056 self._svVersion = value
1057
1058 @property
1059 def SRDLVersion(self) -> SystemRDLVersion:
1060 if self._srdlVersion is not None:
1061 return self._srdlVersion
1062 elif self._parent is not None:
1063 return self._parent.SRDLVersion
1064 elif self._design is not None:
1065 return self._design.SRDLVersion
1066 else:
1067 raise Exception("SRDLVersion was neither set locally nor globally.")
1068
1069 @SRDLVersion.setter
1070 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1071 self._srdlVersion = value
1072
1073 def __len__(self) -> int:
1074 """
1075 Returns number of attributes set on this fileset.
1076
1077 :returns: The number of attributes set on this fileset.
1078 """
1079 return len(self._attributes)
1080
1081 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1082 """Index access for returning attributes on this fileset.
1083
1084 :param key: The attribute type.
1085 :returns: The attribute's value.
1086 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1087 """
1088 if not issubclass(key, Attribute):
1089 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1090
1091 try:
1092 return self._attributes[key]
1093 except KeyError:
1094 return key.resolve(self, key)
1095
1096 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1097 """
1098 Index access for adding or setting attributes on this fileset.
1099
1100 :param key: The attribute type.
1101 :param value: The attributes value.
1102 :raises TypeError: When parameter 'key' is not a subclass of Attribute.
1103 """
1104 if not issubclass(key, Attribute):
1105 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1106
1107 self._attributes[key] = value
1108
1109 def __delitem__(self, key: Type[Attribute]) -> None:
1110 """
1111 Index access for deleting attributes on this fileset.
1112
1113 :param key: The attribute type.
1114 """
1115 if not issubclass(key, Attribute):
1116 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1117
1118 del self._attributes[key]
1119
1120 def __str__(self) -> str:
1121 """Returns the fileset's name."""
1122 return self._name
1123
1124
1125@export
1126class VHDLLibrary(metaclass=ExtendedType, slots=True):
1127 """
1128 A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library.
1129
1130 :arg name: The VHDL libraries' name.
1131 :arg project: Project the VHDL library is associated with.
1132 :arg design: Design the VHDL library is associated with.
1133 :arg vhdlVersion: Default VHDL version for files in this VHDL library, if not specified for the file itself.
1134 """
1135
1136 _name: str
1137 _project: Nullable['Project']
1138 _design: Nullable['Design']
1139 _files: List[File]
1140 _vhdlVersion: VHDLVersion
1141
1142 _attributes: Dict[Attribute, typing_Any]
1143 _dependencyNode: Vertex
1144
1145 def __init__(
1146 self,
1147 name: str,
1148 project: Nullable["Project"] = None,
1149 design: Nullable["Design"] = None,
1150 vhdlVersion: Nullable[VHDLVersion] = None
1151 ) -> None:
1152 self._name = name
1153 self._attributes = {}
1154
1155 if project is not None:
1156 self._project = project
1157 self._design = project._defaultDesign if design is None else design
1158 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1159
1160 if name in self._design._vhdlLibraries:
1161 raise Exception(f"Library '{name}' already in design '{self._design.Name}'.")
1162 else:
1163 self._design._vhdlLibraries[name] = self
1164
1165 elif design is not None:
1166 self._project = design._project
1167 self._design = design
1168 self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph)
1169
1170 if name in design._vhdlLibraries:
1171 raise Exception(f"Library '{name}' already in design '{design.Name}'.")
1172 else:
1173 design._vhdlLibraries[name] = self
1174
1175 else:
1176 self._project = None
1177 self._design = None
1178 self._dependencyNode = None
1179
1180 self._files = []
1181 self._vhdlVersion = vhdlVersion
1182
1183 @property
1184 def Name(self) -> str:
1185 return self._name
1186
1187 @property
1188 def Project(self) -> Nullable['Project']:
1189 """Property setting or returning the project this VHDL library is used in."""
1190 return self._project
1191
1192 @Project.setter
1193 def Project(self, value: 'Project') -> None:
1194 if not isinstance(value, Project):
1195 raise TypeError("Parameter 'value' is not of type 'Project'.")
1196
1197 if value is None:
1198 # TODO: unlink VHDLLibrary from project
1199 self._project = None
1200 else:
1201 self._project = value
1202 if self._design is None:
1203 self._design = value._defaultDesign
1204
1205 @property
1206 def Design(self) -> Nullable['Design']:
1207 """Property setting or returning the design this VHDL library is used in."""
1208 return self._design
1209
1210 @Design.setter
1211 def Design(self, value: 'Design') -> None:
1212 if not isinstance(value, Design):
1213 raise TypeError("Parameter 'value' is not of type 'Design'.")
1214
1215 if value is None:
1216 # TODO: unlink VHDLLibrary from design
1217 self._design = None
1218 else:
1219 if self._design is None:
1220 self._design = value
1221 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph)
1222 elif self._design is not value:
1223 # TODO: move VHDLLibrary to other design
1224 # TODO: create new vertex in dependency graph and remove vertex from old graph
1225 self._design = value
1226 else:
1227 pass
1228
1229 if self._project is None:
1230 self._project = value._project
1231 elif self._project is not value._project:
1232 raise Exception("The design's project is not identical to the already assigned project.")
1233
1234 @property
1235 def Files(self) -> Generator[File, None, None]:
1236 """Read-only property to return all files in this VHDL library."""
1237 for file in self._files:
1238 yield file
1239
1240 @property
1241 def VHDLVersion(self) -> VHDLVersion:
1242 """Property setting or returning the VHDL version of this VHDL library."""
1243 if self._vhdlVersion is not None:
1244 return self._vhdlVersion
1245 elif self._design is not None:
1246 return self._design.VHDLVersion
1247 else:
1248 raise Exception("VHDLVersion is not set on VHDLLibrary nor parent object.")
1249
1250 @VHDLVersion.setter
1251 def VHDLVersion(self, value: VHDLVersion) -> None:
1252 self._vhdlVersion = value
1253
1254 def AddDependency(self, library: 'VHDLLibrary') -> None:
1255 library.parent = self
1256
1257 def AddFile(self, vhdlFile: VHDLSourceFile) -> None:
1258 if not isinstance(vhdlFile, VHDLSourceFile):
1259 ex = TypeError("Parameter 'vhdlFile' is not a 'VHDLSourceFile'.")
1260 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlFile)}'.")
1261 raise ex
1262
1263 self._files.append(vhdlFile)
1264
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'.")
1269
1270 self._files.append(vhdlFile)
1271
1272 @property
1273 def FileCount(self) -> int:
1274 """Returns number of files."""
1275 return len(self._files)
1276
1277 def __len__(self) -> int:
1278 """
1279 Returns number of attributes set on this VHDL library.
1280
1281 :returns: The number of attributes set on this VHDL library.
1282 """
1283 return len(self._attributes)
1284
1285 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1286 """Index access for returning attributes on this VHDL library.
1287
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'.")
1294
1295 try:
1296 return self._attributes[key]
1297 except KeyError:
1298 return key.resolve(self, key)
1299
1300 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1301 """
1302 Index access for adding or setting attributes on this VHDL library.
1303
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'.")
1310
1311 self._attributes[key] = value
1312
1313 def __delitem__(self, key: Type[Attribute]) -> None:
1314 """
1315 Index access for deleting attributes on this VHDL library.
1316
1317 :param key: The attribute type.
1318 """
1319 if not issubclass(key, Attribute):
1320 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1321
1322 del self._attributes[key]
1323
1324 def __str__(self) -> str:
1325 """Returns the VHDL library's name."""
1326 return self._name
1327
1328
1329@export
1330class Design(metaclass=ExtendedType, slots=True):
1331 """
1332 A :term:`Design` represents a group of filesets and the source files therein.
1333
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.
1337
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 """
1347
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]
1355
1356 _vhdlLibraries: Dict[str, VHDLLibrary]
1357 _vhdlVersion: VHDLVersion
1358 _verilogVersion: SystemVerilogVersion
1359 _svVersion: SystemVerilogVersion
1360 _srdlVersion: SystemRDLVersion
1361 _externalVHDLLibraries: List
1362
1363 _vhdlLibraryDependencyGraph: Graph
1364 _fileDependencyGraph: Graph
1365
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 = []
1392
1393 self._vhdlLibraryDependencyGraph = Graph()
1394 self._fileDependencyGraph = Graph()
1395
1396 @property
1397 def Name(self) -> str:
1398 """Property setting or returning the design's name."""
1399 return self._name
1400
1401 @Name.setter
1402 def Name(self, value: str) -> None:
1403 self._name = value
1404
1405 @property
1406 def TopLevel(self) -> str:
1407 """Property setting or returning the fileset's toplevel."""
1408 return self._topLevel
1409
1410 @TopLevel.setter
1411 def TopLevel(self, value: str) -> None:
1412 self._topLevel = value
1413
1414 @property
1415 def Project(self) -> Nullable['Project']:
1416 """Property setting or returning the project this design is used in."""
1417 return self._project
1418
1419 @Project.setter
1420 def Project(self, value: 'Project') -> None:
1421 self._project = value
1422
1423 @property
1424 def Directory(self) -> pathlib_Path:
1425 """Property setting or returning the directory this design is located in."""
1426 return self._directory
1427
1428 @Directory.setter
1429 def Directory(self, value: pathlib_Path) -> None:
1430 self._directory = value
1431
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():
1436 return self._directory.resolve()
1437 elif self._project is not None:
1438 path = (self._project.ResolvedPath / self._directory).resolve()
1439
1440 if path.is_absolute():
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("")
1448
1449 @property
1450 def DefaultFileSet(self) -> FileSet:
1451 """Property setting or returning the default fileset of this design."""
1452 return self._defaultFileSet
1453
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.")
1459
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.")
1464
1465 self._defaultFileSet = value
1466 else:
1467 raise ValueError("Unsupported parameter type for 'value'.")
1468
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
1474
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.
1478
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):
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'.")
1494
1495 for file in fileSet.Files(fileType):
1496 yield file
1497
1498 def Validate(self) -> None:
1499 """Validate this design."""
1500 if self._name is None or self._name == "":
1501 raise Exception("Validation: Design has no name.")
1502
1503 if self._directory is None:
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():
1510 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' does not exist.")
1511 if not path.is_dir():
1512 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' is not a directory.")
1513
1514 if len(self._fileSets) == 0:
1515 raise Exception(f"Validation: Design '{self._name}' has no fileset.")
1516 try:
1517 if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]:
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:
1522 raise Exception(f"Validation: Design '{self._path}' has no project.")
1523
1524 for fileSet in self._fileSets.values():
1525 fileSet.Validate()
1526
1527 @property
1528 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]:
1529 return self._vhdlLibraries
1530
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:
1536 return self._project.VHDLVersion
1537 else:
1538 raise Exception("VHDLVersion was neither set locally nor globally.")
1539
1540 @VHDLVersion.setter
1541 def VHDLVersion(self, value: VHDLVersion) -> None:
1542 self._vhdlVersion = value
1543
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:
1549 return self._project.VerilogVersion
1550 else:
1551 raise Exception("VerilogVersion was neither set locally nor globally.")
1552
1553 @VerilogVersion.setter
1554 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1555 self._verilogVersion = value
1556
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:
1562 return self._project.SVVersion
1563 else:
1564 raise Exception("SVVersion was neither set locally nor globally.")
1565
1566 @SVVersion.setter
1567 def SVVersion(self, value: SystemVerilogVersion) -> None:
1568 self._svVersion = value
1569
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.")
1578
1579 @SRDLVersion.setter
1580 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1581 self._srdlVersion = value
1582
1583 @property
1584 def ExternalVHDLLibraries(self) -> List:
1585 return self._externalVHDLLibraries
1586
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}'.")
1594
1595 self._fileSets[fileSet.Name] = fileSet
1596 fileSet.Design = self
1597 fileSet._parent = self
1598
1599 def AddFileSets(self, fileSets: Iterable[FileSet]) -> None:
1600 for fileSet in fileSets:
1601 self.AddFileSet(fileSet)
1602
1603 @property
1604 def FileSetCount(self) -> int:
1605 """Returns number of file sets excl. sub-filesets."""
1606 return len(self._fileSets)
1607
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
1614
1615 return fileSetCount
1616
1617 def AddFile(self, file: File) -> None:
1618 if file.FileSet is None:
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.")
1622
1623 def AddFiles(self, files: Iterable[File]) -> None:
1624 for file in files:
1625 self.AddFile(file)
1626
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.")
1633
1634
1635 def __len__(self) -> int:
1636 """
1637 Returns number of attributes set on this design.
1638
1639 :returns: The number of attributes set on this design.
1640 """
1641 return len(self._attributes)
1642
1643 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1644 """Index access for returning attributes on this design.
1645
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):
1651 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1652
1653 try:
1654 return self._attributes[key]
1655 except KeyError:
1656 return key.resolve(self, key)
1657
1658 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1659 """
1660 Index access for adding or setting attributes on this design.
1661
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):
1667 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1668
1669 self._attributes[key] = value
1670
1671 def __delitem__(self, key: Type[Attribute]) -> None:
1672 """
1673 Index access for deleting attributes on this design.
1674
1675 :param key: The attribute type.
1676 """
1677 if not issubclass(key, Attribute):
1678 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1679
1680 del self._attributes[key]
1681
1682 def __str__(self) -> str:
1683 return self._name
1684
1685
1686@export
1687class Project(metaclass=ExtendedType, slots=True):
1688 """
1689 A :term:`Project` represents a group of designs and the source files therein.
1690
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 """
1697
1698 _name: str
1699 _rootDirectory: pathlib_Path
1700 _designs: Dict[str, Design]
1701 _defaultDesign: Design
1702 _attributes: Dict[Type[Attribute], typing_Any]
1703
1704 _vhdlVersion: VHDLVersion
1705 _verilogVersion: SystemVerilogVersion
1706 _svVersion: SystemVerilogVersion
1707 _srdlVersion: SystemRDLVersion
1708
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
1725
1726 @property
1727 def Name(self) -> str:
1728 """Property setting or returning the project's name."""
1729 return self._name
1730
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
1735
1736 @RootDirectory.setter
1737 def RootDirectory(self, value: pathlib_Path) -> None:
1738 self._rootDirectory = value
1739
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()))
1749
1750 # TODO: return generator with another method
1751 @property
1752 def Designs(self) -> Dict[str, Design]:
1753 return self._designs
1754
1755 @property
1756 def DefaultDesign(self) -> Design:
1757 return self._defaultDesign
1758
1759 def Validate(self) -> None:
1760 """Validate this project."""
1761 if self._name is None or self._name == "":
1762 raise Exception("Validation: Project has no name.")
1763
1764 if self._rootDirectory is None:
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():
1771 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' does not exist.")
1772 if not path.is_dir():
1773 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' is not a directory.")
1774
1775 if len(self._designs) == 0:
1776 raise Exception(f"Validation: Project '{self._name}' has no design.")
1777 try:
1778 if self._defaultDesign is not self._designs[self._defaultDesign.Name]:
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
1782
1783 for design in self._designs.values():
1784 design.Validate()
1785
1786 @property
1787 def DesignCount(self) -> int:
1788 """Returns number of designs."""
1789 return len(self._designs)
1790
1791 @property
1792 def VHDLVersion(self) -> VHDLVersion:
1793 # TODO: check for None and return exception
1794 return self._vhdlVersion
1795
1796 @VHDLVersion.setter
1797 def VHDLVersion(self, value: VHDLVersion) -> None:
1798 self._vhdlVersion = value
1799
1800 @property
1801 def VerilogVersion(self) -> SystemVerilogVersion:
1802 # TODO: check for None and return exception
1803 return self._verilogVersion
1804
1805 @VerilogVersion.setter
1806 def VerilogVersion(self, value: SystemVerilogVersion) -> None:
1807 self._verilogVersion = value
1808
1809 @property
1810 def SVVersion(self) -> SystemVerilogVersion:
1811 # TODO: check for None and return exception
1812 return self._svVersion
1813
1814 @SVVersion.setter
1815 def SVVersion(self, value: SystemVerilogVersion) -> None:
1816 self._svVersion = value
1817
1818 @property
1819 def SRDLVersion(self) -> SystemRDLVersion:
1820 # TODO: check for None and return exception
1821 return self._srdlVersion
1822
1823 @SRDLVersion.setter
1824 def SRDLVersion(self, value: SystemRDLVersion) -> None:
1825 self._srdlVersion = value
1826
1827 def __len__(self) -> int:
1828 """
1829 Returns number of attributes set on this project.
1830
1831 :returns: The number of attributes set on this project.
1832 """
1833 return len(self._attributes)
1834
1835 def __getitem__(self, key: Type[Attribute]) -> typing_Any:
1836 """Index access for returning attributes on this project.
1837
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):
1843 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1844
1845 try:
1846 return self._attributes[key]
1847 except KeyError:
1848 return key.resolve(self, key)
1849
1850 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None:
1851 """
1852 Index access for adding or setting attributes on this project.
1853
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):
1859 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1860
1861 self._attributes[key] = value
1862
1863 def __delitem__(self, key: Type[Attribute]) -> None:
1864 """
1865 Index access for deleting attributes on this project.
1866
1867 :param key: The attribute type.
1868 """
1869 if not issubclass(key, Attribute):
1870 raise TypeError("Parameter 'key' is not an 'Attribute'.")
1871
1872 del self._attributes[key]
1873
1874 def __str__(self) -> str:
1875 return self._name