Coverage for pyEDAA/OutputFilter/Xilinx/Commands.py: 85%
453 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"""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
38from pyEDAA.OutputFilter.Xilinx import VivadoTclCommand
39from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException
40from pyEDAA.OutputFilter.Xilinx.Common import Line, LineKind, VivadoMessage, VHDLReportMessage
41from pyEDAA.OutputFilter.Xilinx.Common2 import Parser
42from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import Section, RTLElaboration, HandlingCustomAttributes
43from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ConstraintValidation, LoadingPart, ApplySetProperty
44from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RTLComponentStatistics, PartResourceSummary
45from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import CrossBoundaryAndAreaOptimization, ROM_RAM_DSP_SR_Retiming
46from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ApplyingXDCTimingConstraints, TimingOptimization
47from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import TechnologyMapping, IOInsertion, FlatteningBeforeIOInsertion
48from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import FinalNetlistCleanup, RenamingGeneratedInstances
49from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RebuildingUserHierarchy, RenamingGeneratedPorts
50from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedNets, WritingSynthesisReport
51from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import Task, DRCTask, CacheTimingInformationTask, LogicOptimizationTask
52from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import PowerOptimizationTask, FinalCleanupTask, NetlistObfuscationTask
53from pyEDAA.OutputFilter.Xilinx.PlaceDesign import PlacerTask
54from pyEDAA.OutputFilter.Xilinx.PhysicalOptimizeDesign import PhysicalSynthesisTask, InitialUpdateTimingTask
55from pyEDAA.OutputFilter.Xilinx.RouteDesign import RoutingTask
58@export
59class NotPresentException(ProcessorException):
60 pass
63@export
64class SectionNotPresentException(NotPresentException):
65 pass
68@export
69class HandlingCustomAttributes1(HandlingCustomAttributes):
70 pass
73@export
74class HandlingCustomAttributes2(HandlingCustomAttributes):
75 pass
78@export
79class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming):
80 pass
83@export
84class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming):
85 pass
88@export
89class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming):
90 pass
93@export
94class Command(Parser):
95 # _TCL_COMMAND: ClassVar[str]
97 def _CommandStart(self, line: Line) -> Generator[Line, Line, Line]:
98 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 98 ↛ 99line 98 didn't jump to line 99 because the condition on line 98 was never true
99 raise ProcessorException()
101 nextLine = yield line
102 return nextLine
104 def _CommandFinish(self, line: Line) -> Generator[Line, Line, Line]:
105 if line.StartsWith(f"{self._TCL_COMMAND} completed successfully"): 105 ↛ 108line 105 didn't jump to line 108 because the condition on line 105 was always true
106 line._kind |= LineKind.Success
107 else:
108 line._kind |= LineKind.Failed
110 line = yield line
111 end = f"{self._TCL_COMMAND}: {self._TIME}"
112 while self._TIME is not None:
113 if line.StartsWith(end): 113 ↛ 117line 113 didn't jump to line 117 because the condition on line 113 was always true
114 line._kind = LineKind.TaskTime
115 break
117 line = yield line
119 nextLine = yield line
120 return nextLine
122 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]:
123 line = yield from self._CommandStart(line)
125 end = f"{self._TCL_COMMAND} "
126 while True:
127 if line._kind is LineKind.Empty:
128 line = yield line
129 continue
130 elif isinstance(line, VivadoMessage):
131 self._AddMessage(line)
132 elif line.StartsWith(end):
133 nextLine = yield from self._CommandFinish(line)
134 return nextLine
136 line = yield line
139@export
140class CommandWithSections(Command):
141 _sections: Dict[Type[Section], Section]
143 def __init__(self, processor: "Processor") -> None:
144 super().__init__(processor)
146 self._sections = {p: p(self) for p in self._PARSERS}
148 @readonly
149 def Sections(self) -> Dict[Type[Section], Section]:
150 return self._sections
152 def __getitem__(self, key: Type[Section]) -> Section:
153 return self._sections[key]
156@export
157class CommandWithTasks(Command):
158 _tasks: Dict[Type[Task], Task]
160 def __init__(self, processor: "Processor"):
161 super().__init__(processor)
163 self._tasks = {t: t(self) for t in self._PARSERS}
165 @readonly
166 def Tasks(self) -> Dict[Type[Task], Task]:
167 return self._tasks
169 def __getitem__(self, key: Type[Task]) -> Task:
170 return self._tasks[key]
173@export
174class SynthesizeDesign(CommandWithSections):
175 _TCL_COMMAND: ClassVar[str] = "synth_design"
176 _PARSERS: ClassVar[List[Type[Section]]] = (
177 RTLElaboration,
178 HandlingCustomAttributes1,
179 ConstraintValidation,
180 LoadingPart,
181 ApplySetProperty,
182 RTLComponentStatistics,
183 PartResourceSummary,
184 CrossBoundaryAndAreaOptimization,
185 ROM_RAM_DSP_SR_Retiming1,
186 ApplyingXDCTimingConstraints,
187 TimingOptimization,
188 ROM_RAM_DSP_SR_Retiming2,
189 TechnologyMapping,
190 IOInsertion,
191 FlatteningBeforeIOInsertion,
192 FinalNetlistCleanup,
193 RenamingGeneratedInstances,
194 RebuildingUserHierarchy,
195 RenamingGeneratedPorts,
196 HandlingCustomAttributes2,
197 RenamingGeneratedNets,
198 ROM_RAM_DSP_SR_Retiming3,
199 WritingSynthesisReport,
200 )
202 @readonly
203 def HasLatches(self) -> bool:
204 if (8 in self._messagesByID) and (327 in self._messagesByID[8]):
205 return True
207 return "LD" in self._sections[WritingSynthesisReport]._cells
209 @readonly
210 def Latches(self) -> Iterator[VivadoMessage]:
211 try:
212 yield from iter(self._messagesByID[8][327])
213 except KeyError:
214 yield from ()
216 @readonly
217 def HasBlackboxes(self) -> bool:
218 return len(self._sections[WritingSynthesisReport]._blackboxes) > 0
220 @readonly
221 def Blackboxes(self) -> Dict[str, int]:
222 return self._sections[WritingSynthesisReport]._blackboxes
224 @readonly
225 def Cells(self) -> Dict[str, int]:
226 return self._sections[WritingSynthesisReport]._cells
228 @readonly
229 def VHDLReportMessages(self) -> List[VHDLReportMessage]:
230 if 8 in self._messagesByID:
231 if 6031 in (synthMessages := self._messagesByID[8]):
232 return [message for message in synthMessages[6031]]
234 return []
236 @readonly
237 def VHDLAssertMessages(self) -> List[VHDLReportMessage]:
238 if 8 in self._messagesByID:
239 if 63 in (synthMessages := self._messagesByID[8]):
240 return [message for message in synthMessages[63]]
242 return []
244 def __getitem__(self, item: Type[Parser]) -> Union[_PARSERS]:
245 try:
246 return self._sections[item]
247 except KeyError as ex:
248 raise SectionNotPresentException(F"Section '{item._NAME}' not present in '{self._parent.logfile}'.") from ex
250 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]:
251 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 251 ↛ 252line 251 didn't jump to line 252 because the condition on line 251 was never true
252 raise ProcessorException()
254 activeParsers: List[Parser] = list(self._sections.values())
256 rtlElaboration = self._sections[RTLElaboration]
257 # constraintValidation = self._sections[ConstraintValidation]
259 line = yield line
260 if line == "Starting synth_design": 260 ↛ 263line 260 didn't jump to line 263 because the condition on line 260 was always true
261 line._kind = LineKind.Verbose
262 else:
263 raise ProcessorException()
265 line = yield line
266 while True:
267 while True:
268 if line._kind is LineKind.Empty:
269 line = yield line
270 continue
271 elif line.StartsWith("Start "):
272 for parser in activeParsers: # type: Section 272 ↛ 278line 272 didn't jump to line 278 because the loop on line 272 didn't complete
273 if line.StartsWith(parser._START):
274 line = next(section := parser.Generator(line))
275 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter
276 break
277 else:
278 raise Exception(f"Unknown section: {line!r}")
279 break
280 elif line.StartsWith("Starting "):
281 if line.StartsWith(rtlElaboration._START): 281 ↛ 304line 281 didn't jump to line 304 because the condition on line 281 was always true
282 parser = rtlElaboration
283 line = next(section := parser.Generator(line))
284 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter
285 break
286 elif line.StartsWith(self._TCL_COMMAND):
287 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 287 ↛ 304line 287 didn't jump to line 304 because the condition on line 287 was always true
288 line._kind |= LineKind.Success
290 # FIXME: use similar style like for _TIME
291 line = yield line
292 lastLine = yield line
293 return lastLine
294 elif line.StartsWith("Finished RTL Optimization Phase"):
295 line._kind = LineKind.PhaseEnd
296 line._previousLine._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter
297 elif line.StartsWith("----"):
298 if LineKind.Phase in line._previousLine._kind:
299 line._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter
300 elif not isinstance(line, VivadoMessage):
301 pass
302 # line._kind = LineKind.Unprocessed
304 line = yield line
306 line = yield line
308 while True:
309 if line.StartsWith("Finished"):
310 l = line[9:]
311 if not (l.startswith("Flattening") or l.startswith("Final")):
312 line = yield section.send(line)
313 break
315 if isinstance(line, VivadoMessage):
316 self._AddMessage(line)
318 line = yield section.send(line)
320 line = yield section.send(line)
322 activeParsers.remove(parser)
325@export
326class LinkDesign(Command):
327 _TCL_COMMAND: ClassVar[str] = "link_design"
328 _TIME: ClassVar[str] = "Time (s):"
330 _ParsingXDCFile_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\]$""")
331 _FinishedParsingXDCFile_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\]$""")
332 _ParsingXDCFileForCell_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\] for cell '(.*)'$""")
333 _FinishedParsingXDCFileForCell_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\] for cell '(.*)'$""")
335 _commonXDCFiles: Dict[Path, List[VivadoMessage]]
336 _perCellXDCFiles: Dict[Path, Dict[str, List[VivadoMessage]]]
338 def __init__(self, processor: "Processor"):
339 super().__init__(processor)
341 self._commonXDCFiles = {}
342 self._perCellXDCFiles = {}
344 @readonly
345 def CommonXDCFiles(self) -> Dict[Path, List[VivadoMessage]]:
346 return self._commonXDCFiles
348 @readonly
349 def PerCellXDCFiles(self) -> Dict[Path, Dict[str, List[VivadoMessage]]]:
350 return self._perCellXDCFiles
352 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
353 line = yield from self._CommandStart(line)
355 end = f"{self._TCL_COMMAND} "
356 while True:
357 if line._kind is LineKind.Empty:
358 line = yield line
359 continue
360 elif isinstance(line, VivadoMessage):
361 self._AddMessage(line)
362 elif (match := self._ParsingXDCFile_Pattern.match(line._message)) is not None:
363 line._kind = LineKind.Normal
365 path = Path(match[1])
366 self._commonXDCFiles[path] = (messages := [])
368 line = yield line
369 while True:
370 if line._kind is LineKind.Empty: 370 ↛ 371line 370 didn't jump to line 371 because the condition on line 370 was never true
371 line = yield line
372 continue
373 elif isinstance(line, VivadoMessage):
374 self._AddMessage(line)
375 messages.append(line)
376 elif (match := self._FinishedParsingXDCFile_Pattern.match(line._message)) is not None and path == Path(match[1]):
377 line._kind = LineKind.Normal
378 break
379 elif line.StartsWith("Finished Parsing XDC File"): 379 ↛ 380line 379 didn't jump to line 380 because the condition on line 379 was never true
380 line._kind = LineKind.ProcessorError
381 break
382 elif line.StartsWith(end): 382 ↛ 383line 382 didn't jump to line 383 because the condition on line 382 was never true
383 break
385 line = yield line
386 elif (match := self._ParsingXDCFileForCell_Pattern.match(line._message)) is not None:
387 line._kind = LineKind.Normal
389 path = Path(match[1])
390 cell = match[2]
391 if path in self._perCellXDCFiles:
392 self._perCellXDCFiles[path][cell] = (messages := [])
393 else:
394 self._perCellXDCFiles[path] = {cell: (messages := [])}
396 line = yield line
397 while True:
398 if line._kind is LineKind.Empty: 398 ↛ 399line 398 didn't jump to line 399 because the condition on line 398 was never true
399 line = yield line
400 continue
401 elif isinstance(line, VivadoMessage):
402 self._AddMessage(line)
403 messages.append(line)
404 elif (match := self._FinishedParsingXDCFileForCell_Pattern.match(line._message)) is not None and path == Path(match[1]) and cell == match[2]:
405 line._kind = LineKind.Normal
406 break
407 elif line.StartsWith("Finished Parsing XDC File"): 407 ↛ 408line 407 didn't jump to line 408 because the condition on line 407 was never true
408 line._kind = LineKind.ProcessorError
409 break
410 elif line.StartsWith(end): 410 ↛ 411line 410 didn't jump to line 411 because the condition on line 410 was never true
411 break
413 line = yield line
415 if line.StartsWith(end):
416 nextLine = yield from self._CommandFinish(line)
417 return nextLine
419 line = yield line
422@export
423class OptimizeDesign(CommandWithTasks):
424 _TCL_COMMAND: ClassVar[str] = "opt_design"
425 _TIME: ClassVar[str] = None
427 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
428 DRCTask,
429 CacheTimingInformationTask,
430 LogicOptimizationTask,
431 PowerOptimizationTask,
432 FinalCleanupTask,
433 NetlistObfuscationTask
434 )
436 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
437 line = yield from self._CommandStart(line)
439 activeParsers: List[Task] = list(self._tasks.values())
441 while True:
442 while True:
443 if line._kind is LineKind.Empty:
444 line = yield line
445 continue
446 elif isinstance(line, VivadoMessage):
447 self._AddMessage(line)
448 elif line.StartsWith("Starting ") and not line.StartsWith("Starting Connectivity Check Task"):
449 for parser in activeParsers: # type: Section 449 ↛ 454line 449 didn't jump to line 454 because the loop on line 449 didn't complete
450 if line.StartsWith(parser._START): 450 ↛ 449line 450 didn't jump to line 449 because the condition on line 450 was always true
451 line = yield next(task := parser.Generator(line))
452 break
453 else:
454 raise Exception(f"Unknown task: {line!r}")
455 break
456 elif line.StartsWith(self._TCL_COMMAND):
457 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 457 ↛ 466line 457 didn't jump to line 466 because the condition on line 457 was always true
458 line._kind |= LineKind.Success
460 # FIXME: use similar style like for _TIME
461 line = yield line
462 lastLine = yield line
463 return lastLine
464 # line._kind = LineKind.Unprocessed
466 line = yield line
468 while True:
469 # if line.StartsWith("Ending"):
470 # line = yield task.send(line)
471 # break
473 if isinstance(line, VivadoMessage):
474 self._AddMessage(line)
476 try:
477 line = yield task.send(line)
478 except StopIteration as ex:
479 task = None
480 line = ex.value
481 break
483 if task is not None: 483 ↛ 484line 483 didn't jump to line 484 because the condition on line 483 was never true
484 line = yield task.send(line)
486 activeParsers.remove(parser)
489@export
490class PlaceDesign(CommandWithTasks):
491 _TCL_COMMAND: ClassVar[str] = "place_design"
492 _TIME: ClassVar[str] = None
494 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
495 PlacerTask,
496 )
498 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
499 line = yield from self._CommandStart(line)
501 activeParsers: List[Task] = list(self._tasks.values())
503 while True:
504 while True:
505 if line._kind is LineKind.Empty:
506 line = yield line
507 continue
508 elif isinstance(line, VivadoMessage):
509 self._AddMessage(line)
510 elif line.StartsWith("Starting "):
511 for parser in activeParsers: # type: Section 511 ↛ 516line 511 didn't jump to line 516 because the loop on line 511 didn't complete
512 if line.StartsWith(parser._START): 512 ↛ 511line 512 didn't jump to line 511 because the condition on line 512 was always true
513 line = yield next(task := parser.Generator(line))
514 break
515 else:
516 raise Exception(f"Unknown task: {line!r}")
517 break
518 elif line.StartsWith(self._TCL_COMMAND):
519 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 519 ↛ 528line 519 didn't jump to line 528 because the condition on line 519 was always true
520 line._kind |= LineKind.Success
522 # FIXME: use similar style like for _TIME
523 line = yield line
524 lastLine = yield line
525 return lastLine
526 # line._kind = LineKind.Unprocessed
528 line = yield line
530 while True:
531 # if line.StartsWith("Ending"):
532 # line = yield task.send(line)
533 # break
535 if isinstance(line, VivadoMessage):
536 self._AddMessage(line)
538 try:
539 line = yield task.send(line)
540 except StopIteration as ex:
541 task = None
542 line = ex.value
543 break
545 if task is not None: 545 ↛ 546line 545 didn't jump to line 546 because the condition on line 545 was never true
546 line = yield task.send(line)
548 activeParsers.remove(parser)
551@export
552class PhysicalOptimizeDesign(CommandWithTasks):
553 _TCL_COMMAND: ClassVar[str] = "phys_opt_design"
554 _TIME: ClassVar[str] = None
556 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
557 InitialUpdateTimingTask,
558 PhysicalSynthesisTask
559 )
561 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
562 line = yield from self._CommandStart(line)
564 activeParsers: List[Task] = list(self._tasks.values())
566 while True:
567 while True:
568 if line._kind is LineKind.Empty:
569 line = yield line
570 continue
571 elif isinstance(line, VivadoMessage):
572 self._AddMessage(line)
573 elif line.StartsWith("Starting "):
574 for parser in activeParsers: # type: Section 574 ↛ 579line 574 didn't jump to line 579 because the loop on line 574 didn't complete
575 if line.StartsWith(parser._START): 575 ↛ 574line 575 didn't jump to line 574 because the condition on line 575 was always true
576 line = yield next(task := parser.Generator(line))
577 break
578 else:
579 raise Exception(f"Unknown task: {line!r}")
580 break
581 elif line.StartsWith(self._TCL_COMMAND):
582 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 582 ↛ 591line 582 didn't jump to line 591 because the condition on line 582 was always true
583 line._kind |= LineKind.Success
585 # FIXME: use similar style like for _TIME
586 line = yield line
587 lastLine = yield line
588 return lastLine
589 # line._kind = LineKind.Unprocessed
591 line = yield line
593 while True:
594 # if line.StartsWith("Ending"):
595 # line = yield task.send(line)
596 # break
598 if isinstance(line, VivadoMessage):
599 self._AddMessage(line)
601 try:
602 line = yield task.send(line)
603 except StopIteration as ex:
604 task = None
605 line = ex.value
606 break
608 if task is not None: 608 ↛ 609line 608 didn't jump to line 609 because the condition on line 608 was never true
609 line = yield task.send(line)
611 activeParsers.remove(parser)
614@export
615class RouteDesign(CommandWithTasks):
616 _TCL_COMMAND: ClassVar[str] = "route_design"
617 _TIME: ClassVar[str] = "Time (s):"
619 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = (
620 RoutingTask,
621 )
623 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]:
624 line = yield from self._CommandStart(line)
626 activeParsers: List[Task] = list(self._tasks.values())
628 while True:
629 while True:
630 if line._kind is LineKind.Empty:
631 line = yield line
632 continue
633 elif isinstance(line, VivadoMessage):
634 self._AddMessage(line)
635 elif line.StartsWith("Starting "):
636 for parser in activeParsers: # type: Section 636 ↛ 641line 636 didn't jump to line 641 because the loop on line 636 didn't complete
637 if line.StartsWith(parser._START): 637 ↛ 636line 637 didn't jump to line 636 because the condition on line 637 was always true
638 line = yield next(task := parser.Generator(line))
639 break
640 else:
641 raise Exception(f"Unknown task: {line!r}")
642 break
643 elif line.StartsWith(self._TCL_COMMAND):
644 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 644 ↛ 653line 644 didn't jump to line 653 because the condition on line 644 was always true
645 line._kind |= LineKind.Success
647 # FIXME: use similar style like for _TIME
648 line = yield line
649 lastLine = yield line
650 return lastLine
651 # line._kind = LineKind.Unprocessed
653 line = yield line
655 while True:
656 # if line.StartsWith("Ending"):
657 # line = yield task.send(line)
658 # break
660 if isinstance(line, VivadoMessage):
661 self._AddMessage(line)
663 try:
664 line = yield task.send(line)
665 except StopIteration as ex:
666 task = None
667 line = ex.value
668 break
670 if task is not None: 670 ↛ 671line 670 didn't jump to line 671 because the condition on line 670 was never true
671 line = yield task.send(line)
673 activeParsers.remove(parser)
676@export
677class WriteBitstream(Command):
678 _TCL_COMMAND: ClassVar[str] = "write_bitstream"
679 _TIME: ClassVar[str] = "Time (s):"
682@export
683class ReportDRC(Command):
684 _TCL_COMMAND: ClassVar[str] = "report_drc"
685 _TIME: ClassVar[str] = None
688@export
689class ReportMethodology(Command):
690 _TCL_COMMAND: ClassVar[str] = "report_methodology"
691 _TIME: ClassVar[str] = None
694@export
695class ReportPower(Command):
696 _TCL_COMMAND: ClassVar[str] = "report_power"
697 _TIME: ClassVar[str] = None