Source code for pyEDAA.OutputFilter.Xilinx.Common

# ==================================================================================================================== #
#               _____ ____    _        _      ___        _               _   _____ _ _ _                               #
#   _ __  _   _| ____|  _ \  / \      / \    / _ \ _   _| |_ _ __  _   _| |_|  ___(_) | |_ ___ _ __                    #
#  | '_ \| | | |  _| | | | |/ _ \    / _ \  | | | | | | | __| '_ \| | | | __| |_  | | | __/ _ \ '__|                   #
#  | |_) | |_| | |___| |_| / ___ \  / ___ \ | |_| | |_| | |_| |_) | |_| | |_|  _| | | | ||  __/ |                      #
#  | .__/ \__, |_____|____/_/   \_\/_/   \_(_)___/ \__,_|\__| .__/ \__,_|\__|_|   |_|_|\__\___|_|                      #
#  |_|    |___/                                             |_|                                                        #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2025-2025 Electronic Design Automation Abstraction (EDA²)                                                  #
#                                                                                                                      #
# Licensed under the Apache License, Version 2.0 (the "License");                                                      #
# you may not use this file except in compliance with the License.                                                     #
# You may obtain a copy of the License at                                                                              #
#                                                                                                                      #
#   http://www.apache.org/licenses/LICENSE-2.0                                                                         #
#                                                                                                                      #
# Unless required by applicable law or agreed to in writing, software                                                  #
# distributed under the License is distributed on an "AS IS" BASIS,                                                    #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                             #
# See the License for the specific language governing permissions and                                                  #
# limitations under the License.                                                                                       #
#                                                                                                                      #
# SPDX-License-Identifier: Apache-2.0                                                                                  #
# ==================================================================================================================== #
#
"""Basic classes for outputs from AMD/Xilinx Vivado."""
from enum    import Flag
from pathlib import Path
from re      import Pattern, compile as re_compile
from typing import Optional as Nullable, Self, ClassVar, Tuple, Union, Any

from pyTooling.Decorators  import export, readonly
from pyTooling.MetaClasses import ExtendedType


