Source code for pyEDAA.OutputFilter.Xilinx

# ==================================================================================================================== #
#               _____ ____    _        _      ___        _               _   _____ _ _ _                               #
#   _ __  _   _| ____|  _ \  / \      / \    / _ \ _   _| |_ _ __  _   _| |_|  ___(_) | |_ ___ _ __                    #
#  | '_ \| | | |  _| | | | |/ _ \    / _ \  | | | | | | | __| '_ \| | | | __| |_  | | | __/ _ \ '__|                   #
#  | |_) | |_| | |___| |_| / ___ \  / ___ \ | |_| | |_| | |_| |_) | |_| | |_|  _| | | | ||  __/ |                      #
#  | .__/ \__, |_____|____/_/   \_\/_/   \_(_)___/ \__,_|\__| .__/ \__,_|\__|_|   |_|_|\__\___|_|                      #
#  |_|    |___/                                             |_|                                                        #
# ==================================================================================================================== #
# 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 pathlib  import Path
from typing   import Optional as Nullable, Dict, List, Generator, Union, Type

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

from pyEDAA.OutputFilter.Xilinx.Common    import LineKind, Line
from pyEDAA.OutputFilter.Xilinx.Common    import VivadoMessage, TclCommand, VivadoTclCommand
from pyEDAA.OutputFilter.Xilinx.Common    import InfoMessage, VivadoInfoMessage, VivadoIrregularInfoMessage
from pyEDAA.OutputFilter.Xilinx.Common    import WarningMessage, VivadoWarningMessage, VivadoIrregularWarningMessage
from pyEDAA.OutputFilter.Xilinx.Common    import CriticalWarningMessage, VivadoCriticalWarningMessage
from pyEDAA.OutputFilter.Xilinx.Common    import ErrorMessage, VivadoErrorMessage
from pyEDAA.OutputFilter.Xilinx.Common    import VHDLReportMessage
from pyEDAA.OutputFilter.Xilinx.Commands import Command, SynthesizeDesign, LinkDesign, OptimizeDesign, PlaceDesign, \
	PhysicalOptimizeDesign, RouteDesign, WriteBitstream, ReportDRC, ReportMethodology, ReportPower
from pyEDAA.OutputFilter.Xilinx.Common2   import Preamble, VivadoMessagesMixin
from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException, ClassificationException


ProcessedLine = Union[Line, ProcessorException]


