Source code for pyEDAA.OutputFilter.Xilinx.Common2

# ==================================================================================================================== #
#               _____ ____    _        _      ___        _               _   _____ _ _ _                               #
#   _ __  _   _| ____|  _ \  / \      / \    / _ \ _   _| |_ _ __  _   _| |_|  ___(_) | |_ ___ _ __                    #
#  | '_ \| | | |  _| | | | |/ _ \    / _ \  | | | | | | | __| '_ \| | | | __| |_  | | | __/ _ \ '__|                   #
#  | |_) | |_| | |___| |_| / ___ \  / ___ \ | |_| | |_| | |_| |_) | |_| | |_|  _| | | | ||  __/ |                      #
#  | .__/ \__, |_____|____/_/   \_\/_/   \_(_)___/ \__,_|\__| .__/ \__,_|\__|_|   |_|_|\__\___|_|                      #
#  |_|    |___/                                             |_|                                                        #
# ==================================================================================================================== #
# 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 datetime import datetime
from re       import Pattern, compile as re_compile
from typing   import ClassVar, Optional as Nullable, Generator, List, Dict, Tuple, Type

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

from pyEDAA.OutputFilter.Xilinx import Line, LineKind, VivadoMessage
from pyEDAA.OutputFilter.Xilinx import VivadoInfoMessage, VivadoWarningMessage, VivadoCriticalWarningMessage, VivadoErrorMessage
from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException


