Source code for pyEDAA.OutputFilter.Xilinx.Commands

# ==================================================================================================================== #
#               _____ ____    _        _      ___        _               _   _____ _ _ _                               #
#   _ __  _   _| ____|  _ \  / \      / \    / _ \ _   _| |_ _ __  _   _| |_|  ___(_) | |_ ___ _ __                    #
#  | '_ \| | | |  _| | | | |/ _ \    / _ \  | | | | | | | __| '_ \| | | | __| |_  | | | __/ _ \ '__|                   #
#  | |_) | |_| | |___| |_| / ___ \  / ___ \ | |_| | |_| | |_| |_) | |_| | |_|  _| | | | ||  __/ |                      #
#  | .__/ \__, |_____|____/_/   \_\/_/   \_(_)___/ \__,_|\__| .__/ \__,_|\__|_|   |_|_|\__\___|_|                      #
#  |_|    |___/                                             |_|                                                        #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2025-2026 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 re      import compile as re_compile
from typing  import ClassVar, Generator, Union, List, Type, Dict, Any, Tuple

from pyTooling.Decorators import export, readonly
from pyTooling.Common     import getFullyQualifiedName
from pyTooling.Warning    import WarningCollector

from pyEDAA.OutputFilter.Xilinx                  import VivadoTclCommand
from pyEDAA.OutputFilter.Xilinx.Exception        import ProcessorException, NotPresentException
from pyEDAA.OutputFilter.Xilinx.Common           import Line, LineKind, VivadoMessage, VHDLReportMessage
from pyEDAA.OutputFilter.Xilinx.Common2          import Parser, UnknownSection, UnknownTask, Task
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import Section, RTLElaboration, HandlingCustomAttributes
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ConstraintValidation, LoadingPart, ApplySetProperty
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RTLComponentStatistics, RTLHierarchicalComponentStatistics
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import PartResourceSummary, CrossBoundaryAndAreaOptimization
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ROM_RAM_DSP_SR_Retiming, ApplyingXDCTimingConstraints
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import TimingOptimization, TechnologyMapping, IOInsertion
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import FlatteningBeforeIOInsertion, FinalNetlistCleanup
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedInstances, RebuildingUserHierarchy
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedPorts, RenamingGeneratedNets
from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import WritingSynthesisReport
from pyEDAA.OutputFilter.Xilinx.OptimizeDesign   import DRCTask, CacheTimingInformationTask, LogicOptimizationTask
from pyEDAA.OutputFilter.Xilinx.OptimizeDesign   import PowerOptimizationTask, FinalCleanupTask, NetlistObfuscationTask
from pyEDAA.OutputFilter.Xilinx.PlaceDesign      import PlacerTask
from pyEDAA.OutputFilter.Xilinx.PhysicalOptimizeDesign import PhysicalSynthesisTask, InitialUpdateTimingTask
from pyEDAA.OutputFilter.Xilinx.RouteDesign      import RoutingTask


