Coverage for pyEDAA/ProjectModel/Xilinx/Vivado.py: 88%
133 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-18 17:08 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-18 17:08 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ _ __ __ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ |__/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2025 Patrick Lehmann - Boetzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""Specific file types and attributes for Xilinx Vivado."""
32from pathlib import Path
33from typing import Iterable, Optional as Nullable
34from xml.dom import minidom, Node
36from pyTooling.Decorators import export
37from pyTooling.MetaClasses import ExtendedType
38from pyVHDLModel import VHDLVersion
40from pyEDAA.ProjectModel import ProjectFile, XMLFile, XMLContent, SDCContent, Project, FileSet, Attribute, Design
41from pyEDAA.ProjectModel import File as Model_File
42from pyEDAA.ProjectModel import ConstraintFile as Model_ConstraintFile
43from pyEDAA.ProjectModel import VerilogSourceFile as Model_VerilogSourceFile
44from pyEDAA.ProjectModel import VHDLSourceFile as Model_VHDLSourceFile
47@export
48class UsedInAttribute(Attribute):
49 KEY = "UsedIn"
50 VALUE_TYPE = Iterable[str]
53@export
54class ScopeToRefAttribute(Attribute):
55 KEY = "ScopeToRef"
56 VALUE_TYPE = Nullable[str]
59@export
60class ScopeToCellAttribute(Attribute):
61 KEY = "ScopeToCell"
62 VALUE_TYPE = Nullable[str]
65@export
66class File(Model_File):
67 pass
70@export
71class VivadoFileMixIn(metaclass=ExtendedType, mixin=True):
72 def _registerAttributes(self) -> None:
73 self._attributes[UsedInAttribute] = []
76@export
77class ConstraintFile(Model_ConstraintFile, VivadoFileMixIn):
78 def _registerAttributes(self) -> None:
79 super()._registerAttributes()
80 VivadoFileMixIn._registerAttributes(self)
83@export
84class VerilogSourceFile(Model_VerilogSourceFile):
85 def _registerAttributes(self) -> None:
86 super()._registerAttributes()
87 VivadoFileMixIn._registerAttributes(self)
90@export
91class VHDLSourceFile(Model_VHDLSourceFile):
92 def _registerAttributes(self) -> None:
93 super()._registerAttributes()
94 VivadoFileMixIn._registerAttributes(self)
97@export
98class VivadoProjectFile(ProjectFile, XMLContent):
99 """A Vivado project file (``*.xpr``)."""
101 _xprProject: Project
103 def __init__(
104 self,
105 path: Path,
106 project: Nullable[Project] = None,
107 design: Nullable[Design] = None,
108 fileSet: Nullable[FileSet] = None
109 ) -> None:
110 super().__init__(path, project, design, fileSet)
112 self._xprProject = None
114 @property
115 def ProjectModel(self) -> Project:
116 return self._xprProject
118 def Parse(self) -> None:
119 if not self._path.exists(): 119 ↛ 120line 119 didn't jump to line 120 because the condition on line 119 was never true
120 raise Exception(f"Vivado project file '{self._path!s}' not found.") from FileNotFoundError(f"File '{self._path!s}' not found.")
122 try:
123 root = minidom.parse(str(self._path)).documentElement
124 except Exception as ex:
125 raise Exception(f"Couldn't open '{self._path!s}'.") from ex
127 self._xprProject = Project(self._path.stem, rootDirectory=self._path.parent)
128 self._ParseRootElement(root)
130 def _ParseRootElement(self, root) -> None:
131 for rootNode in root.childNodes: 131 ↛ exitline 131 didn't return from function '_ParseRootElement' because the loop on line 131 didn't complete
132 if rootNode.nodeName == "FileSets":
133 self._ParseFileSets(rootNode)
134 break
136 def _ParseFileSets(self, filesetsNode) -> None:
137 for fileSetsNode in filesetsNode.childNodes:
138 if fileSetsNode.nodeType == Node.ELEMENT_NODE and fileSetsNode.tagName == "FileSet":
139 self._ParseFileSet(fileSetsNode)
141 def _ParseFileSet(self, filesetNode) -> None:
142 filesetName = filesetNode.getAttribute("Name")
143 fileset = FileSet(filesetName, design=self._xprProject.DefaultDesign)
145 for fileNode in filesetNode.childNodes:
146 if fileNode.nodeType == Node.ELEMENT_NODE:
147 if fileNode.tagName == "File":
148 self._ParseFile(fileNode, fileset)
149 elif fileNode.nodeType == Node.ELEMENT_NODE and fileNode.tagName == "Config":
150 self._ParseFileSetConfig(fileNode, fileset)
152 def _ParseFile(self, fileNode, fileset) -> None:
153 croppedPath = fileNode.getAttribute("Path").replace("$PPRDIR/", "")
154 filePath = Path(croppedPath)
155 if filePath.suffix in (".vhd", ".vhdl"):
156 self._ParseVHDLFile(fileNode, filePath, fileset)
157 elif filePath.suffix == ".xdc": 157 ↛ 159line 157 didn't jump to line 159 because the condition on line 157 was always true
158 self._ParseXDCFile(fileNode, filePath, fileset)
159 elif filePath.suffix == ".v":
160 self._ParseVerilogFile(fileNode, filePath, fileset)
161 elif filePath.suffix == ".xci":
162 self._ParseXCIFile(fileNode, filePath, fileset)
163 else:
164 self._ParseDefaultFile(fileNode, filePath, fileset)
166 def _ParseVHDLFile(self, fileNode, path, fileset) -> None:
167 vhdlFile = VHDLSourceFile(path)
168 fileset.AddFile(vhdlFile)
169 usedInAttr = vhdlFile[UsedInAttribute]
171 for childNode in fileNode.childNodes:
172 if childNode.nodeType == Node.ELEMENT_NODE and childNode.tagName == "FileInfo":
173 if childNode.getAttribute("SFType") == "VHDL2008":
174 vhdlFile.VHDLVersion = VHDLVersion.VHDL2008
175 else:
176 vhdlFile.VHDLVersion = VHDLVersion.VHDL93
178 for fileAttribute in childNode.childNodes:
179 if fileAttribute.nodeType == Node.ELEMENT_NODE and fileAttribute.tagName == "Attr":
180 if fileAttribute.getAttribute("Name") == "Library":
181 libraryName = fileAttribute.getAttribute("Val")
182 vhdlFile.VHDLLibrary = fileset.GetOrCreateVHDLLibrary(libraryName)
183 elif fileAttribute.getAttribute("Val") == "UsedIn": 183 ↛ 184line 183 didn't jump to line 184 because the condition on line 183 was never true
184 usedInAttr.append(fileAttribute.getAttribute("Val"))
186 def _ParseDefaultFile(self, _, path, fileset) -> None:
187 File(path, fileSet=fileset)
189 def _ParseXDCFile(self, _, path, fileset) -> None:
190 XDCConstraintFile(path, fileSet=fileset)
192 def _ParseVerilogFile(self, _, path, fileset) -> None:
193 VerilogSourceFile(path, fileSet=fileset)
195 def _ParseXCIFile(self, _, path, fileset) -> None:
196 IPCoreInstantiationFile(path, fileSet=fileset)
198 def _ParseFileSetConfig(self, fileNode, fileset) -> None:
199 for option in fileNode.childNodes:
200 if option.nodeType == Node.ELEMENT_NODE and option.tagName == "Option":
201 if option.getAttribute("Name") == "TopModule":
202 fileset.TopLevel = option.getAttribute("Val")
205@export
206class XDCConstraintFile(ConstraintFile, SDCContent):
207 """A Vivado constraint file (Xilinx Design Constraints; ``*.xdc``)."""
209 def _registerAttributes(self) -> None:
210 super()._registerAttributes()
211 self._attributes[ScopeToRefAttribute] = None
212 self._attributes[ScopeToCellAttribute] = None
215@export
216class IPCoreDescriptionFile(XMLFile):
217 pass
220@export
221class IPCoreInstantiationFile(XMLFile):
222 """A Vivado IP core instantiation file (Xilinx IPCore Instance; ``*.xci``)."""