Coverage for pyEDAA / OutputFilter / Xilinx / SynthesizeDesign.py: 82%
389 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:13 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-23 22:13 +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
35from pyTooling.Decorators import export, readonly
36from pyTooling.MetaClasses import ExtendedType, abstractmethod
38from pyEDAA.OutputFilter.Xilinx.Common import VHDLAssertionMessage, Line, LineKind, VivadoInfoMessage, \
39 VHDLReportMessage, VivadoMessage, VivadoWarningMessage, VivadoCriticalWarningMessage, VivadoErrorMessage
40from pyEDAA.OutputFilter.Xilinx.Common2 import BaseParser
42TIME_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+)""")
45@export
46class BaseSection(metaclass=ExtendedType, mixin=True):
47 @abstractmethod
48 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
49 pass
51 @abstractmethod
52 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]:
53 pass
55 @abstractmethod
56 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
57 pass
60@export
61class Section(BaseParser, BaseSection):
62 # _START: ClassVar[str]
63 # _FINISH: ClassVar[str]
65 _command: "Command"
66 _duration: float
68 def __init__(self, command: "Command") -> None:
69 super().__init__() #command._processor)
71 self._command = command
72 self._duration = 0.0
74 @readonly
75 def Duration(self) -> float:
76 return self._duration
78 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
79 line._kind = LineKind.SectionStart
81 line = yield line
82 if line.StartsWith("----"): 82 ↛ 85line 82 didn't jump to line 85 because the condition on line 82 was always true
83 line._kind = LineKind.SectionStart | LineKind.SectionDelimiter
84 else:
85 line._kind |= LineKind.ProcessorError
87 nextLine = yield line
88 return nextLine
90 def _SectionFinish(self, line: Line, skipDashes: bool = False) -> Generator[Line, Line, None]:
91 if not skipDashes:
92 if line.StartsWith("----"): 92 ↛ 95line 92 didn't jump to line 95 because the condition on line 92 was always true
93 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
94 else:
95 line._kind |= LineKind.ProcessorError
97 line = yield line
99 if line.StartsWith(self._FINISH): 99 ↛ 102line 99 didn't jump to line 102 because the condition on line 99 was always true
100 line._kind = LineKind.SectionEnd
101 else:
102 line._kind |= LineKind.ProcessorError
104 line = yield line
105 if line.StartsWith("----"): 105 ↛ 108line 105 didn't jump to line 108 because the condition on line 105 was always true
106 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
107 else:
108 line._kind |= LineKind.ProcessorError
110 nextLine = yield line
111 return nextLine
113 # @mustoverride
114 # def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
115 # if len(line) == 0:
116 # return ProcessingState.EmptyLine
117 # elif line.startswith("----"):
118 # return ProcessingState.DelimiterLine
119 # elif line.startswith(self._START):
120 # return ProcessingState.Skipped
121 # elif line.startswith(self._FINISH):
122 # l = line[len(self._FINISH):]
123 # if (match := TIME_MEMORY_PATTERN.match(l)) is not None:
124 # # cpuParts = match[1].split(":")
125 # elapsedParts = match[2].split(":")
126 # # peakMemory = float(match[3])
127 # # gainMemory = float(match[4])
128 # self._duration = int(elapsedParts[0]) * 3600 + int(elapsedParts[1]) * 60 + int(elapsedParts[2])
129 #
130 # return ProcessingState.Skipped | ProcessingState.Last
131 # elif line.startswith("Start") or line.startswith("Starting"):
132 # print(f"ERROR: didn't find finish\n {line}")
133 # return ProcessingState.Reprocess
134 #
135 # return ProcessingState.Skipped
137 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
138 line = yield from self._SectionStart(line)
140 while True:
141 if line._kind is LineKind.Empty: 141 ↛ 142line 141 didn't jump to line 142 because the condition on line 141 was never true
142 line = yield line
143 continue
144 elif line.StartsWith("----"):
145 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
146 break
147 elif isinstance(line, VivadoMessage):
148 self._AddMessage(line)
149 else:
150 line._kind = LineKind.Verbose
152 line = yield line
154 # line = yield line
155 nextLine = yield from self._SectionFinish(line)
156 return nextLine
159@export
160class SubSection(BaseParser, BaseSection):
161 _section: Section
163 def __init__(self, section: Section) -> None:
164 super().__init__()
165 self._section = section
167 def _SectionStart(self, line: Line) -> Generator[Line, Line, Line]:
168 line._kind = LineKind.SubSectionStart
170 line = yield line
171 if line.StartsWith("----"): 171 ↛ 174line 171 didn't jump to line 174 because the condition on line 171 was always true
172 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter
173 else:
174 line._kind |= LineKind.ProcessorError
176 nextLine = yield line
177 return nextLine
179 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]:
180 if line.StartsWith("----"): 180 ↛ 183line 180 didn't jump to line 183 because the condition on line 180 was always true
181 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
182 else:
183 line._kind |= LineKind.ProcessorError
185 line = yield line
186 if line.StartsWith(self._FINISH): 186 ↛ 189line 186 didn't jump to line 189 because the condition on line 186 was always true
187 line._kind = LineKind.SubSectionEnd
188 else:
189 line._kind |= LineKind.ProcessorError
191 line = yield line
192 if line.StartsWith("----"): 192 ↛ 195line 192 didn't jump to line 195 because the condition on line 192 was always true
193 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
194 else:
195 line._kind |= LineKind.ProcessorError
197 nextLine = yield line
198 return nextLine
200 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
201 line = yield from self._SectionStart(line)
203 while True:
204 if line._kind is LineKind.Empty: 204 ↛ 205line 204 didn't jump to line 205 because the condition on line 204 was never true
205 line = yield line
206 continue
207 elif line.StartsWith("----"): 207 ↛ 210line 207 didn't jump to line 210 because the condition on line 207 was always true
208 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter
209 break
210 elif isinstance(line, VivadoMessage):
211 self._AddMessage(line)
212 else:
213 line._kind = LineKind.Verbose
215 line = yield line
217 nextLine = yield from self._SectionFinish(line)
218 return nextLine
221@export
222class RTLElaboration(Section):
223 _START: ClassVar[str] = "Starting RTL Elaboration : "
224 _FINISH: ClassVar[str] = "Finished RTL Elaboration : "
226 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
227 line = yield from self._SectionStart(line)
229 while True:
230 if line._kind is LineKind.Empty: 230 ↛ 231line 230 didn't jump to line 231 because the condition on line 230 was never true
231 line = yield line
232 continue
233 elif isinstance(line, VivadoInfoMessage):
234 if line._toolID == 8:
235 if line._messageKindID == 63: # VHDL assert statement
236 newLine = VHDLAssertionMessage.Convert(line)
237 if newLine is None: 237 ↛ 238line 237 didn't jump to line 238 because the condition on line 237 was never true
238 pass
239 else:
240 line = newLine
241 elif line._messageKindID == 6031: # VHDL report statement
242 newLine = VHDLReportMessage.Convert(line)
243 if newLine is None: 243 ↛ 244line 243 didn't jump to line 244 because the condition on line 243 was never true
244 pass
245 else:
246 line = newLine
248 if line.StartsWith("----"):
249 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
250 break
251 elif isinstance(line, VivadoMessage):
252 self._AddMessage(line)
253 else:
254 line._kind = LineKind.Verbose
256 line = yield line
258 # line = yield line
259 nextLine = yield from self._SectionFinish(line)
260 return nextLine
263@export
264class HandlingCustomAttributes(Section):
265 _START: ClassVar[str] = "Start Handling Custom Attributes"
266 _FINISH: ClassVar[str] = "Finished Handling Custom Attributes : "
269@export
270class ConstraintValidation(Section):
271 _START: ClassVar[str] = "Finished RTL Optimization Phase 1"
272 _FINISH: ClassVar[str] = "Finished Constraint Validation : "
275@export
276class LoadingPart(Section):
277 _START: ClassVar[str] = "Start Loading Part and Timing Information"
278 _FINISH: ClassVar[str] = "Finished Loading Part and Timing Information : "
280 _part: str
282 def __init__(self, processor: "Processor") -> None:
283 super().__init__(processor)
285 self._part = None
287 @readonly
288 def Part(self) -> str:
289 return self._part
291 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
292 line = yield from self._SectionStart(line)
294 while True:
295 if line._kind is LineKind.Empty: 295 ↛ 296line 295 didn't jump to line 296 because the condition on line 295 was never true
296 line = yield line
297 continue
298 elif line.StartsWith("Loading part: "):
299 line._kind = LineKind.Normal
300 self._part = line._message[14:].strip()
301 elif line.StartsWith("----"):
302 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
303 break
304 elif isinstance(line, VivadoMessage): 304 ↛ 307line 304 didn't jump to line 307 because the condition on line 304 was always true
305 self._AddMessage(line)
306 else:
307 line._kind = LineKind.Verbose
309 line = yield line
311 nextLine = yield from self._SectionFinish(line)
312 return nextLine
314@export
315class ApplySetProperty(Section):
316 _START: ClassVar[str] = "Start Applying 'set_property' XDC Constraints"
317 _FINISH: ClassVar[str] = "Finished applying 'set_property' XDC Constraints : "
320@export
321class RTLComponentStatistics(Section):
322 _START: ClassVar[str] = "Start RTL Component Statistics"
323 _FINISH: ClassVar[str] = "Finished RTL Component Statistics"
325 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
326 line = yield from self._SectionStart(line)
328 while True:
329 if line._kind is LineKind.Empty: 329 ↛ 330line 329 didn't jump to line 330 because the condition on line 329 was never true
330 line = yield line
331 continue
332 elif line.StartsWith("----"):
333 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
334 break
335 elif isinstance(line, VivadoMessage): 335 ↛ 336line 335 didn't jump to line 336 because the condition on line 335 was never true
336 self._AddMessage(line)
337 else:
338 line._kind = LineKind.Verbose
340 line = yield line
342 nextLine = yield from self._SectionFinish(line)
343 return nextLine
346@export
347class RTLHierarchicalComponentStatistics(Section):
348 _START: ClassVar[str] = "Start RTL Hierarchical Component Statistics"
349 _FINISH: ClassVar[str] = "Finished RTL Hierarchical Component Statistics"
352@export
353class PartResourceSummary(Section):
354 _START: ClassVar[str] = "Start Part Resource Summary"
355 _FINISH: ClassVar[str] = "Finished Part Resource Summary"
358@export
359class CrossBoundaryAndAreaOptimization(Section):
360 _START: ClassVar[str] = "Start Cross Boundary and Area Optimization"
361 _FINISH: ClassVar[str] = "Finished Cross Boundary and Area Optimization : "
364@export
365class ROM_RAM_DSP_SR_Retiming(Section):
366 _START: ClassVar[str] = "Start ROM, RAM, DSP, Shift Register and Retiming Reporting"
367 _FINISH: ClassVar[str] = "Finished ROM, RAM, DSP, Shift Register and Retiming Reporting"
370@export
371class ApplyingXDCTimingConstraints(Section):
372 _START: ClassVar[str] = "Start Applying XDC Timing Constraints"
373 _FINISH: ClassVar[str] = "Finished Applying XDC Timing Constraints : "
376@export
377class TimingOptimization(Section):
378 _START: ClassVar[str] = "Start Timing Optimization"
379 _FINISH: ClassVar[str] = "Finished Timing Optimization : "
382@export
383class TechnologyMapping(Section):
384 _START: ClassVar[str] = "Start Technology Mapping"
385 _FINISH: ClassVar[str] = "Finished Technology Mapping : "
388@export
389class FlatteningBeforeIOInsertion(SubSection):
390 _START: ClassVar[str] = "Start Flattening Before IO Insertion"
391 _FINISH: ClassVar[str] = "Finished Flattening Before IO Insertion"
394@export
395class FinalNetlistCleanup(SubSection):
396 _START: ClassVar[str] = "Start Final Netlist Cleanup"
397 _FINISH: ClassVar[str] = "Finished Final Netlist Cleanup"
400@export
401class IOInsertion(Section):
402 _START: ClassVar[str] = "Start IO Insertion"
403 _FINISH: ClassVar[str] = "Finished IO Insertion : "
405 _subsections: Dict[Type[SubSection], SubSection]
407 def __init__(self, command: "Command") -> None:
408 super().__init__(command)
410 self._subsections = {}
412 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
413 line = yield from self._SectionStart(line)
415 while True:
416 while True:
417 if line._kind is LineKind.Empty: 417 ↛ 418line 417 didn't jump to line 418 because the condition on line 417 was never true
418 line = yield line
419 continue
420 elif line.StartsWith("----"):
421 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter
422 elif line.StartsWith("Start "):
423 if line == FlatteningBeforeIOInsertion._START:
424 self._subsections[FlatteningBeforeIOInsertion] = (subsection := FlatteningBeforeIOInsertion(self))
425 line = yield next(parser := subsection.Generator(line))
426 break
427 elif line == FinalNetlistCleanup._START: 427 ↛ 438line 427 didn't jump to line 438 because the condition on line 427 was always true
428 self._subsections[FinalNetlistCleanup] = (subsection := FinalNetlistCleanup(self))
429 line = yield next(parser := subsection.Generator(line))
430 break
431 elif isinstance(line, VivadoMessage): 431 ↛ 432line 431 didn't jump to line 432 because the condition on line 431 was never true
432 self._AddMessage(line)
433 elif line.StartsWith("Finished "): 433 ↛ 436line 433 didn't jump to line 436 because the condition on line 433 was always true
434 break
435 else:
436 line._kind |= LineKind.ProcessorError
438 line = yield line
440 if line.StartsWith(self._FINISH):
441 break
443 while True:
444 if line.StartsWith(subsection._FINISH):
445 line = yield parser.send(line)
446 line = yield parser.send(line)
448 subsection = None
449 parser = None
450 break
452 line = parser.send(line)
453 if isinstance(line, VivadoMessage): 453 ↛ 454line 453 didn't jump to line 454 because the condition on line 453 was never true
454 self._AddMessage(line)
456 line = yield line
458 nextLine = yield from self._SectionFinish(line, True)
459 return nextLine
462@export
463class RenamingGeneratedInstances(Section):
464 _START: ClassVar[str] = "Start Renaming Generated Instances"
465 _FINISH: ClassVar[str] = "Finished Renaming Generated Instances : "
468@export
469class RebuildingUserHierarchy(Section):
470 _START: ClassVar[str] = "Start Rebuilding User Hierarchy"
471 _FINISH: ClassVar[str] = "Finished Rebuilding User Hierarchy : "
474@export
475class RenamingGeneratedPorts(Section):
476 _START: ClassVar[str] = "Start Renaming Generated Ports"
477 _FINISH: ClassVar[str] = "Finished Renaming Generated Ports : "
480@export
481class RenamingGeneratedNets(Section):
482 _START: ClassVar[str] = "Start Renaming Generated Nets"
483 _FINISH: ClassVar[str] = "Finished Renaming Generated Nets : "
486@export
487class WritingSynthesisReport(Section):
488 _START: ClassVar[str] = "Start Writing Synthesis Report"
489 _FINISH: ClassVar[str] = "Finished Writing Synthesis Report : "
491 _blackboxes: Dict[str, int]
492 _cells: Dict[str, int]
494 def __init__(self, command: "Command") -> None:
495 super().__init__(command)
497 self._blackboxes = {}
498 self._cells = {}
500 @readonly
501 def Cells(self) -> Dict[str, int]:
502 return self._cells
504 @readonly
505 def Blackboxes(self) -> Dict[str, int]:
506 return self._blackboxes
508 def _BlackboxesGenerator(self, line: Line) -> Generator[Line, Line, Line]:
509 if line.StartsWith("+-"): 509 ↛ 512line 509 didn't jump to line 512 because the condition on line 509 was always true
510 line._kind = LineKind.TableFrame
511 else:
512 line._kind = LineKind.ProcessorError
514 line = yield line
515 if line.StartsWith("| "): 515 ↛ 518line 515 didn't jump to line 518 because the condition on line 515 was always true
516 line._kind = LineKind.TableHeader
517 else:
518 line._kind = LineKind.ProcessorError
520 line = yield line
521 if line.StartsWith("+-"): 521 ↛ 524line 521 didn't jump to line 524 because the condition on line 521 was always true
522 line._kind = LineKind.TableFrame
523 else:
524 line._kind = LineKind.ProcessorError
526 line = yield line
527 while True:
528 if line.StartsWith("|"):
529 line._kind = LineKind.TableRow
531 columns = line._message.strip("|").split("|")
532 self._blackboxes[columns[1].strip()] = int(columns[2].strip())
533 elif line.StartsWith("+-"): 533 ↛ 537line 533 didn't jump to line 537 because the condition on line 533 was always true
534 line._kind = LineKind.TableFrame
535 break
536 else:
537 line._kind = LineKind.ProcessorError
539 line = yield line
541 nextLine = yield line
542 return nextLine
544 def _CellGenerator(self, line: Line) -> Generator[Line, Line, Line]:
545 if line.StartsWith("+-"): 545 ↛ 548line 545 didn't jump to line 548 because the condition on line 545 was always true
546 line._kind = LineKind.TableFrame
547 else:
548 line._kind = LineKind.ProcessorError
550 line = yield line
551 if line.StartsWith("| "): 551 ↛ 554line 551 didn't jump to line 554 because the condition on line 551 was always true
552 line._kind = LineKind.TableHeader
553 else:
554 line._kind = LineKind.ProcessorError
556 line = yield line
557 if line.StartsWith("+-"): 557 ↛ 560line 557 didn't jump to line 560 because the condition on line 557 was always true
558 line._kind = LineKind.TableFrame
559 else:
560 line._kind = LineKind.ProcessorError
562 line = yield line
563 while True:
564 if line.StartsWith("|"):
565 line._kind = LineKind.TableRow
567 columns = line._message.strip("|").split("|")
568 self._cells[columns[1].strip()] = int(columns[2].strip())
569 elif line.StartsWith("+-"): 569 ↛ 573line 569 didn't jump to line 573 because the condition on line 569 was always true
570 line._kind = LineKind.TableFrame
571 break
572 else:
573 line._kind = LineKind.ProcessorError
575 line = yield line
577 nextLine = yield line
578 return nextLine
580 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
581 line = yield from self._SectionStart(line)
583 while True:
584 if line._kind is LineKind.Empty:
585 line = yield line
586 continue
587 elif line.StartsWith("Report BlackBoxes:"):
588 line._kind = LineKind.ParagraphHeadline
589 line = yield line
590 line = yield from self._BlackboxesGenerator(line)
591 elif line.StartsWith("Report Cell Usage:"):
592 line._kind = LineKind.ParagraphHeadline
593 line = yield line
594 line = yield from self._CellGenerator(line)
595 elif line.StartsWith("----"):
596 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
597 break
598 elif isinstance(line, VivadoMessage): 598 ↛ 599line 598 didn't jump to line 599 because the condition on line 598 was never true
599 self._AddMessage(line)
600 else:
601 line._kind = LineKind.Verbose
602 line = yield line
604 nextLine = yield from self._SectionFinish(line)
605 return nextLine