[docs] @export class CommandNotPresentException(NotPresentException): pass
[docs] @export class SectionNotPresentException(NotPresentException): pass
[docs] @export class HandlingCustomAttributes1(HandlingCustomAttributes): pass
[docs] @export class HandlingCustomAttributes2(HandlingCustomAttributes): pass
# FIXME: remove duplications
[docs] @export class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming): pass
[docs] @export class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming): pass
[docs] @export class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming): pass
[docs] @export class Command(Parser): """ This parser parses outputs from Vivado TCL commands. Depending on the command's output (and how it's implemented), they use different subcategories. .. rubric:: Command subcategories * :class:`CommandWithSections` * :class:`CommandWithtasks` .. rubric:: Supported commands * :class:`SynthesizeDesign` * :class:`LinkDesign` * :class:`OptimizeDesign` * :class:`PlaceDesign` * :class:`PhysicalOptimizeDesign` * :class:`RouteDesign` * :class:`WriteBitstream` * :class:`ReportDRC` * :class:`ReportMethodology` * :class:`ReportPower` .. rubric:: Example .. code-block:: [...] Command: synth_design -top system_top -part xc7z015clg485-2 Starting synth_design [...] """ # _TCL_COMMAND: ClassVar[str]
[docs] def _CommandStart(self, line: Line) -> Generator[Line, Line, Line]: """ A generator accepting a line containing the expected Vivado TCL command. When the generator exits, the returned line is the successor line to the line containing the Vivado TCL command. :param line: The first line for the generator to process. :returns: A generator processing Vivado output log lines. """ if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): raise ProcessorException() # FIXME: add exception message nextLine = yield line return nextLine
def _CommandFinish(self, line: Line) -> Generator[Line, Line, Line]: if line.StartsWith(f"{self._TCL_COMMAND} completed successfully"): line._kind |= LineKind.Success else: line._kind |= LineKind.Failed line = yield line if self._TIME is not None: # and self._processor._preamble._toolVersion > "2022.2": end = f"{self._TCL_COMMAND}: {self._TIME}" if line.StartsWith(end): line._kind = LineKind.TaskTime line = yield line return line def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]: line = yield from self._CommandStart(line) end = f"{self._TCL_COMMAND} " while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith(end): nextLine = yield from self._CommandFinish(line) return nextLine line = yield line
[docs] def __str__(self) -> str: return f"{self._TCL_COMMAND}"
[docs] @export class CommandWithSections(Command): """ A Vivado command writing sections into the output log. .. rubric:: Example .. code-block:: [...] --------------------------------------------------------------------------------- Starting RTL Elaboration : Time (s): cpu = 00:00:03 ; elapsed = 00:00:03 . Memory (MB): peak = 847.230 ; gain = 176.500 --------------------------------------------------------------------------------- INFO: [Synth 8-638] synthesizing module 'system_top' [C:/Users/tgomes/git/2019_1/src/system_top_PE1.vhd:257] [...] [...] [...] --------------------------------------------------------------------------------- Finished RTL Elaboration : Time (s): cpu = 00:00:04 ; elapsed = 00:00:04 . Memory (MB): peak = 917.641 ; gain = 246.910 --------------------------------------------------------------------------------- [...] """ # _PARSERS: ClassVar[Tuple[Type[Section], ...]] _sections: Dict[Type[Section], Section]
[docs] def __init__(self, processor: "Processor") -> None: super().__init__(processor) self._sections = {p: p(self) for p in self._PARSERS}
@readonly def Sections(self) -> Dict[Type[Section], Section]: """ Read-only property to access a dictionary of found sections within the TCL command's output. :returns: A dictionary of found :class:`~pyEDAA.OutputFilter.Xilinx.SynthesizeDesign.Section`s. """ return self._sections def __contains__(self, key: Any) -> bool: if not issubclass(key, Section): ex = TypeError(f"Parameter 'key' is not a Section.") ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") raise ex return key in self._sections def __getitem__(self, key: Type[Section]) -> Section: try: return self._sections[key] except KeyError as ex: raise SectionNotPresentException(F"Section '{key._NAME}' not present in '{self._parent.logfile}'.") from ex
[docs] @export class CommandWithTasks(Command): """ A Vivado command writing tasks into the output log. .. rubric:: Example .. code-block:: [...] Starting Cache Timing Information Task INFO: [Timing 38-35] 79-Done setting XDC timing constraints. [...] [...] Ending Cache Timing Information Task | Checksum: 19fe8cb97 [...] """ # _PARSERS: Tuple[Type[Task], ...] _tasks: Dict[Type[Task], Task]
[docs] def __init__(self, processor: "Processor") -> None: super().__init__(processor) self._tasks = {p: p(self) for p in self._PARSERS}
@readonly def Tasks(self) -> Dict[Type[Task], Task]: """ Read-only property to access a dictionary of found tasks within the TCL command's output. :returns: A dictionary of found :class:`~pyEDAA.OutputFilter.Xilinx.Common2.Task`s. """ return self._tasks def __contains__(self, key: Any) -> bool: if not issubclass(key, Task): ex = TypeError(f"Parameter 'key' is not a Task.") ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") raise ex return key in self._tasks def __getitem__(self, key: Type[Task]) -> Task: try: return self._tasks[key] except KeyError as ex: raise SectionNotPresentException(F"Task '{key._NAME}' not present in '{self._parent.logfile}'.") from ex
[docs] @export class SynthesizeDesign(CommandWithSections): """ A Vivado command output parser for ``synth_design``. """ _TCL_COMMAND: ClassVar[str] = "synth_design" _PARSERS: ClassVar[Tuple[Type[Section], ...]] = ( RTLElaboration, HandlingCustomAttributes1, ConstraintValidation, LoadingPart, ApplySetProperty, RTLComponentStatistics, RTLHierarchicalComponentStatistics, PartResourceSummary, CrossBoundaryAndAreaOptimization, ROM_RAM_DSP_SR_Retiming1, ApplyingXDCTimingConstraints, TimingOptimization, ROM_RAM_DSP_SR_Retiming2, TechnologyMapping, IOInsertion, FlatteningBeforeIOInsertion, FinalNetlistCleanup, RenamingGeneratedInstances, RebuildingUserHierarchy, RenamingGeneratedPorts, HandlingCustomAttributes2, RenamingGeneratedNets, ROM_RAM_DSP_SR_Retiming3, WritingSynthesisReport, ) @readonly def HasLatches(self) -> bool: """ Read-only property returning if synthesis inferred latches into the design. Latch detection is based on: * Vivado message ``synth 8-327`` * Cells of lind ``LD`` listed in the *Cell Usage* report. :returns: True, if the design contains latches. """ if (8 in self._messagesByID) and (327 in self._messagesByID[8]): return True return "LD" in self._sections[WritingSynthesisReport]._cells @readonly def Latches(self) -> List[VivadoMessage]: """ Read-only property to access a list of Vivado output messages for inferred latches. :returns: A list of Vivado messages for interred latches. .. note:: This returns ``[Synth 8-327]`` messages. .. code-block:: WARNING: [Synth 8-327] inferring latch for variable 'Q_reg' """ if 8 in self._messagesByID: if 327 in (synthMessages := self._messagesByID[8]): return [message for message in synthMessages[327]] return [] @readonly def HasBlackboxes(self) -> bool: """ Read-only property returning if the design contains black-boxes. :returns: True, if the design contains black-boxes. """ return len(self._sections[WritingSynthesisReport]._blackboxes) > 0 @readonly def Blackboxes(self) -> Dict[str, int]: """ Read-only property to access the dictionary of found blackbox statistics. :returns: The dictionary of found blackbox statistics. """ return self._sections[WritingSynthesisReport]._blackboxes @readonly def Cells(self) -> Dict[str, int]: """ Read-only property to access the dictionary of synthesized cell statistics. :returns: The dictionary of used cell statistics. """ return self._sections[WritingSynthesisReport]._cells @readonly def VHDLReportMessages(self) -> List[VHDLReportMessage]: """ Read-only property to access a list of Vivado output messages generated by VHDL report statement. :returns: A list of VHDL report statement outputs. .. note:: This returns ``[Synth 8-6031]`` messages. .. code-block:: INFO: [Synth 8-6031] RTL report: "TimingToCycles(time, freq): period=10.000000 ns -- 0.000000 fs" [C:/[...]/StopWatch/src/Utilities.pkg.vhdl:118] """ if 8 in self._messagesByID: if 6031 in (synthMessages := self._messagesByID[8]): return [message for message in synthMessages[6031]] return [] @readonly def VHDLAssertMessages(self) -> List[VHDLReportMessage]: """ Read-only property to access a list of Vivado output messages generated by VHDL assert statement. :returns: A list of VHDL assert statement outputs. .. note:: This returns ``[Synth 8-63]`` messages. .. code-block:: INFO: [Synth 8-63] RTL assertion: "CLOCK_FREQ: 100.000000 ns" [C:/[...]/StopWatch/src/Debouncer.vhdl:28] """ if 8 in self._messagesByID: if 63 in (synthMessages := self._messagesByID[8]): return [message for message in synthMessages[63]] return [] def SectionDetector(self, line: Line) -> Generator[Line, Line, None]: if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): raise ProcessorException() # FIXME: add exception message activeParsers: List[Section] = list(self._sections.values()) rtlElaboration = self._sections[RTLElaboration] # constraintValidation = self._sections[ConstraintValidation] line = yield line if line == "Starting synth_design": line._kind = LineKind.Verbose else: raise ProcessorException() # FIXME: add exception message line = yield line while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith("Start "): for parser in activeParsers: # type: Section if line.StartsWith(parser._START): line = next(section := parser.Generator(line)) line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter break else: WarningCollector.Raise(UnknownSection(f"Unknown section: '{line!r}'", line)) ex = Exception(f"How to recover from here? Unknown section: '{line!r}'") # ex.add_note(f"Current task: start pattern='{self._task}'") ex.add_note(f"Current cmd: {self}") raise ex break elif line.StartsWith("Starting "): if line.StartsWith(rtlElaboration._START): parser = rtlElaboration line = next(section := parser.Generator(line)) line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter break elif line.StartsWith(self._TCL_COMMAND): if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): line._kind |= LineKind.Success # FIXME: use similar style like for _TIME line = yield line lastLine = yield line return lastLine elif line.StartsWith("Finished RTL Optimization Phase"): line._kind = LineKind.PhaseEnd line._previousLine._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter elif line.StartsWith("----"): if LineKind.Phase in line._previousLine._kind: line._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter line = yield line line = yield line while True: if line.StartsWith("Finished"): l = line[9:] if not (l.startswith("Flattening") or l.startswith("Final")): line = yield section.send(line) break if isinstance(line, VivadoMessage): self._AddMessage(line) line = yield section.send(line) line = yield section.send(line) activeParsers.remove(parser)
[docs] @export class LinkDesign(Command): """ A Vivado command output parser for ``link_design``. """ _TCL_COMMAND: ClassVar[str] = "link_design" _TIME: ClassVar[str] = "Time (s):" _ParsingXDCFile_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\]$""") _FinishedParsingXDCFile_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\]$""") _ParsingXDCFileForCell_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\] for cell '(.*)'$""") _FinishedParsingXDCFileForCell_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\] for cell '(.*)'$""") _commonXDCFiles: Dict[Path, List[VivadoMessage]] _perCellXDCFiles: Dict[Path, Dict[str, List[VivadoMessage]]]
[docs] def __init__(self, processor: "Processor") -> None: super().__init__(processor) self._commonXDCFiles = {} self._perCellXDCFiles = {}
@readonly def CommonXDCFiles(self) -> Dict[Path, List[VivadoMessage]]: return self._commonXDCFiles @readonly def PerCellXDCFiles(self) -> Dict[Path, Dict[str, List[VivadoMessage]]]: return self._perCellXDCFiles def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: line = yield from self._CommandStart(line) end = f"{self._TCL_COMMAND} " while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif (match := self._ParsingXDCFile_Pattern.match(line._message)) is not None: line._kind = LineKind.Normal path = Path(match[1]) self._commonXDCFiles[path] = (messages := []) line = yield line while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) messages.append(line) elif (match := self._FinishedParsingXDCFile_Pattern.match(line._message)) is not None and path == Path(match[1]): line._kind = LineKind.Normal break elif line.StartsWith("Finished Parsing XDC File"): line._kind = LineKind.ProcessorError break elif line.StartsWith(end): break line = yield line elif (match := self._ParsingXDCFileForCell_Pattern.match(line._message)) is not None: line._kind = LineKind.Normal path = Path(match[1]) cell = match[2] if path in self._perCellXDCFiles: self._perCellXDCFiles[path][cell] = (messages := []) else: self._perCellXDCFiles[path] = {cell: (messages := [])} line = yield line while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) messages.append(line) elif (match := self._FinishedParsingXDCFileForCell_Pattern.match(line._message)) is not None and path == Path(match[1]) and cell == match[2]: line._kind = LineKind.Normal break elif line.StartsWith("Finished Parsing XDC File"): line._kind = LineKind.ProcessorError break elif line.StartsWith(end): break line = yield line if line.StartsWith(end): nextLine = yield from self._CommandFinish(line) return nextLine line = yield line
[docs] @export class OptimizeDesign(CommandWithTasks): """ A Vivado command output parser for ``opt_design``. """ _TCL_COMMAND: ClassVar[str] = "opt_design" _TIME: ClassVar[str] = None _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( DRCTask, CacheTimingInformationTask, LogicOptimizationTask, PowerOptimizationTask, FinalCleanupTask, NetlistObfuscationTask ) def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: line = yield from self._CommandStart(line) activeParsers: List[Task] = list(self._tasks.values()) while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith("Starting ") and not line.StartsWith("Starting Connectivity Check Task"): for parser in activeParsers: # type: Section if line.StartsWith(parser._START): line = yield next(task := parser.Generator(line)) break else: WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") # ex.add_note(f"Current task: start pattern='{self._task}'") ex.add_note(f"Current cmd: {self}") raise ex break elif line.StartsWith(self._TCL_COMMAND): if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): line._kind |= LineKind.Success # FIXME: use similar style like for _TIME line = yield line lastLine = yield line return lastLine # line._kind = LineKind.Unprocessed line = yield line while True: # if line.StartsWith("Ending"): # line = yield task.send(line) # break if isinstance(line, VivadoMessage): self._AddMessage(line) try: line = yield task.send(line) except StopIteration as ex: task = None line = ex.value if isinstance(line, VivadoMessage): line = yield line break if task is not None: line = yield task.send(line) activeParsers.remove(parser)
[docs] @export class PlaceDesign(CommandWithTasks): """ A Vivado command output parser for ``place_design``. """ _TCL_COMMAND: ClassVar[str] = "place_design" _TIME: ClassVar[str] = None _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( PlacerTask, ) def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: line = yield from self._CommandStart(line) activeParsers: List[Task] = list(self._tasks.values()) while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith("Starting "): for parser in activeParsers: # type: Section if line.StartsWith(parser._START): line = yield next(task := parser.Generator(line)) break else: WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") # ex.add_note(f"Current task: start pattern='{self._task}'") ex.add_note(f"Current cmd: {self}") raise ex break elif line.StartsWith(self._TCL_COMMAND): if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): line._kind |= LineKind.Success # FIXME: use similar style like for _TIME line = yield line lastLine = yield line return lastLine # line._kind = LineKind.Unprocessed line = yield line while True: # if line.StartsWith("Ending"): # line = yield task.send(line) # break if isinstance(line, VivadoMessage): self._AddMessage(line) try: line = yield task.send(line) except StopIteration as ex: task = None line = ex.value if isinstance(line, VivadoMessage): line = yield line break if task is not None: line = yield task.send(line) activeParsers.remove(parser)
[docs] @export class PhysicalOptimizeDesign(CommandWithTasks): """ A Vivado command output parser for ``phy_opt_design``. """ _TCL_COMMAND: ClassVar[str] = "phys_opt_design" _TIME: ClassVar[str] = None _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( InitialUpdateTimingTask, PhysicalSynthesisTask ) def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: line = yield from self._CommandStart(line) activeParsers: List[Task] = list(self._tasks.values()) while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith("Starting "): for parser in activeParsers: # type: Section if line.StartsWith(parser._START): line = yield next(task := parser.Generator(line)) break else: WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") # ex.add_note(f"Current task: start pattern='{self._task}'") ex.add_note(f"Current cmd: {self}") raise ex break elif line.StartsWith(self._TCL_COMMAND): if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): line._kind |= LineKind.Success # FIXME: use similar style like for _TIME line = yield line lastLine = yield line return lastLine # line._kind = LineKind.Unprocessed line = yield line while True: # if line.StartsWith("Ending"): # line = yield task.send(line) # break if isinstance(line, VivadoMessage): self._AddMessage(line) try: line = yield task.send(line) except StopIteration as ex: task = None line = ex.value if isinstance(line, VivadoMessage): line = yield line break if task is not None: line = yield task.send(line) activeParsers.remove(parser)
[docs] @export class RouteDesign(CommandWithTasks): """ A Vivado command output parser for ``route_design``. """ _TCL_COMMAND: ClassVar[str] = "route_design" _TIME: ClassVar[str] = "Time (s):" _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( RoutingTask, ) def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: line = yield from self._CommandStart(line) activeParsers: List[Task] = list(self._tasks.values()) while True: while True: if line._kind is LineKind.Empty: line = yield line continue elif isinstance(line, VivadoMessage): self._AddMessage(line) elif line.StartsWith("Starting "): for parser in activeParsers: # type: Section if line.StartsWith(parser._START): line = yield next(task := parser.Generator(line)) break else: WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") # ex.add_note(f"Current task: start pattern='{self._task}'") ex.add_note(f"Current cmd: {self}") raise ex break elif line.StartsWith(self._TCL_COMMAND): if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): line._kind |= LineKind.Success # FIXME: use similar style like for _TIME line = yield line lastLine = yield line return lastLine # line._kind = LineKind.Unprocessed line = yield line while True: # if line.StartsWith("Ending"): # line = yield task.send(line) # break if isinstance(line, VivadoMessage): self._AddMessage(line) try: line = yield task.send(line) except StopIteration as ex: task = None line = ex.value if isinstance(line, VivadoMessage): line = yield line break if task is not None: line = yield task.send(line) activeParsers.remove(parser)
[docs] @export class WriteBitstream(Command): """ A Vivado command output parser for ``write_bitstream``. """ _TCL_COMMAND: ClassVar[str] = "write_bitstream" _TIME: ClassVar[str] = "Time (s):"
[docs] @export class ReportDRC(Command): """ A Vivado command output parser for ``report_drc``. """ _TCL_COMMAND: ClassVar[str] = "report_drc" _TIME: ClassVar[str] = "Time (s):"
[docs] @export class ReportMethodology(Command): """ A Vivado command output parser for ``report_methodology``. """ _TCL_COMMAND: ClassVar[str] = "report_methodology" _TIME: ClassVar[str] = None
[docs] @export class ReportPower(Command): """ A Vivado command output parser for ``report_power``. """ _TCL_COMMAND: ClassVar[str] = "report_power" _TIME: ClassVar[str] = None