Coverage for pyEDAA/ProjectModel/Xilinx/Vivado.py: 87%
120 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-08 22:18 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-08 22:18 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ _ __ __ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ |__/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2024 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 File(Model_File):
55 pass
58class VivadoFileMixIn(metaclass=ExtendedType, mixin=True):
59 def _registerAttributes(self) -> None:
60 self._attributes[UsedInAttribute] = []
63@export
64class ConstraintFile(Model_ConstraintFile, VivadoFileMixIn):
65 def _registerAttributes(self) -> None:
66 super()._registerAttributes()
67 VivadoFileMixIn._registerAttributes(self)
70@export
71class VerilogSourceFile(Model_VerilogSourceFile):
72 def _registerAttributes(self) -> None:
73 super()._registerAttributes()
74 VivadoFileMixIn._registerAttributes(self)
77@export
78class VHDLSourceFile(Model_VHDLSourceFile):
79 def _registerAttributes(self) -> None:
80 super()._registerAttributes()
81 VivadoFileMixIn._registerAttributes(self)
84@export
85class VivadoProjectFile(ProjectFile, XMLContent):
86 """A Vivado project file (``*.xpr``)."""
88 _xprProject: Project
90 def __init__(
91 self,
92 path: Path,
93 project: Nullable[Project] = None,
94 design: Nullable[Design] = None,
95 fileSet: Nullable[FileSet] = None
96 ) -> None:
97 super().__init__(path, project, design, fileSet)
99 self._xprProject = None
101 @property
102 def ProjectModel(self) -> Project:
103 return self._xprProject
105 def Parse(self) -> None:
106 if not self._path.exists(): 106 ↛ 107line 106 didn't jump to line 107 because the condition on line 106 was never true
107 raise Exception(f"Vivado project file '{self._path!s}' not found.") from FileNotFoundError(f"File '{self._path!s}' not found.")
109 try:
110 root = minidom.parse(str(self._path)).documentElement
111 except Exception as ex:
112 raise Exception(f"Couldn't open '{self._path!s}'.") from ex
114 self._xprProject = Project(self._path.stem, rootDirectory=self._path.parent)
115 self._ParseRootElement(root)
117 def _ParseRootElement(self, root) -> None:
118 for rootNode in root.childNodes: 118 ↛ exitline 118 didn't return from function '_ParseRootElement' because the loop on line 118 didn't complete
119 if rootNode.nodeName == "FileSets":
120 self._ParseFileSets(rootNode)
121 break
123 def _ParseFileSets(self, filesetsNode) -> None:
124 for fileSetsNode in filesetsNode.childNodes:
125 if fileSetsNode.nodeType == Node.ELEMENT_NODE and fileSetsNode.tagName == "FileSet":
126 self._ParseFileSet(fileSetsNode)
128 def _ParseFileSet(self, filesetNode) -> None:
129 filesetName = filesetNode.getAttribute("Name")
130 fileset = FileSet(filesetName, design=self._xprProject.DefaultDesign)
132 for fileNode in filesetNode.childNodes:
133 if fileNode.nodeType == Node.ELEMENT_NODE:
134 if fileNode.tagName == "File":
135 self._ParseFile(fileNode, fileset)
136 elif fileNode.nodeType == Node.ELEMENT_NODE and fileNode.tagName == "Config":
137 self._ParseFileSetConfig(fileNode, fileset)
139 def _ParseFile(self, fileNode, fileset) -> None:
140 croppedPath = fileNode.getAttribute("Path").replace("$PPRDIR/", "")
141 filePath = Path(croppedPath)
142 if filePath.suffix in (".vhd", ".vhdl"):
143 self._ParseVHDLFile(fileNode, filePath, fileset)
144 elif filePath.suffix == ".xdc": 144 ↛ 146line 144 didn't jump to line 146 because the condition on line 144 was always true
145 self._ParseXDCFile(fileNode, filePath, fileset)
146 elif filePath.suffix == ".v":
147 self._ParseVerilogFile(fileNode, filePath, fileset)
148 elif filePath.suffix == ".xci":
149 self._ParseXCIFile(fileNode, filePath, fileset)
150 else:
151 self._ParseDefaultFile(fileNode, filePath, fileset)
153 def _ParseVHDLFile(self, fileNode, path, fileset) -> None:
154 vhdlFile = VHDLSourceFile(path)
155 fileset.AddFile(vhdlFile)
156 usedInAttr = vhdlFile[UsedInAttribute]
158 for childNode in fileNode.childNodes:
159 if childNode.nodeType == Node.ELEMENT_NODE and childNode.tagName == "FileInfo":
160 if childNode.getAttribute("SFType") == "VHDL2008":
161 vhdlFile.VHDLVersion = VHDLVersion.VHDL2008
162 else:
163 vhdlFile.VHDLVersion = VHDLVersion.VHDL93
165 for fileAttribute in childNode.childNodes:
166 if fileAttribute.nodeType == Node.ELEMENT_NODE and fileAttribute.tagName == "Attr":
167 if fileAttribute.getAttribute("Name") == "Library":
168 libraryName = fileAttribute.getAttribute("Val")
169 vhdlFile.VHDLLibrary = fileset.GetOrCreateVHDLLibrary(libraryName)
170 elif fileAttribute.getAttribute("Val") == "UsedIn": 170 ↛ 171line 170 didn't jump to line 171 because the condition on line 170 was never true
171 usedInAttr.append(fileAttribute.getAttribute("Val"))
173 def _ParseDefaultFile(self, _, path, fileset) -> None:
174 File(path, fileSet=fileset)
176 def _ParseXDCFile(self, _, path, fileset) -> None:
177 XDCConstraintFile(path, fileSet=fileset)
179 def _ParseVerilogFile(self, _, path, fileset) -> None:
180 VerilogSourceFile(path, fileSet=fileset)
182 def _ParseXCIFile(self, _, path, fileset) -> None:
183 IPCoreInstantiationFile(path, fileSet=fileset)
185 def _ParseFileSetConfig(self, fileNode, fileset) -> None:
186 for option in fileNode.childNodes:
187 if option.nodeType == Node.ELEMENT_NODE and option.tagName == "Option":
188 if option.getAttribute("Name") == "TopModule":
189 fileset.TopLevel = option.getAttribute("Val")
192@export
193class XDCConstraintFile(ConstraintFile, SDCContent):
194 """A Vivado constraint file (Xilinx Design Constraints; ``*.xdc``)."""
197@export
198class IPCoreDescriptionFile(XMLFile):
199 pass
202@export
203class IPCoreInstantiationFile(XMLFile):
204 """A Vivado IP core instantiation file (Xilinx IPCore Instance; ``*.xci``)."""