Coverage for pyEDAA/OutputFilter/Xilinx/OptimizeDesign.py: 88%
386 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 typing import Generator, ClassVar, List, Type, Dict, Tuple
34from pyTooling.Decorators import export
35from pyTooling.MetaClasses import ExtendedType
37from pyEDAA.OutputFilter.Xilinx import Line, VivadoMessage, LineKind
38from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException
39from pyEDAA.OutputFilter.Xilinx.Common2 import BaseParser, VivadoMessagesMixin
42@export
43class Task(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True):
44 # _START: ClassVar[str]
45 # _FINISH: ClassVar[str]
46 _TIME: ClassVar[str] = "Time (s):"
48 _command: "Command"
49 _duration: float
51 def __init__(self, command: "Command"):
52 super().__init__()
53 VivadoMessagesMixin.__init__(self)
55 self._command = command
57 def _TaskStart(self, line: Line) -> Generator[Line, Line, Line]:
58 if not line.StartsWith(self._START): 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true
59 raise ProcessorException()
61 line._kind = LineKind.TaskStart
62 nextLine = yield line
63 return nextLine
65 def _TaskFinish(self, line: Line) -> Generator[Line, Line, Line]:
66 if not line.StartsWith(self._FINISH): 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true
67 raise ProcessorException()
69 line._kind = LineKind.TaskEnd
70 line = yield line
71 while self._TIME is not None: 71 ↛ 78line 71 didn't jump to line 78 because the condition on line 71 was always true
72 if line.StartsWith(self._TIME):
73 line._kind = LineKind.TaskTime
74 break
76 line = yield line
78 line = yield line
79 return line
81 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
82 line = yield from self._TaskStart(line)
84 while True:
85 if line._kind is LineKind.Empty:
86 line = yield line
87 continue
88 elif line.StartsWith("Ending"):
89 break
90 elif isinstance(line, VivadoMessage):
91 self._AddMessage(line)
92 elif line.StartsWith(self._TIME):
93 line._kind = LineKind.TaskTime
94 nextLine = yield line
95 return nextLine
97 line = yield line
99 nextLine = yield from self._TaskFinish(line)
100 return nextLine
103@export
104class Phase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True):
105 # _START: ClassVar[str]
106 # _FINISH: ClassVar[str]
108 _task: Task
109 _duration: float
111 def __init__(self, task: Task):
112 super().__init__()
113 VivadoMessagesMixin.__init__(self)
115 self._task = task
117 def _PhaseStart(self, line: Line) -> Generator[Line, Line, Line]:
118 if not line.StartsWith(self._START): 118 ↛ 119line 118 didn't jump to line 119 because the condition on line 118 was never true
119 raise ProcessorException()
121 line._kind = LineKind.PhaseStart
122 nextLine = yield line
123 return nextLine
125 def _PhaseFinish(self, line: Line) -> Generator[Line, Line, None]:
126 if not line.StartsWith(self._FINISH): 126 ↛ 127line 126 didn't jump to line 127 because the condition on line 126 was never true
127 raise ProcessorException()
129 line._kind = LineKind.PhaseEnd
130 line = yield line
132 while self._TIME is not None: 132 ↛ 139line 132 didn't jump to line 139 because the condition on line 132 was always true
133 if line.StartsWith(self._TIME):
134 line._kind = LineKind.PhaseTime
135 break
137 line = yield line
139 line = yield line
140 while self._FINAL is not None:
141 if line.StartsWith(self._FINAL): 141 ↛ 145line 141 didn't jump to line 145 because the condition on line 141 was always true
142 line._kind = LineKind.PhaseFinal
143 break
145 line = yield line
147 nextLine = yield line
148 return nextLine
150 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
151 line = yield from self._PhaseStart(line)
153 while True:
154 if line._kind is LineKind.Empty: 154 ↛ 155line 154 didn't jump to line 155 because the condition on line 154 was never true
155 line = yield line
156 continue
157 elif line.StartsWith(self._FINISH):
158 break
159 elif isinstance(line, VivadoMessage):
160 self._AddMessage(line)
162 line = yield line
164 nextLine = yield from self._PhaseFinish(line)
165 return nextLine
168@export
169class SubPhase(BaseParser, VivadoMessagesMixin, metaclass=ExtendedType, slots=True):
170 # _START: ClassVar[str]
171 # _FINISH: ClassVar[str]
173 _phase: Phase
174 _duration: float
176 def __init__(self, phase: Phase):
177 super().__init__()
178 VivadoMessagesMixin.__init__(self)
180 self._phase = phase
182 def _SubPhaseStart(self, line: Line) -> Generator[Line, Line, Line]:
183 if not line.StartsWith(self._START): 183 ↛ 184line 183 didn't jump to line 184 because the condition on line 183 was never true
184 raise ProcessorException()
186 line._kind = LineKind.SubPhaseStart
187 nextLine = yield line
188 return nextLine
190 def _SubPhaseFinish(self, line: Line) -> Generator[Line, Line, None]:
191 if not line.StartsWith(self._FINISH): 191 ↛ 192line 191 didn't jump to line 192 because the condition on line 191 was never true
192 raise ProcessorException()
194 line._kind = LineKind.SubPhaseEnd
195 line = yield line
197 while self._TIME is not None: 197 ↛ 204line 197 didn't jump to line 204 because the condition on line 197 was always true
198 if line.StartsWith(self._TIME):
199 line._kind = LineKind.SubPhaseTime
200 break
202 line = yield line
204 nextLine = yield line
205 return nextLine
207 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
208 line = yield from self._SubPhaseStart(line)
210 while True:
211 if line._kind is LineKind.Empty:
212 line = yield line
213 continue
214 elif line.StartsWith(self._FINISH):
215 break
216 elif isinstance(line, VivadoMessage): 216 ↛ 217line 216 didn't jump to line 217 because the condition on line 216 was never true
217 self._AddMessage(line)
219 line = yield line
221 nextLine = yield from self._SubPhaseFinish(line)
222 return nextLine
225@export
226class Phase11_CoreGenerationAndDesignSetup(SubPhase):
227 _START: ClassVar[str] = "Phase 1.1 Core Generation And Design Setup"
228 _FINISH: ClassVar[str] = "Phase 1.1 Core Generation And Design Setup | Checksum:"
229 _TIME: ClassVar[str] = "Time (s):"
232@export
233class Phase12_SetupConstraintsAndSortNetlist(SubPhase):
234 _START: ClassVar[str] = "Phase 1.2 Setup Constraints And Sort Netlist"
235 _FINISH: ClassVar[str] = "Phase 1.2 Setup Constraints And Sort Netlist | Checksum:"
236 _TIME: ClassVar[str] = "Time (s):"
239@export
240class Phase1_Initialization(Phase):
241 _START: ClassVar[str] = "Phase 1 Initialization"
242 _FINISH: ClassVar[str] = "Phase 1 Initialization | Checksum:"
243 _TIME: ClassVar[str] = "Time (s):"
244 _FINAL: ClassVar[str] = None
246 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
247 Phase11_CoreGenerationAndDesignSetup,
248 Phase12_SetupConstraintsAndSortNetlist
249 )
251 _subphases: Dict[Type[SubPhase], SubPhase]
253 def __init__(self, phase: Phase):
254 super().__init__(phase)
256 self._subphases = {p: p(self) for p in self._PARSERS}
258 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
259 line = yield from self._PhaseStart(line)
261 activeParsers: List[Phase] = list(self._subphases.values())
263 while True:
264 while True:
265 if line._kind is LineKind.Empty:
266 line = yield line
267 continue
268 elif isinstance(line, VivadoMessage): 268 ↛ 269line 268 didn't jump to line 269 because the condition on line 268 was never true
269 self._AddMessage(line)
270 elif line.StartsWith("Phase 1."):
271 for parser in activeParsers: # type: Section 271 ↛ 276line 271 didn't jump to line 276 because the loop on line 271 didn't complete
272 if line.StartsWith(parser._START): 272 ↛ 271line 272 didn't jump to line 271 because the condition on line 272 was always true
273 line = yield next(phase := parser.Generator(line))
274 break
275 else:
276 raise Exception(f"Unknown subphase: {line!r}")
277 break
278 elif line.StartsWith(self._FINISH): 278 ↛ 282line 278 didn't jump to line 282 because the condition on line 278 was always true
279 nextLine = yield from self._PhaseFinish(line)
280 return nextLine
282 line = yield line
284 while phase is not None: 284 ↛ 263line 284 didn't jump to line 263 because the condition on line 284 was always true
285 # if line.StartsWith("Ending"):
286 # line = yield task.send(line)
287 # break
289 if isinstance(line, VivadoMessage): 289 ↛ 290line 289 didn't jump to line 290 because the condition on line 289 was never true
290 self._AddMessage(line)
292 try:
293 line = yield phase.send(line)
294 except StopIteration as ex:
295 activeParsers.remove(parser)
296 line = ex.value
297 break
300@export
301class Phase21_TimerUpdate(SubPhase):
302 _START: ClassVar[str] = "Phase 2.1 Timer Update"
303 _FINISH: ClassVar[str] = "Phase 2.1 Timer Update | Checksum:"
304 _TIME: ClassVar[str] = "Time (s):"
307@export
308class Phase22_TimingDataCollection(SubPhase):
309 _START: ClassVar[str] = "Phase 2.2 Timing Data Collection"
310 _FINISH: ClassVar[str] = "Phase 2.2 Timing Data Collection | Checksum:"
311 _TIME: ClassVar[str] = "Time (s):"
314@export
315class Phase2_TimerUpdateAndTimingDataCollection(Phase):
316 _START: ClassVar[str] = "Phase 2 Timer Update And Timing Data Collection"
317 _FINISH: ClassVar[str] = "Phase 2 Timer Update And Timing Data Collection | Checksum:"
318 _TIME: ClassVar[str] = "Time (s):"
319 _FINAL: ClassVar[str] = None
321 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
322 Phase21_TimerUpdate,
323 Phase22_TimingDataCollection
324 )
326 _subphases: Dict[Type[SubPhase], SubPhase]
328 def __init__(self, phase: Phase):
329 super().__init__(phase)
331 self._subphases = {p: p(self) for p in self._PARSERS}
333 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
334 line = yield from self._PhaseStart(line)
336 activeParsers: List[Phase] = list(self._subphases.values())
338 while True:
339 while True:
340 if line._kind is LineKind.Empty:
341 line = yield line
342 continue
343 elif isinstance(line, VivadoMessage): 343 ↛ 344line 343 didn't jump to line 344 because the condition on line 343 was never true
344 self._AddMessage(line)
345 elif line.StartsWith("Phase 2."):
346 for parser in activeParsers: # type: Section 346 ↛ 351line 346 didn't jump to line 351 because the loop on line 346 didn't complete
347 if line.StartsWith(parser._START): 347 ↛ 346line 347 didn't jump to line 346 because the condition on line 347 was always true
348 line = yield next(phase := parser.Generator(line))
349 break
350 else:
351 raise Exception(f"Unknown subphase: {line!r}")
352 break
353 elif line.StartsWith(self._FINISH): 353 ↛ 357line 353 didn't jump to line 357 because the condition on line 353 was always true
354 nextLine = yield from self._PhaseFinish(line)
355 return nextLine
357 line = yield line
359 while phase is not None: 359 ↛ 338line 359 didn't jump to line 338 because the condition on line 359 was always true
360 # if line.StartsWith("Ending"):
361 # line = yield task.send(line)
362 # break
364 if isinstance(line, VivadoMessage): 364 ↛ 365line 364 didn't jump to line 365 because the condition on line 364 was never true
365 self._AddMessage(line)
367 try:
368 line = yield phase.send(line)
369 except StopIteration as ex:
370 activeParsers.remove(parser)
371 line = ex.value
372 break
375@export
376class Phase3_Retarget(Phase):
377 _START: ClassVar[str] = "Phase 3 Retarget"
378 _FINISH: ClassVar[str] = "Phase 3 Retarget | Checksum:"
379 _TIME: ClassVar[str] = "Time (s):"
380 _FINAL: ClassVar[str] = "Retarget | Checksum:"
383@export
384class Phase4_ConstantPropagation(Phase):
385 _START: ClassVar[str] = "Phase 4 Constant propagation"
386 _FINISH: ClassVar[str] = "Phase 4 Constant propagation | Checksum:"
387 _TIME: ClassVar[str] = "Time (s):"
388 _FINAL: ClassVar[str] = "Constant propagation | Checksum:"
391@export
392class Phase5_Sweep(Phase):
393 _START: ClassVar[str] = "Phase 5 Sweep"
394 _FINISH: ClassVar[str] = "Phase 5 Sweep | Checksum:"
395 _TIME: ClassVar[str] = "Time (s):"
396 _FINAL: ClassVar[str] = "Sweep | Checksum:"
399@export
400class Phase6_BUFGOptimization(Phase):
401 _START: ClassVar[str] = "Phase 6 BUFG optimization"
402 _FINISH: ClassVar[str] = "Phase 6 BUFG optimization | Checksum:"
403 _TIME: ClassVar[str] = "Time (s):"
404 _FINAL: ClassVar[str] = "BUFG optimization | Checksum:"
407@export
408class Phase7_ShiftRegisterOptimization(Phase):
409 _START: ClassVar[str] = "Phase 7 Shift Register Optimization"
410 _FINISH: ClassVar[str] = "Phase 7 Shift Register Optimization | Checksum:"
411 _TIME: ClassVar[str] = "Time (s):"
412 _FINAL: ClassVar[str] = "Shift Register Optimization | Checksum:"
415@export
416class Phase8_PostProcessingNetlist(Phase):
417 _START: ClassVar[str] = "Phase 8 Post Processing Netlist"
418 _FINISH: ClassVar[str] = "Phase 8 Post Processing Netlist | Checksum:"
419 _TIME: ClassVar[str] = "Time (s):"
420 _FINAL: ClassVar[str] = "Post Processing Netlist | Checksum:"
423@export
424class Phase91_FinalizingDesignCoresAndUpdatingShapes(SubPhase):
425 _START: ClassVar[str] = "Phase 9.1 Finalizing Design Cores and Updating Shapes"
426 _FINISH: ClassVar[str] = "Phase 9.1 Finalizing Design Cores and Updating Shapes | Checksum:"
427 _TIME: ClassVar[str] = "Time (s):"
430@export
431class Phase92_VerifyingNetlistConnectivity(SubPhase):
432 _START: ClassVar[str] = "Phase 9.2 Verifying Netlist Connectivity"
433 _FINISH: ClassVar[str] = "Phase 9.2 Verifying Netlist Connectivity | Checksum:"
434 _TIME: ClassVar[str] = "Time (s):"
437@export
438class Phase9_Finalization(Phase):
439 _START: ClassVar[str] = "Phase 9 Finalization"
440 _FINISH: ClassVar[str] = "Phase 9 Finalization | Checksum:"
441 _TIME: ClassVar[str] = "Time (s):"
442 _FINAL: ClassVar[str] = None
444 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
445 Phase91_FinalizingDesignCoresAndUpdatingShapes,
446 Phase92_VerifyingNetlistConnectivity
447 )
449 _subphases: Dict[Type[SubPhase], SubPhase]
451 def __init__(self, phase: Phase):
452 super().__init__(phase)
454 self._subphases = {p: p(self) for p in self._PARSERS}
456 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
457 line = yield from self._PhaseStart(line)
459 activeParsers: List[Phase] = list(self._subphases.values())
461 while True:
462 while True:
463 if line._kind is LineKind.Empty:
464 line = yield line
465 continue
466 elif isinstance(line, VivadoMessage): 466 ↛ 467line 466 didn't jump to line 467 because the condition on line 466 was never true
467 self._AddMessage(line)
468 elif line.StartsWith("Phase 9."):
469 for parser in activeParsers: # type: Section 469 ↛ 474line 469 didn't jump to line 474 because the loop on line 469 didn't complete
470 if line.StartsWith(parser._START): 470 ↛ 469line 470 didn't jump to line 469 because the condition on line 470 was always true
471 line = yield next(phase := parser.Generator(line))
472 break
473 else:
474 raise Exception(f"Unknown subphase: {line!r}")
475 break
476 elif line.StartsWith(self._FINISH): 476 ↛ 480line 476 didn't jump to line 480 because the condition on line 476 was always true
477 nextLine = yield from self._PhaseFinish(line)
478 return nextLine
480 line = yield line
482 while phase is not None: 482 ↛ 461line 482 didn't jump to line 461 because the condition on line 482 was always true
483 # if line.StartsWith("Ending"):
484 # line = yield task.send(line)
485 # break
487 if isinstance(line, VivadoMessage): 487 ↛ 488line 487 didn't jump to line 488 because the condition on line 487 was never true
488 self._AddMessage(line)
490 try:
491 line = yield phase.send(line)
492 except StopIteration as ex:
493 activeParsers.remove(parser)
494 line = ex.value
495 break
498@export
499class DRCTask(Task):
500 _START: ClassVar[str] = "Starting DRC Task"
501 _FINISH: ClassVar[str] = "Time (s):"
504@export
505class CacheTimingInformationTask(Task):
506 _START: ClassVar[str] = "Starting Cache Timing Information Task"
507 _FINISH: ClassVar[str] = "Ending Cache Timing Information Task"
510@export
511class LogicOptimizationTask(Task):
512 _START: ClassVar[str] = "Starting Logic Optimization Task"
513 _FINISH: ClassVar[str] = "Ending Logic Optimization Task"
515 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
516 Phase1_Initialization,
517 Phase2_TimerUpdateAndTimingDataCollection,
518 Phase3_Retarget,
519 Phase4_ConstantPropagation,
520 Phase5_Sweep,
521 Phase6_BUFGOptimization,
522 Phase7_ShiftRegisterOptimization,
523 Phase8_PostProcessingNetlist,
524 Phase9_Finalization
525 )
527 _phases: Dict[Type[Phase], Phase]
529 def __init__(self, command: "Command"):
530 super().__init__(command)
532 self._phases = {p: p(self) for p in self._PARSERS}
534 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
535 line = yield from self._TaskStart(line)
537 activeParsers: List[Phase] = list(self._phases.values())
539 while True:
540 while True:
541 if line._kind is LineKind.Empty:
542 line = yield line
543 continue
544 elif isinstance(line, VivadoMessage):
545 self._AddMessage(line)
546 elif line.StartsWith("Phase "):
547 for parser in activeParsers: # type: Section 547 ↛ 552line 547 didn't jump to line 552 because the loop on line 547 didn't complete
548 if line.StartsWith(parser._START): 548 ↛ 547line 548 didn't jump to line 547 because the condition on line 548 was always true
549 line = yield next(phase := parser.Generator(line))
550 break
551 else:
552 raise Exception(f"Unknown phase: {line!r}")
553 break
554 elif line.StartsWith("Ending"):
555 nextLine = yield from self._TaskFinish(line)
556 return nextLine
557 elif line.StartsWith(self._TIME): 557 ↛ 558line 557 didn't jump to line 558 because the condition on line 557 was never true
558 line._kind = LineKind.TaskTime
559 nextLine = yield line
560 return nextLine
562 line = yield line
564 while phase is not None: 564 ↛ 539line 564 didn't jump to line 539 because the condition on line 564 was always true
565 # if line.StartsWith("Ending"):
566 # line = yield task.send(line)
567 # break
569 if isinstance(line, VivadoMessage):
570 self._AddMessage(line)
572 try:
573 line = yield phase.send(line)
574 except StopIteration as ex:
575 activeParsers.remove(parser)
576 line = ex.value
577 break
579# @export
580# class ConnectivityCheckTask(Task):
581# pass
584@export
585class PowerOptimizationTask(Task):
586 _START: ClassVar[str] = "Starting Power Optimization Task"
587 _FINISH: ClassVar[str] = "Ending Power Optimization Task"
590@export
591class FinalCleanupTask(Task):
592 _START: ClassVar[str] = "Starting Final Cleanup Task"
593 _FINISH: ClassVar[str] = "Ending Final Cleanup Task"
596@export
597class NetlistObfuscationTask(Task):
598 _START: ClassVar[str] = "Starting Netlist Obfuscation Task"
599 _FINISH: ClassVar[str] = "Ending Netlist Obfuscation Task"