Coverage for pyEDAA/OutputFilter/Xilinx/RouteDesign.py: 89%
330 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
36from pyEDAA.OutputFilter.Xilinx import Line, VivadoMessage, LineKind
37from pyEDAA.OutputFilter.Xilinx.Common2 import Task, Phase, SubPhase
40@export
41class Phase1_BuildRTDesign(Phase):
42 _START: ClassVar[str] = "Phase 1 Build RT Design"
43 _FINISH: ClassVar[str] = "Phase 1 Build RT Design | Checksum:"
44 _TIME: ClassVar[str] = "Time (s):"
47@export
48class Phase21_FixTopologyConstraints(SubPhase):
49 _START: ClassVar[str] = "Phase 2.1 Fix Topology Constraints"
50 _FINISH: ClassVar[str] = "Phase 2.1 Fix Topology Constraints | Checksum:"
51 _TIME: ClassVar[str] = "Time (s):"
54@export
55class Phase22_PreRouteCleanup(SubPhase):
56 _START: ClassVar[str] = "Phase 2.2 Pre Route Cleanup"
57 _FINISH: ClassVar[str] = "Phase 2.2 Pre Route Cleanup | Checksum:"
58 _TIME: ClassVar[str] = "Time (s):"
61@export
62class Phase23_UpdateTiming(SubPhase):
63 _START: ClassVar[str] = "Phase 2.3 Update Timing"
64 _FINISH: ClassVar[str] = "Phase 2.3 Update Timing | Checksum:"
65 _TIME: ClassVar[str] = "Time (s):"
68@export
69class Phase24_SoftConstraintPins_FastBudgeting(SubPhase):
70 _START: ClassVar[str] = "Phase 2.4 Soft Constraint Pins - Fast Budgeting"
71 _FINISH: ClassVar[str] = "Phase 2.4 Soft Constraint Pins - Fast Budgeting | Checksum:"
72 _TIME: ClassVar[str] = "Time (s):"
75@export
76class Phase2_RouterInitialization(Phase):
77 _START: ClassVar[str] = "Phase 2 Router Initialization"
78 _FINISH: ClassVar[str] = "Phase 2 Router Initialization | Checksum:"
79 _TIME: ClassVar[str] = "Time (s):"
81 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
82 Phase21_FixTopologyConstraints,
83 Phase22_PreRouteCleanup,
84 Phase23_UpdateTiming,
85 Phase24_SoftConstraintPins_FastBudgeting
86 )
88 _subphases: Dict[Type[SubPhase], SubPhase]
90 def __init__(self, phase: Phase):
91 super().__init__(phase)
93 self._subphases = {p: p(self) for p in self._PARSERS}
95 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
96 line = yield from self._PhaseStart(line)
98 activeParsers: List[Phase] = list(self._subphases.values())
100 while True:
101 while True:
102 if line._kind is LineKind.Empty:
103 line = yield line
104 continue
105 elif isinstance(line, VivadoMessage):
106 self._AddMessage(line)
107 elif line.StartsWith("Phase 2."):
108 for parser in activeParsers: # type: SubPhase 108 ↛ 113line 108 didn't jump to line 113 because the loop on line 108 didn't complete
109 if line.StartsWith(parser._START): 109 ↛ 108line 109 didn't jump to line 108 because the condition on line 109 was always true
110 line = yield next(phase := parser.Generator(line))
111 break
112 else:
113 raise Exception(f"Unknown subphase: {line!r}")
114 break
115 elif line.StartsWith(self._FINISH):
116 nextLine = yield from self._PhaseFinish(line)
117 return nextLine
119 line = yield line
121 while phase is not None: 121 ↛ 100line 121 didn't jump to line 100 because the condition on line 121 was always true
122 # if line.StartsWith("Ending"):
123 # line = yield task.send(line)
124 # break
126 if isinstance(line, VivadoMessage):
127 self._AddMessage(line)
129 try:
130 line = yield phase.send(line)
131 except StopIteration as ex:
132 activeParsers.remove(parser)
133 line = ex.value
134 break
137@export
138class Phase3_GlobalRouting(Phase):
139 _START: ClassVar[str] = "Phase 3 Global Routing"
140 _FINISH: ClassVar[str] = "Phase 3 Global Routing | Checksum:"
141 _TIME: ClassVar[str] = "Time (s):"
144@export
145class Phase41_InitialNetRoutingPass(SubPhase):
146 _START: ClassVar[str] = "Phase 4.1 Initial Net Routing Pass"
147 _FINISH: ClassVar[str] = "Phase 4.1 Initial Net Routing Pass | Checksum:"
148 _TIME: ClassVar[str] = "Time (s):"
151@export
152class Phase4_InitialRouting(Phase):
153 _START: ClassVar[str] = "Phase 4 Initial Routing"
154 _FINISH: ClassVar[str] = "Phase 4 Initial Routing | Checksum:"
155 _TIME: ClassVar[str] = "Time (s):"
157 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
158 Phase41_InitialNetRoutingPass,
159 )
161 _subphases: Dict[Type[SubPhase], SubPhase]
163 def __init__(self, phase: Phase):
164 super().__init__(phase)
166 self._subphases = {p: p(self) for p in self._PARSERS}
168 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
169 line = yield from self._PhaseStart(line)
171 activeParsers: List[Phase] = list(self._subphases.values())
173 while True:
174 while True:
175 if line._kind is LineKind.Empty:
176 line = yield line
177 continue
178 elif isinstance(line, VivadoMessage): 178 ↛ 179line 178 didn't jump to line 179 because the condition on line 178 was never true
179 self._AddMessage(line)
180 elif line.StartsWith("Phase 4."):
181 for parser in activeParsers: # type: SubPhase 181 ↛ 186line 181 didn't jump to line 186 because the loop on line 181 didn't complete
182 if line.StartsWith(parser._START): 182 ↛ 181line 182 didn't jump to line 181 because the condition on line 182 was always true
183 line = yield next(phase := parser.Generator(line))
184 break
185 else:
186 raise Exception(f"Unknown subphase: {line!r}")
187 break
188 elif line.StartsWith(self._FINISH): 188 ↛ 192line 188 didn't jump to line 192 because the condition on line 188 was always true
189 nextLine = yield from self._PhaseFinish(line)
190 return nextLine
192 line = yield line
194 while phase is not None: 194 ↛ 173line 194 didn't jump to line 173 because the condition on line 194 was always true
195 # if line.StartsWith("Ending"):
196 # line = yield task.send(line)
197 # break
199 if isinstance(line, VivadoMessage): 199 ↛ 200line 199 didn't jump to line 200 because the condition on line 199 was never true
200 self._AddMessage(line)
202 try:
203 line = yield phase.send(line)
204 except StopIteration as ex:
205 activeParsers.remove(parser)
206 line = ex.value
207 break
210@export
211class Phase51_GlobalIteration0(SubPhase):
212 _START: ClassVar[str] = "Phase 5.1 Global Iteration 0"
213 _FINISH: ClassVar[str] = "Phase 5.1 Global Iteration 0 | Checksum:"
214 _TIME: ClassVar[str] = "Time (s):"
217@export
218class Phase52_GlobalIteration1(SubPhase):
219 _START: ClassVar[str] = "Phase 5.2 Global Iteration 1"
220 _FINISH: ClassVar[str] = "Phase 5.2 Global Iteration 1 | Checksum:"
221 _TIME: ClassVar[str] = "Time (s):"
224@export
225class Phase5_RipUpAndReroute(Phase):
226 _START: ClassVar[str] = "Phase 5 Rip-up And Reroute"
227 _FINISH: ClassVar[str] = "Phase 5 Rip-up And Reroute | Checksum:"
228 _TIME: ClassVar[str] = "Time (s):"
230 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
231 Phase51_GlobalIteration0,
232 Phase52_GlobalIteration1
233 )
235 _subphases: Dict[Type[SubPhase], SubPhase]
237 def __init__(self, phase: Phase):
238 super().__init__(phase)
240 self._subphases = {p: p(self) for p in self._PARSERS}
242 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
243 line = yield from self._PhaseStart(line)
245 activeParsers: List[Phase] = list(self._subphases.values())
247 while True:
248 while True:
249 if line._kind is LineKind.Empty:
250 line = yield line
251 continue
252 elif isinstance(line, VivadoMessage): 252 ↛ 253line 252 didn't jump to line 253 because the condition on line 252 was never true
253 self._AddMessage(line)
254 elif line.StartsWith("Phase 5."):
255 for parser in activeParsers: # type: SubPhase 255 ↛ 260line 255 didn't jump to line 260 because the loop on line 255 didn't complete
256 if line.StartsWith(parser._START): 256 ↛ 255line 256 didn't jump to line 255 because the condition on line 256 was always true
257 line = yield next(phase := parser.Generator(line))
258 break
259 else:
260 raise Exception(f"Unknown subphase: {line!r}")
261 break
262 elif line.StartsWith(self._FINISH): 262 ↛ 266line 262 didn't jump to line 266 because the condition on line 262 was always true
263 nextLine = yield from self._PhaseFinish(line)
264 return nextLine
266 line = yield line
268 while phase is not None: 268 ↛ 247line 268 didn't jump to line 247 because the condition on line 268 was always true
269 # if line.StartsWith("Ending"):
270 # line = yield task.send(line)
271 # break
273 if isinstance(line, VivadoMessage):
274 self._AddMessage(line)
276 try:
277 line = yield phase.send(line)
278 except StopIteration as ex:
279 activeParsers.remove(parser)
280 line = ex.value
281 break
284@export
285class Phase61_DelayCleanUp(SubPhase):
286 _START: ClassVar[str] = "Phase 6.1 Delay CleanUp"
287 _FINISH: ClassVar[str] = "Phase 6.1 Delay CleanUp | Checksum:"
288 _TIME: ClassVar[str] = "Time (s):"
291@export
292class Phase62_ClockSkewOptimization(SubPhase):
293 _START: ClassVar[str] = "Phase 6.2 Clock Skew Optimization"
294 _FINISH: ClassVar[str] = "Phase 6.2 Clock Skew Optimization | Checksum:"
295 _TIME: ClassVar[str] = "Time (s):"
298@export
299class Phase6_DelayAndSkewOptimization(Phase):
300 _START: ClassVar[str] = "Phase 6 Delay and Skew Optimization"
301 _FINISH: ClassVar[str] = "Phase 6 Delay and Skew Optimization | Checksum:"
302 _TIME: ClassVar[str] = "Time (s):"
304 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
305 Phase61_DelayCleanUp,
306 Phase62_ClockSkewOptimization
307 )
309 _subphases: Dict[Type[SubPhase], SubPhase]
311 def __init__(self, phase: Phase):
312 super().__init__(phase)
314 self._subphases = {p: p(self) for p in self._PARSERS}
316 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
317 line = yield from self._PhaseStart(line)
319 activeParsers: List[Phase] = list(self._subphases.values())
321 while True:
322 while True:
323 if line._kind is LineKind.Empty:
324 line = yield line
325 continue
326 elif isinstance(line, VivadoMessage): 326 ↛ 327line 326 didn't jump to line 327 because the condition on line 326 was never true
327 self._AddMessage(line)
328 elif line.StartsWith("Phase 6."):
329 for parser in activeParsers: # type: SubPhase 329 ↛ 334line 329 didn't jump to line 334 because the loop on line 329 didn't complete
330 if line.StartsWith(parser._START): 330 ↛ 329line 330 didn't jump to line 329 because the condition on line 330 was always true
331 line = yield next(phase := parser.Generator(line))
332 break
333 else:
334 raise Exception(f"Unknown subphase: {line!r}")
335 break
336 elif line.StartsWith(self._FINISH): 336 ↛ 340line 336 didn't jump to line 340 because the condition on line 336 was always true
337 nextLine = yield from self._PhaseFinish(line)
338 return nextLine
340 line = yield line
342 while phase is not None: 342 ↛ 321line 342 didn't jump to line 321 because the condition on line 342 was always true
343 # if line.StartsWith("Ending"):
344 # line = yield task.send(line)
345 # break
347 if isinstance(line, VivadoMessage): 347 ↛ 348line 347 didn't jump to line 348 because the condition on line 347 was never true
348 self._AddMessage(line)
350 try:
351 line = yield phase.send(line)
352 except StopIteration as ex:
353 activeParsers.remove(parser)
354 line = ex.value
355 break
358@export
359class Phase71_HoldFixIter(SubPhase):
360 _START: ClassVar[str] = "Phase 7.1 Hold Fix Iter"
361 _FINISH: ClassVar[str] = "Phase 7.1 Hold Fix Iter | Checksum:"
362 _TIME: ClassVar[str] = "Time (s):"
365@export
366class Phase7_PostHoldFix(Phase):
367 _START: ClassVar[str] = "Phase 7 Post Hold Fix"
368 _FINISH: ClassVar[str] = "Phase 7 Post Hold Fix | Checksum:"
369 _TIME: ClassVar[str] = "Time (s):"
371 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
372 Phase71_HoldFixIter,
373 )
375 _subphases: Dict[Type[SubPhase], SubPhase]
377 def __init__(self, phase: Phase):
378 super().__init__(phase)
380 self._subphases = {p: p(self) for p in self._PARSERS}
382 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
383 line = yield from self._PhaseStart(line)
385 activeParsers: List[Phase] = list(self._subphases.values())
387 while True:
388 while True:
389 if line._kind is LineKind.Empty:
390 line = yield line
391 continue
392 elif isinstance(line, VivadoMessage): 392 ↛ 393line 392 didn't jump to line 393 because the condition on line 392 was never true
393 self._AddMessage(line)
394 elif line.StartsWith("Phase 7."):
395 for parser in activeParsers: # type: SubPhase 395 ↛ 400line 395 didn't jump to line 400 because the loop on line 395 didn't complete
396 if line.StartsWith(parser._START): 396 ↛ 395line 396 didn't jump to line 395 because the condition on line 396 was always true
397 line = yield next(phase := parser.Generator(line))
398 break
399 else:
400 raise Exception(f"Unknown subphase: {line!r}")
401 break
402 elif line.StartsWith(self._FINISH): 402 ↛ 406line 402 didn't jump to line 406 because the condition on line 402 was always true
403 nextLine = yield from self._PhaseFinish(line)
404 return nextLine
406 line = yield line
408 while phase is not None: 408 ↛ 387line 408 didn't jump to line 387 because the condition on line 408 was always true
409 # if line.StartsWith("Ending"):
410 # line = yield task.send(line)
411 # break
413 if isinstance(line, VivadoMessage):
414 self._AddMessage(line)
416 try:
417 line = yield phase.send(line)
418 except StopIteration as ex:
419 activeParsers.remove(parser)
420 line = ex.value
421 break
424@export
425class Phase8_RouteFinalize(Phase):
426 _START: ClassVar[str] = "Phase 8 Route finalize"
427 _FINISH: ClassVar[str] = "Phase 8 Route finalize | Checksum:"
428 _TIME: ClassVar[str] = "Time (s):"
431@export
432class Phase9_VerifyingRoutedNets(Phase):
433 _START: ClassVar[str] = "Phase 9 Verifying routed nets"
434 _FINISH: ClassVar[str] = "Phase 9 Verifying routed nets | Checksum:"
435 _TIME: ClassVar[str] = "Time (s):"
438@export
439class Phase10_DepositingRoutes(Phase):
440 _START: ClassVar[str] = "Phase 10 Depositing Routes"
441 _FINISH: ClassVar[str] = "Phase 10 Depositing Routes | Checksum:"
442 _TIME: ClassVar[str] = "Time (s):"
445@export
446class Phase11_PostProcessRouting(Phase):
447 _START: ClassVar[str] = "Phase 11 Post Process Routing"
448 _FINISH: ClassVar[str] = "Phase 11 Post Process Routing | Checksum:"
449 _TIME: ClassVar[str] = "Time (s):"
452@export
453class Phase12_PostRouterTiming(Phase):
454 _START: ClassVar[str] = "Phase 12 Post Router Timing"
455 _FINISH: ClassVar[str] = "Phase 12 Post Router Timing | Checksum:"
456 _TIME: ClassVar[str] = "Time (s):"
459@export
460class Phase13_PostRouteEventProcessing(Phase):
461 _START: ClassVar[str] = "Phase 13 Post-Route Event Processing"
462 _FINISH: ClassVar[str] = "Phase 13 Post-Route Event Processing | Checksum:"
463 _TIME: ClassVar[str] = "Time (s):"
466@export
467class RoutingTask(Task):
468 _START: ClassVar[str] = "Starting Routing Task"
469 _FINISH: ClassVar[str] = "Ending Routing Task"
471 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = (
472 Phase1_BuildRTDesign,
473 Phase2_RouterInitialization,
474 Phase3_GlobalRouting,
475 Phase4_InitialRouting,
476 Phase5_RipUpAndReroute,
477 Phase6_DelayAndSkewOptimization,
478 Phase7_PostHoldFix,
479 Phase8_RouteFinalize,
480 Phase9_VerifyingRoutedNets,
481 Phase10_DepositingRoutes,
482 Phase11_PostProcessRouting,
483 Phase12_PostRouterTiming,
484 Phase13_PostRouteEventProcessing
485 )
487 def Generator(self, line: Line) -> Generator[Line, Line, Line]:
488 line = yield from self._TaskStart(line)
490 activeParsers: List[Phase] = list(self._phases.values())
492 while True:
493 while True:
494 if isinstance(line, VivadoMessage):
495 self._AddMessage(line)
496 elif line.StartsWith("Phase "):
497 for parser in activeParsers: # type: Section 497 ↛ 502line 497 didn't jump to line 502 because the loop on line 497 didn't complete
498 if line.StartsWith(parser._START): 498 ↛ 497line 498 didn't jump to line 497 because the condition on line 498 was always true
499 line = yield next(phase := parser.Generator(line))
500 break
501 else:
502 raise Exception(f"Unknown phase: {line!r}")
503 break
504 elif line.StartsWith("Ending"):
505 nextLine = yield from self._TaskFinish(line)
506 return nextLine
507 elif line.StartsWith(self._TIME): 507 ↛ 508line 507 didn't jump to line 508 because the condition on line 507 was never true
508 line._kind = LineKind.TaskTime
509 nextLine = yield line
510 return nextLine
512 line = yield line
514 while phase is not None: 514 ↛ 492line 514 didn't jump to line 492 because the condition on line 514 was always true
515 # if line.StartsWith("Ending"):
516 # line = yield task.send(line)
517 # break
519 if isinstance(line, VivadoMessage):
520 self._AddMessage(line)
522 try:
523 line = yield phase.send(line)
524 except StopIteration as ex:
525 activeParsers.remove(parser)
526 line = ex.value
527 break