[docs] @export class Processor(VivadoMessagesMixin, metaclass=ExtendedType, slots=True): _duration: float _lines: List[ProcessedLine] _preamble: Preamble _commands: Dict[Type[Command], Command]
[docs] def __init__(self): super().__init__() self._duration = 0.0 self._lines = [] self._preamble = None self._commands = {}
@readonly def Lines(self) -> List[ProcessedLine]: return self._lines @readonly def Preamble(self) -> Preamble: return self._preamble @readonly def Commands(self) -> Dict[Type[Command], Command]: return self._commands @readonly def Duration(self) -> float: return self._duration def __getitem__(self, item: Type[Command]) -> Command: return self._commands[item]
[docs] def IsIncompleteLog(self) -> bool: """ :return: .. info:: ``INFO: [Common 17-14] Message 'Synth 8-3321' appears 100 times and further instances of the messages will be disabled. Use the Tcl command set_msg_config to change the current settings.`` """ return 17 in self._messagesByID and 14 in self._messagesByID[17]
def LineClassification(self) -> Generator[ProcessedLine, str, None]: # Instantiate and initialize CommandFinder next(cmdFinder := self.CommandFinder()) # wait for first line lastLine = None rawMessageLine = yield lineNumber = 0 _errorMessage = "Unknown processing error" while rawMessageLine is not None: lineNumber += 1 rawMessageLine = rawMessageLine.rstrip() errorMessage = _errorMessage if rawMessageLine.startswith(VivadoInfoMessage._MESSAGE_KIND): if (line := VivadoInfoMessage.Parse(lineNumber, rawMessageLine)) is None: line = VivadoIrregularInfoMessage.Parse(lineNumber, rawMessageLine) errorMessage = f"Line starting with 'INFO' was not a VivadoInfoMessage." elif rawMessageLine.startswith(VivadoWarningMessage._MESSAGE_KIND): if (line := VivadoWarningMessage.Parse(lineNumber, rawMessageLine)) is None: line = VivadoIrregularWarningMessage.Parse(lineNumber, rawMessageLine) errorMessage = f"Line starting with 'WARNING' was not a VivadoWarningMessage." elif rawMessageLine.startswith(VivadoCriticalWarningMessage._MESSAGE_KIND): line = VivadoCriticalWarningMessage.Parse(lineNumber, rawMessageLine) errorMessage = f"Line starting with 'CRITICAL WARNING' was not a VivadoCriticalWarningMessage." elif rawMessageLine.startswith(VivadoErrorMessage._MESSAGE_KIND): line = VivadoErrorMessage.Parse(lineNumber, rawMessageLine) errorMessage = f"Line starting with 'ERROR' was not a VivadoErrorMessage." elif len(rawMessageLine) == 0: line = Line(lineNumber, LineKind.Empty, rawMessageLine) elif rawMessageLine.startswith("Command: "): line = VivadoTclCommand.Parse(lineNumber, rawMessageLine) errorMessage = "Line starting with 'Command:' was not a VivadoTclCommand." else: line = Line(lineNumber, LineKind.Unprocessed, rawMessageLine) if line is None: line = Line(lineNumber, LineKind.ProcessorError, rawMessageLine) else: if line.StartsWith("Resolution:") and isinstance(lastLine, VivadoMessage): line._kind = LineKind.Verbose line.PreviousLine = lastLine lastLine = line line = cmdFinder.send(line) if isinstance(line, VivadoMessage): self._AddMessage(line) if line._kind is LineKind.ProcessorError: line = ClassificationException(errorMessage, lineNumber, rawMessageLine) self._lines.append(line) rawMessageLine = yield line def CommandFinder(self) -> Generator[Line, Line, None]: self._preamble = Preamble(self) cmd = None tclProcedures = {"source"} # wait for first line line = yield # process preable line = yield from self._preamble.Generator(line) while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoTclCommand): if line._command == SynthesizeDesign._TCL_COMMAND: self._commands[SynthesizeDesign] = (cmd := SynthesizeDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == LinkDesign._TCL_COMMAND: self._commands[LinkDesign] = (cmd := LinkDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == OptimizeDesign._TCL_COMMAND: self._commands[OptimizeDesign] = (cmd := OptimizeDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == OptimizeDesign._TCL_COMMAND: self._commands[OptimizeDesign] = (cmd := OptimizeDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == PlaceDesign._TCL_COMMAND: self._commands[PlaceDesign] = (cmd := PlaceDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == PhysicalOptimizeDesign._TCL_COMMAND: self._commands[PhysicalOptimizeDesign] = (cmd := PhysicalOptimizeDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == RouteDesign._TCL_COMMAND: self._commands[RouteDesign] = (cmd := RouteDesign(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == WriteBitstream._TCL_COMMAND: self._commands[WriteBitstream] = (cmd := WriteBitstream(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == ReportDRC._TCL_COMMAND: self._commands[ReportDRC] = (cmd := ReportDRC(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == ReportMethodology._TCL_COMMAND: self._commands[ReportMethodology] = (cmd := ReportMethodology(self)) line = yield next(gen := cmd.SectionDetector(line)) break elif line._command == ReportPower._TCL_COMMAND: self._commands[ReportPower] = (cmd := ReportPower(self)) line = yield next(gen := cmd.SectionDetector(line)) break firstWord = line.Partition(" ")[0] if firstWord in tclProcedures: line = TclCommand.FromLine(line) line = yield line # end = f"{cmd._TCL_COMMAND} completed successfully" while True: # if line.StartsWith(end): # # line._kind |= LineKind.Success # lastLine = gen.send(line) # if LineKind.Last in line._kind: # line._kind ^= LineKind.Last # line = yield lastLine # break try: line = yield gen.send(line) except StopIteration as ex: line = ex.value break
[docs] @export class Document(Processor): _logfile: Path
[docs] def __init__(self, logfile: Path) -> None: super().__init__() self._logfile = logfile
def Parse(self) -> None: with Stopwatch() as sw: with self._logfile.open("r", encoding="utf-8") as f: content = f.read() next(generator := self.LineClassification()) for rawLine in content.splitlines(): generator.send(rawLine) self._duration = sw.Duration