Coverage for pyEDAA/OutputFilter/Xilinx/SynthesizeDesign.py: 82%
428 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-05 22:59 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-05 22:59 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ___ _ _ _____ _ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ / _ \ _ _| |_ _ __ _ _| |_| ___(_) | |_ ___ _ __ #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | | __| '_ \| | | | __| |_ | | | __/ _ \ '__| #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| | |_| | |_| |_) | |_| | |_| _| | | | || __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/ \__,_|\__| .__/ \__,_|\__|_| |_|_|\__\___|_| #
7# |_| |___/ |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2025-2026 Electronic Design Automation Abstraction (EDA²) #
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"""A filtering anc classification processor for AMD/Xilinx Vivado Synthesis outputs."""
32from re import compile as re_compile
33from typing import ClassVar, Dict, Generator, Type, Optional as Nullable, Any, Union
35from pyTooling.Common import getFullyQualifiedName
36from pyTooling.Decorators import export, readonly
37from pyTooling.MetaClasses import ExtendedType, abstractmethod
39from pyEDAA.OutputFilter.Xilinx.Exception import NotPresentException
40from pyEDAA.OutputFilter.Xilinx.Common import VHDLAssertionMessage, Line, LineKind, VivadoInfoMessage, VHDLReportMessage, VivadoMessage
41from pyEDAA.OutputFilter.Xilinx.Common2 import BaseParser
43TIME_MEMORY_PATTERN = re_compile(r"""Time \(s\): cpu = (\d{2}:\d{2}:\d{2}) ; elapsed = (\d{2}:\d{2}:\d{2}) . Memory \(MB\): peak = (\d+\.\d+) ; gain = (\d+\.\d+)""")
46@export
47class SubSectionNotPresentException(NotPresentException):
48 pass
51@export
52class BaseSection(metaclass=ExtendedType, mixin=True):
53 @abstractmethod
54 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
55 pass
57 @abstractmethod
58 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]:
59 pass
61 @abstractmethod
62 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
63 pass
66@export
67class Section(BaseParser, BaseSection):
68 """
69 Base-class for sections within log outputs from *synthesize design*.
70 """
71 # _NAME: ClassVar[str]
72 # _START: ClassVar[str]
73 # _FINISH: ClassVar[str]
75 _command: "Command" #: Reference to the command (parent).
76 _duration: float #: Duration synthesis spent in processing a synthesis step logged in this log output section.
78 def __init__(self, command: "Command") -> None:
79 """
80 Initialized a section.
82 :param command: Reference to the parent TCL command.
83 """
84 super().__init__() #command._processor)
86 self._command = command
87 self._duration = 0.0
89 @readonly
90 def Duration(self) -> float:
91 """
92 Read-only property to access the duration synthesis spent in processing a synthesis step logged in this log output
93 section.
95 :returns: Synthesis step duration in seconds.
96 """
97 return self._duration
99 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
100 line._kind = LineKind.SectionStart
102 line = yield line
103 if line.StartsWith("----"): 103 ↛ 106line 103 didn't jump to line 106 because the condition on line 103 was always true
104 line._kind = LineKind.SectionStart | LineKind.SectionDelimiter
105 else:
106 line._kind |= LineKind.ProcessorError
108 nextLine = yield line
109 return nextLine
111 def _SectionFinish(self, line: Line, skipDashes: bool = False) -> Generator[Line, Line, None]:
112 if not skipDashes:
113 if line.StartsWith("----"): 113 ↛ 116line 113 didn't jump to line 116 because the condition on line 113 was always true
114 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
115 else:
116 line._kind |= LineKind.ProcessorError
118 line = yield line
120 if line.StartsWith(self._FINISH): 120 ↛ 123line 120 didn't jump to line 123 because the condition on line 120 was always true
121 line._kind = LineKind.SectionEnd
122 else:
123 line._kind |= LineKind.ProcessorError
125 line = yield line
126 if line.StartsWith("----"): 126 ↛ 129line 126 didn't jump to line 129 because the condition on line 126 was always true
127 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
128 else:
129 line._kind |= LineKind.ProcessorError
131 nextLine = yield line
132 return nextLine
134 # @mustoverride
135 # def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
136 # if len(line) == 0:
137 # return ProcessingState.EmptyLine
138 # elif line.startswith("----"):
139 # return ProcessingState.DelimiterLine
140 # elif line.startswith(self._START):
141 # return ProcessingState.Skipped
142 # elif line.startswith(self._FINISH):
143 # l = line[len(self._FINISH):]
144 # if (match := TIME_MEMORY_PATTERN.match(l)) is not None:
145 # # cpuParts = match[1].split(":")
146 # elapsedParts = match[2].split(":")
147 # # peakMemory = float(match[3])
148 # # gainMemory = float(match[4])
149 # self._duration = int(elapsedParts[0]) * 3600 + int(elapsedParts[1]) * 60 + int(elapsedParts[2])
150 #
151 # return ProcessingState.Skipped | ProcessingState.Last
152 # elif line.startswith("Start") or line.startswith("Starting"):
153 # print(f"ERROR: didn't find finish\n {line}")
154 # return ProcessingState.Reprocess
155 #
156 # return ProcessingState.Skipped
158 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
159 line = yield from self._SectionStart(line)
161 while True:
162 if line._kind is LineKind.Empty: 162 ↛ 163line 162 didn't jump to line 163 because the condition on line 162 was never true
163 line = yield line
164 continue
165 elif line.StartsWith("----"):
166 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
167 break
168 elif isinstance(line, VivadoMessage):
169 self._AddMessage(line)
170 else:
171 line._kind = LineKind.Verbose
173 line = yield line
175 # line = yield line
176 nextLine = yield from self._SectionFinish(line)
177 return nextLine
180@export
181class SubSection(BaseParser, BaseSection):
182 """
183 Base-class for subsections within log outputs from *synthesize design*.
184 """
185 # _NAME: ClassVar[str]
187 _section: Section #: Reference to the section (parent).
189 def __init__(self, section: Section) -> None:
190 """
191 Initialized a subsection.
193 :param section: Reference to the parent section.
194 """
195 super().__init__()
196 self._section = section
198 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
199 line._kind = LineKind.SubSectionStart
201 line = yield line
202 if line.StartsWith("----"): 202 ↛ 205line 202 didn't jump to line 205 because the condition on line 202 was always true
203 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter
204 else:
205 line._kind |= LineKind.ProcessorError
207 nextLine = yield line
208 return nextLine
210 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]:
211 if line.StartsWith("----"): 211 ↛ 214line 211 didn't jump to line 214 because the condition on line 211 was always true
212 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
213 else:
214 line._kind |= LineKind.ProcessorError
216 line = yield line
217 if line.StartsWith(self._FINISH): 217 ↛ 220line 217 didn't jump to line 220 because the condition on line 217 was always true
218 line._kind = LineKind.SubSectionEnd
219 else:
220 line._kind |= LineKind.ProcessorError
222 line = yield line
223 if line.StartsWith("----"): 223 ↛ 226line 223 didn't jump to line 226 because the condition on line 223 was always true
224 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
225 else:
226 line._kind |= LineKind.ProcessorError
228 nextLine = yield line
229 return nextLine
231 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
232 line = yield from self._SectionStart(line)
234 while True:
235 if line._kind is LineKind.Empty: 235 ↛ 236line 235 didn't jump to line 236 because the condition on line 235 was never true
236 line = yield line
237 continue
238 elif line.StartsWith("----"): 238 ↛ 241line 238 didn't jump to line 241 because the condition on line 238 was always true
239 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
240 break
241 elif isinstance(line, VivadoMessage):
242 self._AddMessage(line)
243 else:
244 line._kind = LineKind.Verbose
246 line = yield line
248 nextLine = yield from self._SectionFinish(line)
249 return nextLine
252@export
253class SectionWithChildren(Section):
254 """
255 Base-class for sections with subsections.
256 """
257 _subsections: Dict[Type[SubSection], SubSection]
259 def __init__(self, command: "Command") -> None:
260 super().__init__(command)
262 self._subsections = {}
264 def __contains__(self, key: Any) -> bool:
265 if not issubclass(key, SubSection): 265 ↛ 266line 265 didn't jump to line 266 because the condition on line 265 was never true
266 ex = TypeError(f"Parameter 'item' is not a SubSection.")
267 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.")
268 raise ex
270 return key in self._subsections
272 def __getitem__(self, item: Type[SubSection]) -> SubSection:
273 try:
274 return self._sections[item]
275 except KeyError as ex:
276 raise SubSectionNotPresentException(f"SubSection '{item._NAME}' not present in '{self._parent._parent.logfile}'.") from ex
279@export
280class RTLElaboration(Section):
281 """
282 *RTL Elaboration* section.
284 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
285 """
286 _NAME: ClassVar[str] = "RTL Elaboration"
287 _START: ClassVar[str] = "Starting RTL Elaboration : "
288 _FINISH: ClassVar[str] = "Finished RTL Elaboration : "
290 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
291 line = yield from self._SectionStart(line)
293 while True:
294 if line._kind is LineKind.Empty: 294 ↛ 295line 294 didn't jump to line 295 because the condition on line 294 was never true
295 line = yield line
296 continue
297 elif isinstance(line, VivadoInfoMessage):
298 if line._toolID == 8:
299 if line._messageKindID == 63: # VHDL assert statement
300 newLine = VHDLAssertionMessage.Convert(line)
301 if newLine is None: 301 ↛ 302line 301 didn't jump to line 302 because the condition on line 301 was never true
302 pass
303 else:
304 line = newLine
305 elif line._messageKindID == 6031: # VHDL report statement
306 newLine = VHDLReportMessage.Convert(line)
307 if newLine is None: 307 ↛ 308line 307 didn't jump to line 308 because the condition on line 307 was never true
308 pass
309 else:
310 line = newLine
312 if line.StartsWith("----"):
313 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
314 break
315 elif isinstance(line, VivadoMessage):
316 self._AddMessage(line)
317 else:
318 line._kind = LineKind.Verbose
320 line = yield line
322 # line = yield line
323 nextLine = yield from self._SectionFinish(line)
324 return nextLine
327@export
328class HandlingCustomAttributes(Section):
329 """
330 *Handling Custom Attributes* section.
332 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
333 """
334 _NAME: ClassVar[str] = "Handling Custom Attributes"
335 _START: ClassVar[str] = "Start Handling Custom Attributes"
336 _FINISH: ClassVar[str] = "Finished Handling Custom Attributes : "
339@export
340class ConstraintValidation(Section):
341 """
342 *Constraint Validation* section.
344 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
345 """
346 _NAME: ClassVar[str] = "Constraint Validation"
347 _START: ClassVar[str] = "Finished RTL Optimization Phase 1"
348 _FINISH: ClassVar[str] = "Finished Constraint Validation : "
351@export
352class LoadingPart(Section):
353 """
354 *Loading Part and Timing Information* section.
356 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
357 """
358 _NAME: ClassVar[str] = "Loading Part and Timing Information"
359 _START: ClassVar[str] = "Start Loading Part and Timing Information"
360 _FINISH: ClassVar[str] = "Finished Loading Part and Timing Information : "
362 _part: Nullable[str] #: Part name of the device this design was synthesized for.
364 def __init__(self, command: "Command") -> None:
365 """
366 Initializes the section for loading the device information.
368 :param command: Reference to the TCL command.
369 """
370 super().__init__(command)
372 self._part = None
374 @readonly
375 def Part(self) -> Nullable[str]:
376 """
377 Read-only property to access the used device's part name.
379 :returns: Part name of the device this design was synthesized for.
380 """
381 return self._part
383 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
384 line = yield from self._SectionStart(line)
386 while True:
387 if line._kind is LineKind.Empty: 387 ↛ 388line 387 didn't jump to line 388 because the condition on line 387 was never true
388 line = yield line
389 continue
390 elif line.StartsWith("Loading part: "):
391 line._kind = LineKind.Normal
392 self._part = line._message[14:].strip()
393 elif line.StartsWith("----"):
394 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
395 break
396 elif isinstance(line, VivadoMessage): 396 ↛ 399line 396 didn't jump to line 399 because the condition on line 396 was always true
397 self._AddMessage(line)
398 else:
399 line._kind = LineKind.Verbose
401 line = yield line
403 nextLine = yield from self._SectionFinish(line)
404 return nextLine
406@export
407class ApplySetProperty(Section):
408 """
409 *Applying 'set_property' XDC Constraints* section.
411 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
412 """
413 _NAME: ClassVar[str] = "Applying 'set_property' XDC Constraints"
414 _START: ClassVar[str] = "Start Applying 'set_property' XDC Constraints"
415 _FINISH: ClassVar[str] = "Finished applying 'set_property' XDC Constraints : "
418@export
419class RTLComponentStatistics(Section):
420 """
421 *RTL Component Statistics* section.
423 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
424 """
425 _NAME: ClassVar[str] = "RTL Component Statistics"
426 _START: ClassVar[str] = "Start RTL Component Statistics"
427 _FINISH: ClassVar[str] = "Finished RTL Component Statistics"
429 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
430 line = yield from self._SectionStart(line)
432 while True:
433 if line._kind is LineKind.Empty: 433 ↛ 434line 433 didn't jump to line 434 because the condition on line 433 was never true
434 line = yield line
435 continue
436 elif line.StartsWith("----"):
437 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
438 break
439 elif isinstance(line, VivadoMessage): 439 ↛ 440line 439 didn't jump to line 440 because the condition on line 439 was never true
440 self._AddMessage(line)
441 else:
442 line._kind = LineKind.Verbose
444 line = yield line
446 nextLine = yield from self._SectionFinish(line)
447 return nextLine
450@export
451class RTLHierarchicalComponentStatistics(Section):
452 """
453 *RTL Hierarchical Component Statistics* section.
455 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
456 """
457 _NAME: ClassVar[str] = "RTL Hierarchical Component Statistics"
458 _START: ClassVar[str] = "Start RTL Hierarchical Component Statistics"
459 _FINISH: ClassVar[str] = "Finished RTL Hierarchical Component Statistics"
462@export
463class PartResourceSummary(Section):
464 """
465 *Part Resource Summary* section.
467 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
468 """
469 _NAME: ClassVar[str] = "Part Resource Summary"
470 _START: ClassVar[str] = "Start Part Resource Summary"
471 _FINISH: ClassVar[str] = "Finished Part Resource Summary"
474@export
475class CrossBoundaryAndAreaOptimization(Section):
476 """
477 *Cross Boundary and Area Optimization* section.
479 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
480 """
481 _NAME: ClassVar[str] = "Cross Boundary and Area Optimization"
482 _START: ClassVar[str] = "Start Cross Boundary and Area Optimization"
483 _FINISH: ClassVar[str] = "Finished Cross Boundary and Area Optimization : "
486@export
487class ROM_RAM_DSP_SR_Retiming(Section):
488 """
489 *ROM, RAM, DSP, Shift Register and Retiming Reporting* section.
491 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
492 """
493 _NAME: ClassVar[str] = "ROM, RAM, DSP, Shift Register and Retiming Reporting"
494 _START: ClassVar[str] = "Start ROM, RAM, DSP, Shift Register and Retiming Reporting"
495 _FINISH: ClassVar[str] = "Finished ROM, RAM, DSP, Shift Register and Retiming Reporting"
498@export
499class ApplyingXDCTimingConstraints(Section):
500 """
501 *Applying XDC Timing Constraints* section.
503 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
504 """
505 _NAME: ClassVar[str] = "Applying XDC Timing Constraints"
506 _START: ClassVar[str] = "Start Applying XDC Timing Constraints"
507 _FINISH: ClassVar[str] = "Finished Applying XDC Timing Constraints : "
510@export
511class TimingOptimization(Section):
512 """
513 *Timing Optimization* section.
515 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
516 """
517 _NAME: ClassVar[str] = "Timing Optimization"
518 _START: ClassVar[str] = "Start Timing Optimization"
519 _FINISH: ClassVar[str] = "Finished Timing Optimization : "
522@export
523class TechnologyMapping(Section):
524 """
525 *Technology Mapping* section.
527 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
528 """
529 _NAME: ClassVar[str] = "Technology Mapping"
530 _START: ClassVar[str] = "Start Technology Mapping"
531 _FINISH: ClassVar[str] = "Finished Technology Mapping : "
534@export
535class FlatteningBeforeIOInsertion(SubSection):
536 """
537 *Flattening Before IO Insertion* subsection.
539 Used by section :class:`IOInsertion`.
540 """
541 _NAME: ClassVar[str] = "Flattening Before IO Insertion"
542 _START: ClassVar[str] = "Start Flattening Before IO Insertion"
543 _FINISH: ClassVar[str] = "Finished Flattening Before IO Insertion"
546@export
547class FinalNetlistCleanup(SubSection):
548 """
549 *Final Netlist Cleanup* subsection.
551 Used by section :class:`IOInsertion`.
552 """
553 _NAME: ClassVar[str] = "Final Netlist Cleanup"
554 _START: ClassVar[str] = "Start Final Netlist Cleanup"
555 _FINISH: ClassVar[str] = "Finished Final Netlist Cleanup"
558@export
559class IOInsertion(SectionWithChildren):
560 """
561 *IO Insertion* section.
563 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
564 """
565 _NAME: ClassVar[str] = "IO Insertion"
566 _START: ClassVar[str] = "Start IO Insertion"
567 _FINISH: ClassVar[str] = "Finished IO Insertion : "
569 # TODO: generalize, use _PARSERS and move to SectionWithChildren
570 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
571 line = yield from self._SectionStart(line)
573 while True:
574 while True:
575 if line._kind is LineKind.Empty: 575 ↛ 576line 575 didn't jump to line 576 because the condition on line 575 was never true
576 line = yield line
577 continue
578 elif line.StartsWith("----"):
579 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter
580 elif line.StartsWith("Start "):
581 if line == FlatteningBeforeIOInsertion._START:
582 self._subsections[FlatteningBeforeIOInsertion] = (subsection := FlatteningBeforeIOInsertion(self))
583 line = yield next(parser := subsection.Generator(line))
584 break
585 elif line == FinalNetlistCleanup._START: 585 ↛ 596line 585 didn't jump to line 596 because the condition on line 585 was always true
586 self._subsections[FinalNetlistCleanup] = (subsection := FinalNetlistCleanup(self))
587 line = yield next(parser := subsection.Generator(line))
588 break
589 elif isinstance(line, VivadoMessage): 589 ↛ 590line 589 didn't jump to line 590 because the condition on line 589 was never true
590 self._AddMessage(line)
591 elif line.StartsWith("Finished "): 591 ↛ 594line 591 didn't jump to line 594 because the condition on line 591 was always true
592 break
593 else:
594 line._kind |= LineKind.ProcessorError
596 line = yield line
598 if line.StartsWith(self._FINISH):
599 break
601 while True:
602 if line.StartsWith(subsection._FINISH):
603 line = yield parser.send(line)
604 line = yield parser.send(line)
606 subsection = None
607 parser = None
608 break
610 line = parser.send(line)
611 if isinstance(line, VivadoMessage): 611 ↛ 612line 611 didn't jump to line 612 because the condition on line 611 was never true
612 self._AddMessage(line)
614 line = yield line
616 nextLine = yield from self._SectionFinish(line, True)
617 return nextLine
620@export
621class RenamingGeneratedInstances(Section):
622 """
623 *Renaming Generated Instances* section.
625 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
626 """
627 _NAME: ClassVar[str] = "Renaming Generated Instances"
628 _START: ClassVar[str] = "Start Renaming Generated Instances"
629 _FINISH: ClassVar[str] = "Finished Renaming Generated Instances : "
632@export
633class RebuildingUserHierarchy(Section):
634 """
635 *Rebuilding User Hierarchy* section.
637 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
638 """
639 _NAME: ClassVar[str] = "Rebuilding User Hierarchy"
640 _START: ClassVar[str] = "Start Rebuilding User Hierarchy"
641 _FINISH: ClassVar[str] = "Finished Rebuilding User Hierarchy : "
644@export
645class RenamingGeneratedPorts(Section):
646 """
647 *Renaming Generated Ports* section.
649 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
650 """
651 _NAME: ClassVar[str] = "Renaming Generated Ports"
652 _START: ClassVar[str] = "Start Renaming Generated Ports"
653 _FINISH: ClassVar[str] = "Finished Renaming Generated Ports : "
656@export
657class RenamingGeneratedNets(Section):
658 """
659 *Renaming Generated Nets* section.
661 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
662 """
663 _NAME: ClassVar[str] = "Renaming Generated Nets"
664 _START: ClassVar[str] = "Start Renaming Generated Nets"
665 _FINISH: ClassVar[str] = "Finished Renaming Generated Nets : "
668@export
669class WritingSynthesisReport(Section):
670 """
671 *Writing Synthesis Report* section.
673 Used by Vivado command :class:`~pyEDAA.OutputFilter.Xilinx.Commands.SynthesizeDesign`.
674 """
675 _NAME: ClassVar[str] = "Writing Synthesis Report"
676 _START: ClassVar[str] = "Start Writing Synthesis Report"
677 _FINISH: ClassVar[str] = "Finished Writing Synthesis Report : "
679 _blackboxes: Dict[str, int] #: Blackbox statistics: blackbox name -> count
680 _cells: Dict[str, int] #: Cell statistics: cell name -> count
682 def __init__(self, command: "Command") -> None:
683 super().__init__(command)
685 self._blackboxes = {}
686 self._cells = {}
688 @readonly
689 def Cells(self) -> Dict[str, int]:
690 """
691 Read-only property to access the dictionary of synthesized cell statistics.
693 :returns: The dictionary of used cell statistics.
694 """
695 return self._cells
697 @readonly
698 def Blackboxes(self) -> Dict[str, int]:
699 """
700 Read-only property to access the dictionary of found blackbox statistics.
702 :returns: The dictionary of found blackbox statistics.
703 """
704 return self._blackboxes
706 def _BlackboxesGenerator(self, line: Line) -> Generator[Line, Line, Line]:
707 """
708 A parser parsing the blackboxes table.
710 :param line: First line to process.
711 :returns: A generator to process multiple lines containing a table of blackboxes.
713 .. rubric:: Example
715 .. code-block::
717 Report BlackBoxes:
718 +------+----------------------------------+----------+
719 | |BlackBox name |Instances |
720 +------+----------------------------------+----------+
721 |1 |name | 1|
722 |[...] |[...] | [...]|
723 +------+----------------------------------+----------+
724 """
725 if line.StartsWith("+-"): 725 ↛ 728line 725 didn't jump to line 728 because the condition on line 725 was always true
726 line._kind = LineKind.TableFrame
727 else:
728 line._kind = LineKind.ProcessorError
730 line = yield line
731 if line.StartsWith("| "): 731 ↛ 734line 731 didn't jump to line 734 because the condition on line 731 was always true
732 line._kind = LineKind.TableHeader
733 else:
734 line._kind = LineKind.ProcessorError
736 line = yield line
737 if line.StartsWith("+-"): 737 ↛ 740line 737 didn't jump to line 740 because the condition on line 737 was always true
738 line._kind = LineKind.TableFrame
739 else:
740 line._kind = LineKind.ProcessorError
742 line = yield line
743 while True:
744 if line.StartsWith("|"):
745 line._kind = LineKind.TableRow
747 columns = line._message.strip("|").split("|")
748 self._blackboxes[columns[1].strip()] = int(columns[2].strip())
749 elif line.StartsWith("+-"): 749 ↛ 753line 749 didn't jump to line 753 because the condition on line 749 was always true
750 line._kind = LineKind.TableFrame
751 break
752 else:
753 line._kind = LineKind.ProcessorError
755 line = yield line
757 nextLine = yield line
758 return nextLine
760 def _CellGenerator(self, line: Line) -> Generator[Line, Line, Line]:
761 """
762 A parser parsing the cell statistic table.
764 :param line: First line to process.
765 :returns: A generator to process multiple lines containing a table of cell statistics.
767 .. rubric:: Example
769 .. code-block::
771 Report Cell Usage:
772 +------+----------------------------------+------+
773 | |Cell |Count |
774 +------+----------------------------------+------+
775 |1 |name | 1|
776 |[...] |[...] | [...]|
777 +------+----------------------------------+------+
778 """
779 if line.StartsWith("+-"): 779 ↛ 782line 779 didn't jump to line 782 because the condition on line 779 was always true
780 line._kind = LineKind.TableFrame
781 else:
782 line._kind = LineKind.ProcessorError
784 line = yield line
785 if line.StartsWith("| "): 785 ↛ 788line 785 didn't jump to line 788 because the condition on line 785 was always true
786 line._kind = LineKind.TableHeader
787 else:
788 line._kind = LineKind.ProcessorError
790 line = yield line
791 if line.StartsWith("+-"): 791 ↛ 794line 791 didn't jump to line 794 because the condition on line 791 was always true
792 line._kind = LineKind.TableFrame
793 else:
794 line._kind = LineKind.ProcessorError
796 line = yield line
797 while True:
798 if line.StartsWith("|"):
799 line._kind = LineKind.TableRow
801 columns = line._message.strip("|").split("|")
802 self._cells[columns[1].strip()] = int(columns[2].strip())
803 elif line.StartsWith("+-"): 803 ↛ 807line 803 didn't jump to line 807 because the condition on line 803 was always true
804 line._kind = LineKind.TableFrame
805 break
806 else:
807 line._kind = LineKind.ProcessorError
809 line = yield line
811 nextLine = yield line
812 return nextLine
814 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
815 line = yield from self._SectionStart(line)
817 while True:
818 if line._kind is LineKind.Empty:
819 line = yield line
820 continue
821 elif line.StartsWith("Report BlackBoxes:"):
822 line._kind = LineKind.ParagraphHeadline
823 line = yield line
824 line = yield from self._BlackboxesGenerator(line)
825 elif line.StartsWith("Report Cell Usage:"):
826 line._kind = LineKind.ParagraphHeadline
827 line = yield line
828 line = yield from self._CellGenerator(line)
829 elif line.StartsWith("----"):
830 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
831 break
832 elif isinstance(line, VivadoMessage): 832 ↛ 833line 832 didn't jump to line 833 because the condition on line 832 was never true
833 self._AddMessage(line)
834 else:
835 line._kind = LineKind.Verbose
836 line = yield line
838 nextLine = yield from self._SectionFinish(line)
839 return nextLine