Coverage for pyEDAA/OutputFilter/Xilinx/SynthesizeDesign.py: 79%
385 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-16 06:19 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-16 06:19 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ___ _ _ _____ _ _ _ #
3# _ __ _ _| ____| _ \ / \ / \ / _ \ _ _| |_ _ __ _ _| |_| ___(_) | |_ ___ _ __ #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | | __| '_ \| | | | __| |_ | | | __/ _ \ '__| #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| | |_| | |_| |_) | |_| | |_| _| | | | || __/ | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/ \__,_|\__| .__/ \__,_|\__|_| |_|_|\__\___|_| #
7# |_| |___/ |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2025-2025 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, List, 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"):
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: 234 ↛ 248line 234 didn't jump to line 248 because the condition on line 234 was always true
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"):
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("----"): 301 ↛ 304line 301 didn't jump to line 304 because the condition on line 301 was always true
302 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
303 break
304 elif isinstance(line, VivadoMessage):
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 PartResourceSummary(Section):
348 _START: ClassVar[str] = "Start Part Resource Summary"
349 _FINISH: ClassVar[str] = "Finished Part Resource Summary"
352@export
353class CrossBoundaryAndAreaOptimization(Section):
354 _START: ClassVar[str] = "Start Cross Boundary and Area Optimization"
355 _FINISH: ClassVar[str] = "Finished Cross Boundary and Area Optimization : "
358@export
359class ROM_RAM_DSP_SR_Retiming(Section):
360 _START: ClassVar[str] = "Start ROM, RAM, DSP, Shift Register and Retiming Reporting"
361 _FINISH: ClassVar[str] = "Finished ROM, RAM, DSP, Shift Register and Retiming Reporting"
364@export
365class ApplyingXDCTimingConstraints(Section):
366 _START: ClassVar[str] = "Start Applying XDC Timing Constraints"
367 _FINISH: ClassVar[str] = "Finished Applying XDC Timing Constraints : "
370@export
371class TimingOptimization(Section):
372 _START: ClassVar[str] = "Start Timing Optimization"
373 _FINISH: ClassVar[str] = "Finished Timing Optimization : "
376@export
377class TechnologyMapping(Section):
378 _START: ClassVar[str] = "Start Technology Mapping"
379 _FINISH: ClassVar[str] = "Finished Technology Mapping : "
382@export
383class FlatteningBeforeIOInsertion(SubSection):
384 _START: ClassVar[str] = "Start Flattening Before IO Insertion"
385 _FINISH: ClassVar[str] = "Finished Flattening Before IO Insertion"
388@export
389class FinalNetlistCleanup(SubSection):
390 _START: ClassVar[str] = "Start Final Netlist Cleanup"
391 _FINISH: ClassVar[str] = "Finished Final Netlist Cleanup"
394@export
395class IOInsertion(Section):
396 _START: ClassVar[str] = "Start IO Insertion"
397 _FINISH: ClassVar[str] = "Finished IO Insertion : "
399 _subsections: Dict[Type[SubSection], SubSection]
401 def __init__(self, command: "Command"):
402 super().__init__(command)
404 self._subsections = {}
406 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
407 line = yield from self._SectionStart(line)
409 while True:
410 while True:
411 if line._kind is LineKind.Empty: 411 ↛ 412line 411 didn't jump to line 412 because the condition on line 411 was never true
412 line = yield line
413 continue
414 elif line.StartsWith("----"):
415 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter
416 elif line.StartsWith("Start "):
417 if line == FlatteningBeforeIOInsertion._START:
418 self._subsections[FlatteningBeforeIOInsertion] = (subsection := FlatteningBeforeIOInsertion(self))
419 line = yield next(parser := subsection.Generator(line))
420 break
421 elif line == FinalNetlistCleanup._START: 421 ↛ 432line 421 didn't jump to line 432 because the condition on line 421 was always true
422 self._subsections[FinalNetlistCleanup] = (subsection := FinalNetlistCleanup(self))
423 line = yield next(parser := subsection.Generator(line))
424 break
425 elif isinstance(line, VivadoMessage): 425 ↛ 426line 425 didn't jump to line 426 because the condition on line 425 was never true
426 self._AddMessage(line)
427 elif line.StartsWith("Finished "): 427 ↛ 430line 427 didn't jump to line 430 because the condition on line 427 was always true
428 break
429 else:
430 line._kind |= LineKind.ProcessorError
432 line = yield line
434 if line.StartsWith(self._FINISH):
435 break
437 while True:
438 if line.StartsWith(subsection._FINISH):
439 line = yield parser.send(line)
440 line = yield parser.send(line)
442 subsection = None
443 parser = None
444 break
446 line = parser.send(line)
447 if isinstance(line, VivadoMessage): 447 ↛ 448line 447 didn't jump to line 448 because the condition on line 447 was never true
448 self._AddMessage(line)
450 line = yield line
452 nextLine = yield from self._SectionFinish(line, True)
453 return nextLine
456@export
457class RenamingGeneratedInstances(Section):
458 _START: ClassVar[str] = "Start Renaming Generated Instances"
459 _FINISH: ClassVar[str] = "Finished Renaming Generated Instances : "
462@export
463class RebuildingUserHierarchy(Section):
464 _START: ClassVar[str] = "Start Rebuilding User Hierarchy"
465 _FINISH: ClassVar[str] = "Finished Rebuilding User Hierarchy : "
468@export
469class RenamingGeneratedPorts(Section):
470 _START: ClassVar[str] = "Start Renaming Generated Ports"
471 _FINISH: ClassVar[str] = "Finished Renaming Generated Ports : "
474@export
475class RenamingGeneratedNets(Section):
476 _START: ClassVar[str] = "Start Renaming Generated Nets"
477 _FINISH: ClassVar[str] = "Finished Renaming Generated Nets : "
480@export
481class WritingSynthesisReport(Section):
482 _START: ClassVar[str] = "Start Writing Synthesis Report"
483 _FINISH: ClassVar[str] = "Finished Writing Synthesis Report : "
485 _blackboxes: Dict[str, int]
486 _cells: Dict[str, int]
488 def __init__(self, command: "Command"):
489 super().__init__(command)
491 self._blackboxes = {}
492 self._cells = {}
494 @readonly
495 def Cells(self) -> Dict[str, int]:
496 return self._cells
498 @readonly
499 def Blackboxes(self) -> Dict[str, int]:
500 return self._blackboxes
502 def _BlackboxesGenerator(self, line: Line) -> Generator[Line, Line, Line]:
503 if line.StartsWith("+-"): 503 ↛ 506line 503 didn't jump to line 506 because the condition on line 503 was always true
504 line._kind = LineKind.TableFrame
505 else:
506 line._kind = LineKind.ProcessorError
508 line = yield 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.TableHeader
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.TableFrame
517 else:
518 line._kind = LineKind.ProcessorError
520 line = yield line
521 while True:
522 if line.StartsWith("|"): 522 ↛ 523line 522 didn't jump to line 523 because the condition on line 522 was never true
523 line._kind = LineKind.TableRow
525 columns = line._message.strip("|").split("|")
526 self._blackboxes[columns[1].strip()] = int(columns[2].strip())
527 elif line.StartsWith("+-"): 527 ↛ 531line 527 didn't jump to line 531 because the condition on line 527 was always true
528 line._kind = LineKind.TableFrame
529 break
530 else:
531 line._kind = LineKind.ProcessorError
533 line = yield line
535 nextLine = yield line
536 return nextLine
538 def _CellGenerator(self, line: Line) -> Generator[Line, Line, Line]:
539 if line.StartsWith("+-"): 539 ↛ 542line 539 didn't jump to line 542 because the condition on line 539 was always true
540 line._kind = LineKind.TableFrame
541 else:
542 line._kind = LineKind.ProcessorError
544 line = yield 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.TableHeader
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.TableFrame
553 else:
554 line._kind = LineKind.ProcessorError
556 line = yield line
557 while True:
558 if line.StartsWith("|"):
559 line._kind = LineKind.TableRow
561 columns = line._message.strip("|").split("|")
562 self._cells[columns[1].strip()] = int(columns[2].strip())
563 elif line.StartsWith("+-"): 563 ↛ 567line 563 didn't jump to line 567 because the condition on line 563 was always true
564 line._kind = LineKind.TableFrame
565 break
566 else:
567 line._kind = LineKind.ProcessorError
569 line = yield line
571 nextLine = yield line
572 return nextLine
574 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
575 line = yield from self._SectionStart(line)
577 while True:
578 if line._kind is LineKind.Empty:
579 line = yield line
580 continue
581 elif line.StartsWith("Report BlackBoxes:"):
582 line._kind = LineKind.ParagraphHeadline
583 line = yield line
584 line = yield from self._BlackboxesGenerator(line)
585 elif line.StartsWith("Report Cell Usage:"):
586 line._kind = LineKind.ParagraphHeadline
587 line = yield line
588 line = yield from self._CellGenerator(line)
589 elif line.StartsWith("----"): 589 ↛ 592line 589 didn't jump to line 592 because the condition on line 589 was always true
590 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter
591 break
592 elif isinstance(line, VivadoMessage):
593 self._AddMessage(line)
594 else:
595 line._kind = LineKind.Verbose
596 line = yield line
598 nextLine = yield from self._SectionFinish(line)
599 return nextLine