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

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 

35 

36from pyTooling.Decorators import export 

37from pyTooling.MetaClasses import ExtendedType 

38from pyVHDLModel import VHDLVersion 

39 

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 

45 

46 

47@export 

48class UsedInAttribute(Attribute): 

49 KEY = "UsedIn" 

50 VALUE_TYPE = Iterable[str] 

51 

52 

53@export 

54class File(Model_File): 

55 pass 

56 

57 

58class VivadoFileMixIn(metaclass=ExtendedType, mixin=True): 

59 def _registerAttributes(self) -> None: 

60 self._attributes[UsedInAttribute] = [] 

61 

62 

63@export 

64class ConstraintFile(Model_ConstraintFile, VivadoFileMixIn): 

65 def _registerAttributes(self) -> None: 

66 super()._registerAttributes() 

67 VivadoFileMixIn._registerAttributes(self) 

68 

69 

70@export 

71class VerilogSourceFile(Model_VerilogSourceFile): 

72 def _registerAttributes(self) -> None: 

73 super()._registerAttributes() 

74 VivadoFileMixIn._registerAttributes(self) 

75 

76 

77@export 

78class VHDLSourceFile(Model_VHDLSourceFile): 

79 def _registerAttributes(self) -> None: 

80 super()._registerAttributes() 

81 VivadoFileMixIn._registerAttributes(self) 

82 

83 

84@export 

85class VivadoProjectFile(ProjectFile, XMLContent): 

86 """A Vivado project file (``*.xpr``).""" 

87 

88 _xprProject: Project 

89 

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) 

98 

99 self._xprProject = None 

100 

101 @property 

102 def ProjectModel(self) -> Project: 

103 return self._xprProject 

104 

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.") 

108 

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 

113 

114 self._xprProject = Project(self._path.stem, rootDirectory=self._path.parent) 

115 self._ParseRootElement(root) 

116 

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 

122 

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) 

127 

128 def _ParseFileSet(self, filesetNode) -> None: 

129 filesetName = filesetNode.getAttribute("Name") 

130 fileset = FileSet(filesetName, design=self._xprProject.DefaultDesign) 

131 

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) 

138 

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) 

152 

153 def _ParseVHDLFile(self, fileNode, path, fileset) -> None: 

154 vhdlFile = VHDLSourceFile(path) 

155 fileset.AddFile(vhdlFile) 

156 usedInAttr = vhdlFile[UsedInAttribute] 

157 

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 

164 

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")) 

172 

173 def _ParseDefaultFile(self, _, path, fileset) -> None: 

174 File(path, fileSet=fileset) 

175 

176 def _ParseXDCFile(self, _, path, fileset) -> None: 

177 XDCConstraintFile(path, fileSet=fileset) 

178 

179 def _ParseVerilogFile(self, _, path, fileset) -> None: 

180 VerilogSourceFile(path, fileSet=fileset) 

181 

182 def _ParseXCIFile(self, _, path, fileset) -> None: 

183 IPCoreInstantiationFile(path, fileSet=fileset) 

184 

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") 

190 

191 

192@export 

193class XDCConstraintFile(ConstraintFile, SDCContent): 

194 """A Vivado constraint file (Xilinx Design Constraints; ``*.xdc``).""" 

195 

196 

197@export 

198class IPCoreDescriptionFile(XMLFile): 

199 pass 

200 

201 

202@export 

203class IPCoreInstantiationFile(XMLFile): 

204 """A Vivado IP core instantiation file (Xilinx IPCore Instance; ``*.xci``)."""