Coverage for pyEDAA / OutputFilter / Xilinx / Commands.py: 82%
488 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"""Basic classes for outputs from AMD/Xilinx Vivado."""
32from pathlib import Path
33from re import compile as re_compile
34from typing import ClassVar, Generator, Union, List, Type, Dict, Iterator, Any, Tuple
36from pyTooling.Decorators import export, readonly
37from pyTooling.Versioning import YearReleaseVersion
38from pyTooling.Warning import WarningCollector
40from pyEDAA.OutputFilter import OutputFilterException
41from pyEDAA.OutputFilter.Xilinx import VivadoTclCommand
42from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException
43from pyEDAA.OutputFilter.Xilinx.Common import Line, LineKind, VivadoMessage, VHDLReportMessage
44from pyEDAA.OutputFilter.Xilinx.Common2 import Parser, UnknownSection, UnknownTask
45from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import Section, RTLElaboration, HandlingCustomAttributes
46from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ConstraintValidation, LoadingPart, ApplySetProperty
47from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RTLComponentStatistics, RTLHierarchicalComponentStatistics
48from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import PartResourceSummary, CrossBoundaryAndAreaOptimization
49from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ROM_RAM_DSP_SR_Retiming, ApplyingXDCTimingConstraints
50from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import TimingOptimization, TechnologyMapping, IOInsertion
51from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import FlatteningBeforeIOInsertion, FinalNetlistCleanup
52from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedInstances, RebuildingUserHierarchy
53from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedPorts, RenamingGeneratedNets
54from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import WritingSynthesisReport
55from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import Task, DRCTask, CacheTimingInformationTask, LogicOptimizationTask
56from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import PowerOptimizationTask, FinalCleanupTask, NetlistObfuscationTask
57from pyEDAA.OutputFilter.Xilinx.PlaceDesign import PlacerTask
58from pyEDAA.OutputFilter.Xilinx.PhysicalOptimizeDesign import PhysicalSynthesisTask, InitialUpdateTimingTask
59from pyEDAA.OutputFilter.Xilinx.RouteDesign import RoutingTask
62@export
63class NotPresentException(ProcessorException):
64 pass
67@export
68class SectionNotPresentException(NotPresentException):
69 pass
72@export
73class HandlingCustomAttributes1(HandlingCustomAttributes):
74 pass
77@export
78class HandlingCustomAttributes2(HandlingCustomAttributes):
79 pass
82@export
83class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming):
84 pass
87@export
88class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming):
89 pass
92@export
93class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming):
94 pass
97@export
98class Command(Parser):
99 # _TCL_COMMAND: ClassVar[str]
101 def _CommandStart(self, line: Line) -> Generator[Line, Line, Line]:
102 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 102 ↛ 103line 102 didn't jump to line 103 because the condition on line 102 was never true
103 raise ProcessorException()
105 nextLine = yield line
106 return nextLine
108 def _CommandFinish(self, line: Line) -> Generator[Line, Line, Line]:
109 if line.StartsWith(f"{self._TCL_COMMAND} completed successfully"): 109 ↛ 112line 109 didn't jump to line 112 because the condition on line 109 was always true
110 line._kind |= LineKind.Success
111 else:
112 line._kind |= LineKind.Failed
114 line = yield line
115 end = f"{self._TCL_COMMAND}: {self._TIME}"
116 while self._TIME is not None:
117 if line.StartsWith(end): 117 ↛ 121line 117 didn't jump to line 121 because the condition on line 117 was always true
118 line._kind = LineKind.TaskTime
119 break
121 line = yield line
123 nextLine = yield line
124 return nextLine
126 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]:
127 line = yield from self._CommandStart(line)
129 end = f"{self._TCL_COMMAND} "
130 while True:
131 if line._kind is LineKind.Empty:
132 line = yield line
133 continue
134 elif isinstance(line, VivadoMessage):
135 self._AddMessage(line)
136 elif line.StartsWith(end):
137 nextLine = yield from self._CommandFinish(line)
138 return nextLine
140 line = yield line
142 def __str__(self) -> str:
143 return f"{self._TCL_COMMAND}"
146@export
147class CommandWithSections(Command):
148 _sections: Dict[Type[Section], Section]
150 _PARSERS: ClassVar[Tuple[Type[Section], ...]] = dict()
152 def __init__(self, processor: "Processor") -> None:
153 super().__init__(processor)
155 self._sections = {p: p(self) for p in self._PARSERS}
157 @readonly
158 def Sections(self) -> Dict[Type[Section], Section]:
159 return self._sections
161 def __getitem__(self, key: Type[Section]) -> Section:
162 return self._sections[key]
165@export
166class CommandWithTasks(Command):
167 _tasks: Dict[Type[Task], Task]
169 def __init__(self, processor: "Processor") -> None:
170 super().__init__(processor)
172 self._tasks = {t: t(self) for t in self._PARSERS}
174 @readonly
175 def Tasks(self) -> Dict[Type[Task], Task]:
176 return self._tasks
178 def __getitem__(self, key: Type[Task]) -> Task:
179 return self._tasks[key]
182@export
183class SynthesizeDesign(CommandWithSections):
184 _TCL_COMMAND: ClassVar[str] = "synth_design"
185 _PARSERS: ClassVar[Tuple[Type[Section], ...]] = (
186 RTLElaboration,
187 HandlingCustomAttributes1,
188 ConstraintValidation,
189 LoadingPart,
190 ApplySetProperty,
191 RTLComponentStatistics,
192 RTLHierarchicalComponentStatistics,
193 PartResourceSummary,
194 CrossBoundaryAndAreaOptimization,
195 ROM_RAM_DSP_SR_Retiming1,
196 ApplyingXDCTimingConstraints,
197 TimingOptimization,
198 ROM_RAM_DSP_SR_Retiming2,
199 TechnologyMapping,
200 IOInsertion,
201 FlatteningBeforeIOInsertion,
202 FinalNetlistCleanup,
203 RenamingGeneratedInstances,
204 RebuildingUserHierarchy,
205 RenamingGeneratedPorts,
206 HandlingCustomAttributes2,
207 RenamingGeneratedNets,
208 ROM_RAM_DSP_SR_Retiming3,
209 WritingSynthesisReport,
210 )
212 @readonly
213 def HasLatches(self) -> bool:
214 if (8 in self._messagesByID) and (327 in self._messagesByID[8]):
215 return True
217 return "LD" in self._sections[WritingSynthesisReport]._cells
219 @readonly
220 def Latches(self) -> Iterator[VivadoMessage]:
221 try:
222 yield from iter(self._messagesByID[8][327])
223 except KeyError:
224 yield from ()
226 @readonly
227 def HasBlackboxes(self) -> bool:
228 return len(self._sections[WritingSynthesisReport]._blackboxes) > 0
230 @readonly
231 def Blackboxes(self) -> Dict[str, int]:
232 return self._sections[WritingSynthesisReport]._blackboxes
234 @readonly
235 def Cells(self) -> Dict[str, int]:
236 return self._sections[WritingSynthesisReport]._cells
238 @readonly
239 def VHDLReportMessages(self) -> List[VHDLReportMessage]:
240 if 8 in self._messagesByID:
241 if 6031 in (synthMessages := self._messagesByID[8]):
242 return [message for message in synthMessages[6031]]
244 return []
246 @readonly
247 def VHDLAssertMessages(self) -> List[VHDLReportMessage]:
248 if 8 in self._messagesByID:
249 if 63 in (synthMessages := self._messagesByID[8]):
250 return [message for message in synthMessages[63]]
252 return []
254 def __getitem__(self, item: Type[Parser]) -> Union[_PARSERS]:
255 try:
256 return self._sections[item]
257 except KeyError as ex:
258 raise SectionNotPresentException(F"Section '{item._NAME}' not present in '{self._parent.logfile}'.") from ex
260 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]:
261 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 261 ↛ 262line 261 didn't jump to line 262 because the condition on line 261 was never true
262 raise ProcessorException()
264 activeParsers: List[Parser] = list(self._sections.values())
266 rtlElaboration = self._sections[RTLElaboration]
267 # constraintValidation = self._sections[ConstraintValidation]
269 line = yield line
270 if line == "Starting synth_design": 270 ↛ 273line 270 didn't jump to line 273 because the condition on line 270 was always true
271 line._kind = LineKind.Verbose
272 else:
273 raise ProcessorException()
275 line = yield line
276 while True:
277 while True:
278 if line._kind is LineKind.Empty:
279 line = yield line
280 continue
281 elif line.StartsWith("Start "):
282 for parser in activeParsers: # type: Section 282 ↛ 288line 282 didn't jump to line 288 because the loop on line 282 didn't complete
283 if line.StartsWith(parser._START):
284 line = next(section := parser.Generator(line))
285 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter
286 break
287 else:
288 WarningCollector.Raise(UnknownSection(f"Unknown section: '{line!r}'", line))
289 ex = Exception(f"How to recover from here? Unknown section: '{line!r}'")
290 ex.add_note(f"Current task: start pattern='{self._task}'")
291 ex.add_note(f"Current cmd: {self._task._command}")
292 raise ex
293 break
294 elif line.StartsWith("Starting "):
295 if line.StartsWith(rtlElaboration._START): 295 ↛ 318line 295 didn't jump to line 318 because the condition on line 295 was always true
296 parser = rtlElaboration
297 line = next(section := parser.Generator(line))
298 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter
299 break
300 elif line.StartsWith(self._TCL_COMMAND):
301 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 301 ↛ 318line 301 didn't jump to line 318 because the condition on line 301 was always true
302 line._kind |= LineKind.Success
304 # FIXME: use similar style like for _TIME
305 line = yield line
306 lastLine = yield line
307 return lastLine
308 elif line.StartsWith("Finished RTL Optimization Phase"):
309 line._kind = LineKind.PhaseEnd
310 line._previousLine._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter
311 elif line.StartsWith("----"):
312 if LineKind.Phase in line._previousLine._kind:
313 line._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter
314 elif not isinstance(line, VivadoMessage):
315 pass
316 # line._kind = LineKind.Unprocessed
318 line = yield line
320 line = yield line
322 while True:
323 if line.StartsWith("Finished"):
324 l = line[9:]
325 if not (l.startswith("Flattening") or l.startswith("Final")):
326 line = yield section.send(line)
327 break
329 if isinstance(line, VivadoMessage):
330 self._AddMessage(line)
332 line = yield section.send(line)
334 line = yield section.send(line)
336 activeParsers.remove(parser)
339@export
340class LinkDesign(Command):
341 _TCL_COMMAND: ClassVar[str] = "link_design"
342 _TIME: ClassVar[str] = "Time (s):"
344 _ParsingXDCFile_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\]$""")
345 _FinishedParsingXDCFile_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\]$""")
346 _ParsingXDCFileForCell_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\] for cell '(.*)'$""")
347 _FinishedParsingXDCFileForCell_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\] for cell '(.*)'$""")
349 _commonXDCFiles: Dict[Path, List[VivadoMessage]]
350 _perCellXDCFiles: Dict[Path, Dict[str, List[VivadoMessage]]]
352 def __init__(self, processor: "Processor") -> None:
353 super().__init__(processor)
355 self._commonXDCFiles = {}
356 self._perCellXDCFiles = {}
358 @readonly
359 def CommonXDCFiles(self) -> Dict[Path, List[VivadoMessage]]:
360 return self._commonXDCFiles
362 @readonly
363 def PerCellXDCFiles(self) -> Dict[Path, Dict[str, List[VivadoMessage]]]:
364 return self._perCellXDCFiles
366 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
367 line = yield from self._CommandStart(line)
369 end = f"{self._TCL_COMMAND} "
370 while True:
371 if line._kind is LineKind.Empty:
372 line = yield line
373 continue
374 elif isinstance(line, VivadoMessage):
375 self._AddMessage(line)
376 elif (match := self._ParsingXDCFile_Pattern.match(line._message)) is not None:
377 line._kind = LineKind.Normal
379 path = Path(match[1])
380 self._commonXDCFiles[path] = (messages := [])
382 line = yield line
383 while True:
384 if line._kind is LineKind.Empty: 384 ↛ 385line 384 didn't jump to line 385 because the condition on line 384 was never true
385 line = yield line
386 continue
387 elif isinstance(line, VivadoMessage):
388 self._AddMessage(line)
389 messages.append(line)
390 elif (match := self._FinishedParsingXDCFile_Pattern.match(line._message)) is not None and path == Path(match[1]):
391 line._kind = LineKind.Normal
392 break
393 elif line.StartsWith("Finished Parsing XDC File"): 393 ↛ 394line 393 didn't jump to line 394 because the condition on line 393 was never true
394 line._kind = LineKind.ProcessorError
395 break
396 elif line.StartsWith(end): 396 ↛ 397line 396 didn't jump to line 397 because the condition on line 396 was never true
397 break
399 line = yield line
400 elif (match := self._ParsingXDCFileForCell_Pattern.match(line._message)) is not None:
401 line._kind = LineKind.Normal
403 path = Path(match[1])
404 cell = match[2]
405 if path in self._perCellXDCFiles:
406 self._perCellXDCFiles[path][cell] = (messages := [])
407 else:
408 self._perCellXDCFiles[path] = {cell: (messages := [])}
410 line = yield line
411 while True:
412 if line._kind is LineKind.Empty: 412 ↛ 413line 412 didn't jump to line 413 because the condition on line 412 was never true
413 line = yield line
414 continue
415 elif isinstance(line, VivadoMessage):
416 self._AddMessage(line)
417 messages.append(line)
418 elif (match := self._FinishedParsingXDCFileForCell_Pattern.match(line._message)) is not None and path == Path(match[1]) and cell == match[2]:
419 line._kind = LineKind.Normal
420 break
421 elif line.StartsWith("Finished Parsing XDC File"): 421 ↛ 422line 421 didn't jump to line 422 because the condition on line 421 was never true
422 line._kind = LineKind.ProcessorError
423 break
424 elif line.StartsWith(end): 424 ↛ 425line 424 didn't jump to line 425 because the condition on line 424 was never true
425 break
427 line = yield line
429 if line.StartsWith(end):
430 nextLine = yield from self._CommandFinish(line)
431 return nextLine
433 line = yield line
436@export
437class OptimizeDesign(CommandWithTasks):
438 _TCL_COMMAND: ClassVar[str] = "opt_design"
439 _TIME: ClassVar[str] = None
441 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
442 DRCTask,
443 CacheTimingInformationTask,
444 LogicOptimizationTask,
445 PowerOptimizationTask,
446 FinalCleanupTask,
447 NetlistObfuscationTask
448 )
450 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
451 line = yield from self._CommandStart(line)
453 activeParsers: List[Task] = list(self._tasks.values())
455 while True:
456 while True:
457 if line._kind is LineKind.Empty:
458 line = yield line
459 continue
460 elif isinstance(line, VivadoMessage):
461 self._AddMessage(line)
462 elif line.StartsWith("Starting ") and not line.StartsWith("Starting Connectivity Check Task"):
463 for parser in activeParsers: # type: Section 463 ↛ 468line 463 didn't jump to line 468 because the loop on line 463 didn't complete
464 if line.StartsWith(parser._START): 464 ↛ 463line 464 didn't jump to line 463 because the condition on line 464 was always true
465 line = yield next(task := parser.Generator(line))
466 break
467 else:
468 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line))
469 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'")
470 ex.add_note(f"Current task: start pattern='{self._task}'")
471 ex.add_note(f"Current cmd: {self._task._command}")
472 raise ex
473 break
474 elif line.StartsWith(self._TCL_COMMAND):
475 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 475 ↛ 484line 475 didn't jump to line 484 because the condition on line 475 was always true
476 line._kind |= LineKind.Success
478 # FIXME: use similar style like for _TIME
479 line = yield line
480 lastLine = yield line
481 return lastLine
482 # line._kind = LineKind.Unprocessed
484 line = yield line
486 while True:
487 # if line.StartsWith("Ending"):
488 # line = yield task.send(line)
489 # break
491 if isinstance(line, VivadoMessage):
492 self._AddMessage(line)
494 try:
495 line = yield task.send(line)
496 except StopIteration as ex:
497 task = None
498 line = ex.value
500 if isinstance(line, VivadoMessage):
501 line = yield line
503 break
505 if task is not None: 505 ↛ 506line 505 didn't jump to line 506 because the condition on line 505 was never true
506 line = yield task.send(line)
508 activeParsers.remove(parser)
511@export
512class PlaceDesign(CommandWithTasks):
513 _TCL_COMMAND: ClassVar[str] = "place_design"
514 _TIME: ClassVar[str] = None
516 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
517 PlacerTask,
518 )
520 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
521 line = yield from self._CommandStart(line)
523 activeParsers: List[Task] = list(self._tasks.values())
525 while True:
526 while True:
527 if line._kind is LineKind.Empty:
528 line = yield line
529 continue
530 elif isinstance(line, VivadoMessage):
531 self._AddMessage(line)
532 elif line.StartsWith("Starting "):
533 for parser in activeParsers: # type: Section 533 ↛ 538line 533 didn't jump to line 538 because the loop on line 533 didn't complete
534 if line.StartsWith(parser._START): 534 ↛ 533line 534 didn't jump to line 533 because the condition on line 534 was always true
535 line = yield next(task := parser.Generator(line))
536 break
537 else:
538 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line))
539 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'")
540 ex.add_note(f"Current task: start pattern='{self._task}'")
541 ex.add_note(f"Current cmd: {self._task._command}")
542 raise ex
543 break
544 elif line.StartsWith(self._TCL_COMMAND):
545 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 545 ↛ 554line 545 didn't jump to line 554 because the condition on line 545 was always true
546 line._kind |= LineKind.Success
548 # FIXME: use similar style like for _TIME
549 line = yield line
550 lastLine = yield line
551 return lastLine
552 # line._kind = LineKind.Unprocessed
554 line = yield line
556 while True:
557 # if line.StartsWith("Ending"):
558 # line = yield task.send(line)
559 # break
561 if isinstance(line, VivadoMessage):
562 self._AddMessage(line)
564 try:
565 line = yield task.send(line)
566 except StopIteration as ex:
567 task = None
568 line = ex.value
570 if isinstance(line, VivadoMessage): 570 ↛ 571line 570 didn't jump to line 571 because the condition on line 570 was never true
571 line = yield line
573 break
575 if task is not None: 575 ↛ 576line 575 didn't jump to line 576 because the condition on line 575 was never true
576 line = yield task.send(line)
578 activeParsers.remove(parser)
581@export
582class PhysicalOptimizeDesign(CommandWithTasks):
583 _TCL_COMMAND: ClassVar[str] = "phys_opt_design"
584 _TIME: ClassVar[str] = None
586 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
587 InitialUpdateTimingTask,
588 PhysicalSynthesisTask
589 )
591 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
592 line = yield from self._CommandStart(line)
594 activeParsers: List[Task] = list(self._tasks.values())
596 while True:
597 while True:
598 if line._kind is LineKind.Empty:
599 line = yield line
600 continue
601 elif isinstance(line, VivadoMessage):
602 self._AddMessage(line)
603 elif line.StartsWith("Starting "):
604 for parser in activeParsers: # type: Section 604 ↛ 609line 604 didn't jump to line 609 because the loop on line 604 didn't complete
605 if line.StartsWith(parser._START): 605 ↛ 604line 605 didn't jump to line 604 because the condition on line 605 was always true
606 line = yield next(task := parser.Generator(line))
607 break
608 else:
609 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line))
610 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'")
611 ex.add_note(f"Current task: start pattern='{self._task}'")
612 ex.add_note(f"Current cmd: {self._task._command}")
613 raise ex
614 break
615 elif line.StartsWith(self._TCL_COMMAND):
616 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 616 ↛ 625line 616 didn't jump to line 625 because the condition on line 616 was always true
617 line._kind |= LineKind.Success
619 # FIXME: use similar style like for _TIME
620 line = yield line
621 lastLine = yield line
622 return lastLine
623 # line._kind = LineKind.Unprocessed
625 line = yield line
627 while True:
628 # if line.StartsWith("Ending"):
629 # line = yield task.send(line)
630 # break
632 if isinstance(line, VivadoMessage):
633 self._AddMessage(line)
635 try:
636 line = yield task.send(line)
637 except StopIteration as ex:
638 task = None
639 line = ex.value
641 if isinstance(line, VivadoMessage): 641 ↛ 644line 641 didn't jump to line 644 because the condition on line 641 was always true
642 line = yield line
644 break
646 if task is not None: 646 ↛ 647line 646 didn't jump to line 647 because the condition on line 646 was never true
647 line = yield task.send(line)
649 activeParsers.remove(parser)
652@export
653class RouteDesign(CommandWithTasks):
654 _TCL_COMMAND: ClassVar[str] = "route_design"
655 _TIME: ClassVar[str] = "Time (s):"
657 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
658 RoutingTask,
659 )
661 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
662 line = yield from self._CommandStart(line)
664 activeParsers: List[Task] = list(self._tasks.values())
666 while True:
667 while True:
668 if line._kind is LineKind.Empty:
669 line = yield line
670 continue
671 elif isinstance(line, VivadoMessage):
672 self._AddMessage(line)
673 elif line.StartsWith("Starting "):
674 for parser in activeParsers: # type: Section 674 ↛ 679line 674 didn't jump to line 679 because the loop on line 674 didn't complete
675 if line.StartsWith(parser._START): 675 ↛ 674line 675 didn't jump to line 674 because the condition on line 675 was always true
676 line = yield next(task := parser.Generator(line))
677 break
678 else:
679 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line))
680 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'")
681 ex.add_note(f"Current task: start pattern='{self._task}'")
682 ex.add_note(f"Current cmd: {self._task._command}")
683 raise ex
684 break
685 elif line.StartsWith(self._TCL_COMMAND):
686 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 686 ↛ 695line 686 didn't jump to line 695 because the condition on line 686 was always true
687 line._kind |= LineKind.Success
689 # FIXME: use similar style like for _TIME
690 line = yield line
691 lastLine = yield line
692 return lastLine
693 # line._kind = LineKind.Unprocessed
695 line = yield line
697 while True:
698 # if line.StartsWith("Ending"):
699 # line = yield task.send(line)
700 # break
702 if isinstance(line, VivadoMessage):
703 self._AddMessage(line)
705 try:
706 line = yield task.send(line)
707 except StopIteration as ex:
708 task = None
709 line = ex.value
711 if isinstance(line, VivadoMessage): 711 ↛ 712line 711 didn't jump to line 712 because the condition on line 711 was never true
712 line = yield line
714 break
716 if task is not None: 716 ↛ 717line 716 didn't jump to line 717 because the condition on line 716 was never true
717 line = yield task.send(line)
719 activeParsers.remove(parser)
722@export
723class WriteBitstream(Command):
724 _TCL_COMMAND: ClassVar[str] = "write_bitstream"
725 _TIME: ClassVar[str] = "Time (s):"
728@export
729class ReportDRC(Command):
730 _TCL_COMMAND: ClassVar[str] = "report_drc"
731 _TIME: ClassVar[str] = None
734@export
735class ReportMethodology(Command):
736 _TCL_COMMAND: ClassVar[str] = "report_methodology"
737 _TIME: ClassVar[str] = None
740@export
741class ReportPower(Command):
742 _TCL_COMMAND: ClassVar[str] = "report_power"
743 _TIME: ClassVar[str] = None