[docs] @export class VivadoMessagesMixin(metaclass=ExtendedType, mixin=True): _infoMessages: List[VivadoInfoMessage] _warningMessages: List[VivadoWarningMessage] _criticalWarningMessages: List[VivadoCriticalWarningMessage] _errorMessages: List[VivadoErrorMessage] _toolIDs: Dict[int, str] _toolNames: Dict[str, int] _messagesByID: Dict[int, Dict[int, List[VivadoMessage]]]
[docs] def __init__(self) -> None: self._infoMessages = [] self._warningMessages = [] self._criticalWarningMessages = [] self._errorMessages = [] self._toolIDs = {} self._toolNames = {} self._messagesByID = {}
@readonly def ToolIDs(self) -> Dict[int, str]: return self._toolIDs @readonly def ToolNames(self) -> Dict[str, int]: return self._toolNames @readonly def MessagesByID(self) -> Dict[int, Dict[int, List[VivadoMessage]]]: return self._messagesByID @readonly def InfoMessages(self) -> List[VivadoInfoMessage]: return self._infoMessages @readonly def WarningMessages(self) -> List[VivadoWarningMessage]: return self._warningMessages @readonly def CriticalWarningMessages(self) -> List[VivadoCriticalWarningMessage]: return self._criticalWarningMessages @readonly def ErrorMessages(self) -> List[VivadoErrorMessage]: return self._errorMessages def _AddMessage(self, message: VivadoMessage) -> None: if isinstance(message, VivadoInfoMessage): self._infoMessages.append(message) elif isinstance(message, VivadoWarningMessage): self._warningMessages.append(message) elif isinstance(message, VivadoCriticalWarningMessage): self._criticalWarningMessages.append(message) elif isinstance(message, VivadoErrorMessage): self._errorMessages.append(message) if message._toolID in self._messagesByID: sub = self._messagesByID[message._toolID] if message._messageKindID in sub: sub[message._messageKindID].append(message) else: sub[message._messageKindID] = [message] else: self._toolIDs[message._toolID] = message._toolName self._toolNames[message._toolName] = message._toolID self._messagesByID[message._toolID] = {message._messageKindID: [message]}
[docs] @export class BaseParser(VivadoMessagesMixin, metaclass=ExtendedType, slots=True):
[docs] def __init__(self) -> None: super().__init__()
[docs] @export class Parser(BaseParser): _processor: "Processor"
[docs] def __init__(self, processor: "Processor"): super().__init__() self._processor = processor
@readonly def Processor(self) -> "Processor": return self._processor
[docs] @export class Preamble(Parser): _toolVersion: Nullable[YearReleaseVersion] _startDatetime: Nullable[datetime] _VERSION: ClassVar[Pattern] = re_compile(r"""# Vivado v(\d+\.\d(\.\d)?) \(64-bit\)""") _STARTTIME: ClassVar[Pattern] = re_compile(r"""# Start of session at: (\w+ \w+ \d+ \d+:\d+:\d+ \d+)""")
[docs] def __init__(self, processor: "BaseProcessor"): super().__init__(processor) self._toolVersion = None self._startDatetime = None
@readonly def ToolVersion(self) -> YearReleaseVersion: return self._toolVersion @readonly def StartDatetime(self) -> datetime: return self._startDatetime def Generator(self, line: Line) -> Generator[Line, Line, Line]: if line.StartsWith("#----"): line._kind = LineKind.SectionDelimiter else: line._kind |= LineKind.ProcessorError line = yield line while True: if (match := self._VERSION.match(line._message)) is not None: self._toolVersion = YearReleaseVersion.Parse(match[1]) line._kind = LineKind.Normal elif (match := self._STARTTIME.match(line._message)) is not None: self._startDatetime = datetime.strptime(match[1], "%a %b %d %H:%M:%S %Y") line._kind = LineKind.Normal elif line.StartsWith("#----"): line._kind = LineKind.SectionDelimiter break else: line._kind = LineKind.Verbose line = yield line nextLine = yield line return nextLine
[docs] @export class Task(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True): # _START: ClassVar[str] # _FINISH: ClassVar[str] _TIME: ClassVar[str] = "Time (s):" _PARSERS: ClassVar[Tuple[Type["Phase"], ...]] = tuple() _command: "Command" _duration: float _phases: Dict[Type["Phase"], "Phase"]
[docs] def __init__(self, command: "Command"): super().__init__() VivadoMessagesMixin.__init__(self) self._command = command self._phases = {p: p(self) for p in self._PARSERS}
@readonly def Command(self) -> "Command": return self._command @readonly def Phases(self) -> Dict[Type["Phase"], "Phase"]: return self._phases def __getitem__(self, key: Type["Phase"]) -> "Phase": return self._phases[key] def _TaskStart(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._START): raise ProcessorException() line._kind = LineKind.TaskStart nextLine = yield line return nextLine def _TaskFinish(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._FINISH): raise ProcessorException() line._kind = LineKind.TaskEnd line = yield line while self._TIME is not None: if line.StartsWith(self._TIME): line._kind = LineKind.TaskTime break line = yield line line = yield line return line def Generator(self, line: Line) -> Generator[Line, Line, Line]: line = yield from self._TaskStart(line) while True: if line._kind is LineKind.Empty: line = yield line continue elif self._FINISH is not None and line.StartsWith("Ending"): break elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith(self._TIME): line._kind = LineKind.TaskTime nextLine = yield line return nextLine line = yield line nextLine = yield from self._TaskFinish(line) return nextLine
[docs] def __str__(self) -> str: return self._NAME
[docs] @export class Phase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True): # _START: ClassVar[str] # _FINISH: ClassVar[str] _TIME: ClassVar[str] = "Time (s):" _task: Task _duration: float
[docs] def __init__(self, task: Task): super().__init__() VivadoMessagesMixin.__init__(self) self._task = task
@readonly def Task(self) -> Task: return self._task def _PhaseStart(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._START): raise ProcessorException() line._kind = LineKind.PhaseStart nextLine = yield line return nextLine def _PhaseFinish(self, line: Line) -> Generator[Line, Line, None]: if not line.StartsWith(self._FINISH): raise ProcessorException() line._kind = LineKind.PhaseEnd line = yield line if self._TIME is not None: while self._TIME is not None: if line.StartsWith(self._TIME): line._kind = LineKind.PhaseTime break line = yield line line = yield line return line def Generator(self, line: Line) -> Generator[Line, Line, Line]: line = yield from self._PhaseStart(line) while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith(self._FINISH): break line = yield line nextLine = yield from self._PhaseFinish(line) return nextLine
[docs] @export class SubPhase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True): # _START: ClassVar[str] # _FINISH: ClassVar[str] _phase: Phase _duration: float
[docs] def __init__(self, phase: Phase): super().__init__() VivadoMessagesMixin.__init__(self) self._phase = phase
def _SubPhaseStart(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._START): raise ProcessorException() line._kind = LineKind.SubPhaseStart nextLine = yield line return nextLine def _SubPhaseFinish(self, line: Line) -> Generator[Line, Line, None]: if not line.StartsWith(self._FINISH): raise ProcessorException() if self._TIME is None: line._kind = LineKind.SubPhaseTime else: line._kind = LineKind.SubPhaseEnd line = yield line while self._TIME is not None: if line.StartsWith(self._TIME): line._kind = LineKind.SubPhaseTime break line = yield line nextLine = yield line return nextLine def Generator(self, line: Line) -> Generator[Line, Line, Line]: line = yield from self._SubPhaseStart(line) while True: if line._kind is LineKind.Empty: line = yield line continue elif line.StartsWith(self._FINISH): break elif isinstance(line, VivadoMessage): self._AddMessage(line) line = yield line nextLine = yield from self._SubPhaseFinish(line) return nextLine
[docs] @export class SubSubPhase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True): # _START: ClassVar[str] # _FINISH: ClassVar[str] _subphase: SubPhase _duration: float
[docs] def __init__(self, subphase: SubPhase): super().__init__() VivadoMessagesMixin.__init__(self) self._subphase = subphase
def _SubSubPhaseStart(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._START): raise ProcessorException() line._kind = LineKind.SubSubPhaseStart nextLine = yield line return nextLine def _SubSubPhaseFinish(self, line: Line) -> Generator[Line, Line, None]: if not line.StartsWith(self._FINISH): raise ProcessorException() line._kind = LineKind.SubSubPhaseEnd line = yield line while self._TIME is not None: if line.StartsWith(self._TIME): line._kind = LineKind.SubSubPhaseTime break line = yield line nextLine = yield line return nextLine def Generator(self, line: Line) -> Generator[Line, Line, Line]: line = yield from self._SubSubPhaseStart(line) while True: if line._kind is LineKind.Empty: line = yield line continue elif line.StartsWith(self._FINISH): break elif isinstance(line, VivadoMessage): self._AddMessage(line) line = yield line nextLine = yield from self._SubSubPhaseFinish(line) return nextLine
[docs] @export class SubSubSubPhase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True): # _START: ClassVar[str] # _FINISH: ClassVar[str] _subsubphase: SubSubPhase _duration: float
[docs] def __init__(self, subsubphase: SubSubPhase): super().__init__() VivadoMessagesMixin.__init__(self) self._subsubphase = subsubphase
def _SubSubSubPhaseStart(self, line: Line) -> Generator[Line, Line, Line]: if not line.StartsWith(self._START): raise ProcessorException() line._kind = LineKind.SubSubSubPhaseStart nextLine = yield line return nextLine def _SubSubSubPhaseFinish(self, line: Line) -> Generator[Line, Line, None]: if not line.StartsWith(self._FINISH): raise ProcessorException() line._kind = LineKind.SubSubSubPhaseEnd line = yield line while self._TIME is not None: if line.StartsWith(self._TIME): line._kind = LineKind.SubSubSubPhaseTime break line = yield line nextLine = yield line return nextLine def Generator(self, line: Line) -> Generator[Line, Line, Line]: line = yield from self._SubSubSubPhaseStart(line) while True: if line._kind is LineKind.Empty: line = yield line continue elif line.StartsWith(self._FINISH): break elif isinstance(line, VivadoMessage): self._AddMessage(line) line = yield line nextLine = yield from self._SubSubSubPhaseFinish(line) return nextLine