Vivado.py

Summary

Lines:

114 of 128 covered (89.1%)

Branches:

38 of 46 taken (82.6%), 4 lines partially

Coverage:

87.4%

Directory:

Xilinx

Missed lines:

85-86, 119, 123-124, 158-163, 183, 186, 192, 195

Source

pyEDAA/ProjectModel/Xilinx/Vivado.pycoveredpartialuncoveredexcludednot executable
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#                                                                                                                      #
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"""Specific file types and attributes for Xilinx Vivado."""
33from pathlib               import Path
34from typing                import Iterable, Optional as Nullable
35from xml.dom               import minidom, Node
36
37from pyTooling.Decorators  import export
38from pyTooling.MetaClasses import ExtendedType
39from pyVHDLModel           import VHDLVersion
40
41from pyEDAA.ProjectModel   import ProjectFile, XMLFile, XMLContent, SDCContent, Project, FileSet, Attribute, Design, \
42                                  File as Model_File, ConstraintFile as Model_ConstraintFile, \
43                                  VerilogSourceFile as Model_VerilogSourceFile, VHDLSourceFile as Model_VHDLSourceFile
44
45
46@export
47class UsedInAttribute(Attribute):
48	KEY = "UsedIn"
49	VALUE_TYPE = Iterable[str]
50
51
52@export
53class ScopeToRefAttribute(Attribute):
54	KEY = "ScopeToRef"
55	VALUE_TYPE = Nullable[str]
56
57
58@export
59class ScopeToCellAttribute(Attribute):
60	KEY = "ScopeToCell"
61	VALUE_TYPE = Nullable[str]
62
63
64@export
65class File(Model_File):
66	pass
67
68
69@export
70class VivadoFileMixIn(metaclass=ExtendedType, mixin=True):
71	def _registerAttributes(self) -> None:
72		self._attributes[UsedInAttribute] = []
73
74
75@export
76class ConstraintFile(Model_ConstraintFile, VivadoFileMixIn):
77	def _registerAttributes(self) -> None:
78		super()._registerAttributes()
79		VivadoFileMixIn._registerAttributes(self)
80
81
82@export
83class VerilogSourceFile(Model_VerilogSourceFile):
84	def _registerAttributes(self) -> None:
85		super()._registerAttributes()
86		VivadoFileMixIn._registerAttributes(self)
87
88
89@export
90class VHDLSourceFile(Model_VHDLSourceFile):
91	def _registerAttributes(self) -> None:
92		super()._registerAttributes()
93		VivadoFileMixIn._registerAttributes(self)
94
95
96@export
97class VivadoProjectFile(ProjectFile, XMLContent):
98	"""A Vivado project file (``*.xpr``)."""
99
100	_xprProject: Project
101
102	def __init__(
103		self,
104		path: Path,
105		project: Nullable[Project] = None,
106		design:  Nullable[Design] =  None,
107		fileSet: Nullable[FileSet] = None
108	) -> None:
109		super().__init__(path, project, design, fileSet)
110
111		self._xprProject = None
112
113	@property
114	def ProjectModel(self) -> Project:
115		return self._xprProject
116
117	def Parse(self) -> None:
118		if not self._path.exists():
119			raise Exception(f"Vivado project file '{self._path!s}' not found.") from FileNotFoundError(f"File '{self._path!s}' not found.")
120
121		try:
122			root = minidom.parse(str(self._path)).documentElement
123		except Exception as ex:
124			raise Exception(f"Couldn't open '{self._path!s}'.") from ex
125
126		self._xprProject = Project(self._path.stem, rootDirectory=self._path.parent)
127		self._ParseRootElement(root)
128
129	def _ParseRootElement(self, root) -> None:
130		for rootNode in root.childNodes:
131			if rootNode.nodeName == "FileSets":
132				self._ParseFileSets(rootNode)
133				break
134
135	def _ParseFileSets(self, filesetsNode) -> None:
136		for fileSetsNode in filesetsNode.childNodes:
137			if fileSetsNode.nodeType == Node.ELEMENT_NODE and fileSetsNode.tagName == "FileSet":
138				self._ParseFileSet(fileSetsNode)
139
140	def _ParseFileSet(self, filesetNode) -> None:
141		filesetName = filesetNode.getAttribute("Name")
142		fileset = FileSet(filesetName, design=self._xprProject.DefaultDesign)
143
144		for fileNode in filesetNode.childNodes:
145			if fileNode.nodeType == Node.ELEMENT_NODE:
146				if fileNode.tagName == "File":
147					self._ParseFile(fileNode, fileset)
148				elif fileNode.nodeType == Node.ELEMENT_NODE and fileNode.tagName == "Config":
149					self._ParseFileSetConfig(fileNode, fileset)
150
151	def _ParseFile(self, fileNode, fileset) -> None:
152		croppedPath = fileNode.getAttribute("Path").replace("$PPRDIR/", "")
153		filePath = Path(croppedPath)
154		if filePath.suffix in (".vhd", ".vhdl"):
155			self._ParseVHDLFile(fileNode, filePath, fileset)
156		elif filePath.suffix == ".xdc":
157			self._ParseXDCFile(fileNode, filePath, fileset)
158		elif filePath.suffix == ".v":
159			self._ParseVerilogFile(fileNode, filePath, fileset)
160		elif filePath.suffix == ".xci":
161			self._ParseXCIFile(fileNode, filePath, fileset)
162		else:
163			self._ParseDefaultFile(fileNode, filePath, fileset)
164
165	def _ParseVHDLFile(self, fileNode, path, fileset) -> None:
166		vhdlFile = VHDLSourceFile(path)
167		fileset.AddFile(vhdlFile)
168		usedInAttr = vhdlFile[UsedInAttribute]
169
170		for childNode in fileNode.childNodes:
171			if childNode.nodeType == Node.ELEMENT_NODE and childNode.tagName == "FileInfo":
172				if childNode.getAttribute("SFType") == "VHDL2008":
173					vhdlFile.VHDLVersion = VHDLVersion.VHDL2008
174				else:
175					vhdlFile.VHDLVersion = VHDLVersion.VHDL93
176
177				for fileAttribute in childNode.childNodes:
178					if fileAttribute.nodeType == Node.ELEMENT_NODE and fileAttribute.tagName == "Attr":
179						if fileAttribute.getAttribute("Name") == "Library":
180							libraryName = fileAttribute.getAttribute("Val")
181							vhdlFile.VHDLLibrary = fileset.GetOrCreateVHDLLibrary(libraryName)
182						elif fileAttribute.getAttribute("Val") == "UsedIn":
183							usedInAttr.append(fileAttribute.getAttribute("Val"))
184
185	def _ParseDefaultFile(self, _, path, fileset) -> None:
186		File(path, fileSet=fileset)
187
188	def _ParseXDCFile(self, _, path, fileset) -> None:
189		XDCConstraintFile(path, fileSet=fileset)
190
191	def _ParseVerilogFile(self, _, path, fileset) -> None:
192		VerilogSourceFile(path, fileSet=fileset)
193
194	def _ParseXCIFile(self, _, path, fileset) -> None:
195		IPCoreInstantiationFile(path, fileSet=fileset)
196
197	def _ParseFileSetConfig(self, fileNode, fileset) -> None:
198		for option in fileNode.childNodes:
199			if option.nodeType == Node.ELEMENT_NODE and option.tagName == "Option":
200				if option.getAttribute("Name") == "TopModule":
201					fileset.TopLevel = option.getAttribute("Val")
202
203
204@export
205class XDCConstraintFile(ConstraintFile, SDCContent):
206	"""A Vivado constraint file (Xilinx Design Constraints; ``*.xdc``)."""
207
208	def _registerAttributes(self) -> None:
209		super()._registerAttributes()
210		self._attributes[ScopeToRefAttribute] =  None
211		self._attributes[ScopeToCellAttribute] = None
212
213
214@export
215class IPCoreDescriptionFile(XMLFile):
216	pass
217
218
219@export
220class IPCoreInstantiationFile(XMLFile):
221	"""A Vivado IP core instantiation file (Xilinx IPCore Instance; ``*.xci``)."""