[docs] @export class LineKind(Flag): Unprocessed = 0 ProcessorError = 2** 0 Empty = 2** 1 Delimiter = 2** 2 Success = 2** 3 Failed = 2** 4 Verbose = 2**10 Normal = 2**11 Info = 2**12 Warning = 2**13 CriticalWarning = 2**14 Error = 2**15 Fatal = 2**16 Start = 2**20 End = 2**21 Header = 2**22 Content = 2**23 Time = 2**24 Footer = 2**25 Last = 2**29 Message = 2**30 InfoMessage = Message | Info WarningMessage = Message | Warning CriticalWarningMessage = Message | CriticalWarning ErrorMessage = Message | Error Task = 2**31 TaskStart = Task | Start TaskEnd = Task | End TaskTime = Task | Time Phase = 2**32 PhaseDelimiter = Phase | Delimiter PhaseStart = Phase | Start PhaseEnd = Phase | End PhaseTime = Phase | Time PhaseFinal = Phase | Footer SubPhase = 2**33 SubPhaseStart = SubPhase | Start SubPhaseEnd = SubPhase | End SubPhaseTime = SubPhase | Time SubSubPhase = 2**34 SubSubPhaseStart = SubSubPhase | Start SubSubPhaseEnd = SubSubPhase | End SubSubPhaseTime = SubSubPhase | Time SubSubSubPhase = 2**35 SubSubSubPhaseStart = SubSubSubPhase | Start SubSubSubPhaseEnd = SubSubSubPhase | End SubSubSubPhaseTime = SubSubSubPhase | Time Section = 2**36 SectionDelimiter = Section | Delimiter SectionStart = Section | Start SectionEnd = Section | End SubSection = 2**37 SubSectionDelimiter = SubSection | Delimiter SubSectionStart = SubSection | Start SubSectionEnd = SubSection | End Paragraph = 2**38 ParagraphHeadline = Paragraph | Header Hierarchy = 2**39 HierarchyStart = Hierarchy | Start HierarchyEnd = Hierarchy | End XDC = 2**40 XDCStart = XDC | Start XDCEnd = XDC | End Table = 2**41 TableFrame = Table | Delimiter TableHeader = Table | Header TableRow = Table | Content TableFooter = Table | Footer TclCommand = 2**42 GenericTclCommand = TclCommand | 2**0 VivadoTclCommand = TclCommand | 2**1
[docs] @export class Line(metaclass=ExtendedType, slots=True): """ This class represents any line in a log file. """ _lineNumber: int _kind: LineKind _message: str _previousLine: "Line" _nextLine: "Line"
[docs] def __init__(self, lineNumber: int, kind: LineKind, message: str) -> None: self._lineNumber = lineNumber self._kind = kind self._message = message self._previousLine = None self._nextLine = None
@readonly def LineNumber(self) -> int: return self._lineNumber @readonly def Kind(self) -> LineKind: return self._kind @readonly def Message(self) -> str: return self._message @property def PreviousLine(self) -> "Line": return self._previousLine @PreviousLine.setter def PreviousLine(self, line: "Line") -> None: self._previousLine = line if line is not None: line._nextLine = self @readonly def NextLine(self) -> "Line": return self._nextLine def Partition(self, separator: str) -> Tuple[str, str, str]: return self._message.partition(separator) def StartsWith(self, prefix: Union[str, Tuple[str, ...]]): return self._message.startswith(prefix) def __getitem__(self, item: slice) -> str: return self._message[item]
[docs] def __eq__(self, other: Any): return self._message == other
[docs] def __ne__(self, other: Any): return self._message != other
[docs] def __str__(self) -> str: return self._message
[docs] def __repr__(self) -> str: return f"{self._lineNumber}: {self._message}"
[docs] @export class InfoMessage(metaclass=ExtendedType, mixin=True): pass
[docs] @export class WarningMessage(metaclass=ExtendedType, mixin=True): pass
[docs] @export class CriticalWarningMessage(metaclass=ExtendedType, mixin=True): pass
[docs] @export class ErrorMessage(metaclass=ExtendedType, mixin=True): pass
[docs] @export class VivadoMessage(Line): """ This class represents an AMD/Xilinx Vivado message. The usual message format is: .. code-block:: text INFO: [Synth 8-7079] Multithreading enabled for synth_design using a maximum of 2 processes. WARNING: [Synth 8-3332] Sequential element (gen[0].Sync/FF2) is unused and will be removed from module sync_Bits_Xilinx. The following message severities are defined: * ``INFO`` * ``WARNING`` * ``CRITICAL WARNING`` * ``ERROR`` .. seealso:: :class:`VivadoInfoMessage` Representing a Vivado info message. :class:`VivadoWarningMessage` Representing a Vivado warning message. :class:`VivadoCriticalWarningMessage` Representing a Vivado critical warning message. :class:`VivadoErrorMessage` Representing a Vivado error message. """ # _MESSAGE_KIND: ClassVar[str] # _REGEXP: ClassVar[Pattern] _toolID: int _toolName: str _messageKindID: int
[docs] def __init__(self, lineNumber: int, kind: LineKind, tool: str, toolID: int, messageKindID: int, message: str) -> None: super().__init__(lineNumber, kind, message) self._toolID = toolID self._toolName = tool self._messageKindID = messageKindID
@readonly def ToolName(self) -> str: return self._toolName @readonly def ToolID(self) -> int: return self._toolID @readonly def MessageKindID(self) -> int: return self._messageKindID @classmethod def Parse(cls, lineNumber: int, kind: LineKind, rawMessage: str) -> Nullable[Self]: if (match := cls._REGEXP.match(rawMessage)) is not None: return cls(lineNumber, kind, match[1], int(match[2]), int(match[3]), match[4]) return None
[docs] def __str__(self) -> str: return f"{self._MESSAGE_KIND}: [{self._toolName} {self._toolID}-{self._messageKindID}] {self._message}"
[docs] @export class VivadoInfoMessage(VivadoMessage, InfoMessage): """ This class represents an AMD/Xilinx Vivado info message. """ _MESSAGE_KIND: ClassVar[str] = "INFO" _REGEXP: ClassVar[Pattern] = re_compile(r"""INFO: \[(\w+) (\d+)-(\d+)\] (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: return super().Parse(lineNumber, LineKind.InfoMessage, rawMessage)
[docs] @export class VivadoIrregularInfoMessage(VivadoMessage, InfoMessage): """ This class represents an irregular AMD/Xilinx Vivado info message. """ _MESSAGE_KIND: ClassVar[str] = "INFO" _REGEXP: ClassVar[Pattern] = re_compile(r"""INFO: \[(\w+)-(\d+)\] (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: if (match := cls._REGEXP.match(rawMessage)) is not None: return cls(lineNumber, LineKind.InfoMessage, match[1], None, int(match[2]), match[3]) return None
[docs] def __str__(self) -> str: return f"{self._MESSAGE_KIND}: [{self._toolName}-{self._messageKindID}] {self._message}"
[docs] @export class VivadoWarningMessage(VivadoMessage, WarningMessage): """ This class represents an AMD/Xilinx Vivado warning message. """ _MESSAGE_KIND: ClassVar[str] = "WARNING" _REGEXP: ClassVar[Pattern] = re_compile(r"""WARNING: \[(\w+) (\d+)-(\d+)\] (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: return super().Parse(lineNumber, LineKind.WarningMessage, rawMessage)
[docs] @export class VivadoIrregularWarningMessage(VivadoMessage, WarningMessage): """ This class represents an AMD/Xilinx Vivado warning message. """ _MESSAGE_KIND: ClassVar[str] = "WARNING" _REGEXP: ClassVar[Pattern] = re_compile(r"""WARNING: (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: if (match := cls._REGEXP.match(rawMessage)) is not None: return cls(lineNumber, LineKind.WarningMessage, None, None, None, match[1]) return None
[docs] def __str__(self) -> str: return f"{self._MESSAGE_KIND}: {self._message}"
[docs] @export class VivadoCriticalWarningMessage(VivadoMessage, CriticalWarningMessage): """ This class represents an AMD/Xilinx Vivado critical warning message. """ _MESSAGE_KIND: ClassVar[str] = "CRITICAL WARNING" _REGEXP: ClassVar[Pattern] = re_compile(r"""CRITICAL WARNING: \[(\w+) (\d+)-(\d+)\] (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: return super().Parse(lineNumber, LineKind.CriticalWarningMessage, rawMessage)
[docs] @export class VivadoErrorMessage(VivadoMessage, ErrorMessage): """ This class represents an AMD/Xilinx Vivado error message. """ _MESSAGE_KIND: ClassVar[str] = "ERROR" _REGEXP: ClassVar[Pattern] = re_compile(r"""ERROR: \[(\w+) (\d+)-(\d+)\] (.*)""") @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: return super().Parse(lineNumber, LineKind.ErrorMessage, rawMessage)
[docs] @export class VHDLReportMessage(VivadoInfoMessage): _REGEXP: ClassVar[Pattern ] = re_compile(r"""RTL report: "(.*)" \[(.*):(\d+)\]""") _reportMessage: str _sourceFile: Path _sourceLineNumber: int
[docs] def __init__(self, lineNumber: int, tool: str, toolID: int, messageKindID: int, rawMessage: str, reportMessage: str, sourceFile: Path, sourceLineNumber: int): super().__init__(lineNumber, LineKind.InfoMessage, tool, toolID, messageKindID, rawMessage) self._reportMessage = reportMessage self._sourceFile = sourceFile self._sourceLineNumber = sourceLineNumber
@classmethod def Convert(cls, line: VivadoInfoMessage) -> Nullable[Self]: if (match := cls._REGEXP.match(line._message)) is not None: return cls(line._lineNumber, line._toolName, line._toolID, line._messageKindID, line._message, match[1], Path(match[2]), int(match[3])) return None
[docs] @export class VHDLAssertionMessage(VHDLReportMessage): _REGEXP: ClassVar[Pattern ] = re_compile(r"""RTL assertion: "(.*)" \[(.*):(\d+)\]""")
[docs] @export class TclCommand(Line): _command: str _args: Tuple[str, ...]
[docs] def __init__(self, lineNumber: int, command: str, arguments: Tuple[str, ...], tclCommand: str) -> None: super().__init__(lineNumber, LineKind.GenericTclCommand, tclCommand) self._command = command self._args = arguments
@readonly def Command(self) -> str: return self._command @readonly def Arguments(self) -> Tuple[str]: return self._args @classmethod def FromLine(cls, line: Line) -> Nullable[Self]: args = line._message.split() return cls(line._lineNumber, args[0], tuple(args[1:]), line._message)
[docs] def __str__(self) -> str: return f"{self._command} {' '.join(self._args)}"
[docs] @export class VivadoTclCommand(TclCommand): _PREFIX: ClassVar[str] = "Command:" @classmethod def Parse(cls, lineNumber: int, rawMessage: str) -> Nullable[Self]: command = rawMessage[len(cls._PREFIX) + 1:] args = command.split() command = cls(lineNumber, args[0], tuple(args[1:]), command) command._kind = LineKind.VivadoTclCommand return command
[docs] def __str__(self) -> str: return f"{self._PREFIX} {self._command} {' '.join(self._args)}"