Coverage for pyEDAA/OutputFilter/Xilinx/Commands.py: 82%

492 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-05 22:59 +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, Any, Tuple 

35 

36from pyTooling.Decorators import export, readonly 

37from pyTooling.Common import getFullyQualifiedName 

38from pyTooling.Warning import WarningCollector 

39 

40from pyEDAA.OutputFilter.Xilinx import VivadoTclCommand 

41from pyEDAA.OutputFilter.Xilinx.Exception import ProcessorException, NotPresentException 

42from pyEDAA.OutputFilter.Xilinx.Common import Line, LineKind, VivadoMessage, VHDLReportMessage 

43from pyEDAA.OutputFilter.Xilinx.Common2 import Parser, UnknownSection, UnknownTask, Task 

44from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import Section, RTLElaboration, HandlingCustomAttributes 

45from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ConstraintValidation, LoadingPart, ApplySetProperty 

46from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RTLComponentStatistics, RTLHierarchicalComponentStatistics 

47from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import PartResourceSummary, CrossBoundaryAndAreaOptimization 

48from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import ROM_RAM_DSP_SR_Retiming, ApplyingXDCTimingConstraints 

49from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import TimingOptimization, TechnologyMapping, IOInsertion 

50from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import FlatteningBeforeIOInsertion, FinalNetlistCleanup 

51from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedInstances, RebuildingUserHierarchy 

52from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import RenamingGeneratedPorts, RenamingGeneratedNets 

53from pyEDAA.OutputFilter.Xilinx.SynthesizeDesign import WritingSynthesisReport 

54from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import DRCTask, CacheTimingInformationTask, LogicOptimizationTask 

55from pyEDAA.OutputFilter.Xilinx.OptimizeDesign import PowerOptimizationTask, FinalCleanupTask, NetlistObfuscationTask 

56from pyEDAA.OutputFilter.Xilinx.PlaceDesign import PlacerTask 

57from pyEDAA.OutputFilter.Xilinx.PhysicalOptimizeDesign import PhysicalSynthesisTask, InitialUpdateTimingTask 

58from pyEDAA.OutputFilter.Xilinx.RouteDesign import RoutingTask 

59 

60 

61@export 

62class CommandNotPresentException(NotPresentException): 

63 pass 

64 

65 

66@export 

67class SectionNotPresentException(NotPresentException): 

68 pass 

69 

70 

71@export 

72class HandlingCustomAttributes1(HandlingCustomAttributes): 

73 pass 

74 

75 

76@export 

77class HandlingCustomAttributes2(HandlingCustomAttributes): 

78 pass 

79 

80# FIXME: remove duplications 

81@export 

82class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming): 

83 pass 

84 

85 

86@export 

87class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming): 

88 pass 

89 

90 

91@export 

92class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming): 

93 pass 

94 

95 

96@export 

97class Command(Parser): 

98 """ 

99 This parser parses outputs from Vivado TCL commands. 

100 

101 Depending on the command's output (and how it's implemented), they use different subcategories. 

102 

103 .. rubric:: Command subcategories 

104 

105 * :class:`CommandWithSections` 

106 * :class:`CommandWithtasks` 

107 

108 .. rubric:: Supported commands 

109 

110 * :class:`SynthesizeDesign` 

111 * :class:`LinkDesign` 

112 * :class:`OptimizeDesign` 

113 * :class:`PlaceDesign` 

114 * :class:`PhysicalOptimizeDesign` 

115 * :class:`RouteDesign` 

116 * :class:`WriteBitstream` 

117 * :class:`ReportDRC` 

118 * :class:`ReportMethodology` 

119 * :class:`ReportPower` 

120 

121 .. rubric:: Example 

122 

123 .. code-block:: 

124 

125 [...] 

126 Command: synth_design -top system_top -part xc7z015clg485-2 

127 Starting synth_design 

128 [...] 

129 """ 

130 

131 # _TCL_COMMAND: ClassVar[str] 

132 

133 def _CommandStart(self, line: Line) -> Generator[Line, Line, Line]: 

134 """ 

135 A generator accepting a line containing the expected Vivado TCL command. 

136 

137 When the generator exits, the returned line is the successor line to the line containing the Vivado TCL command. 

138 

139 :param line: The first line for the generator to process. 

140 :returns: A generator processing Vivado output log lines. 

141 """ 

142 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true

143 raise ProcessorException() # FIXME: add exception message 

144 

145 nextLine = yield line 

146 return nextLine 

147 

148 def _CommandFinish(self, line: Line) -> Generator[Line, Line, Line]: 

149 if line.StartsWith(f"{self._TCL_COMMAND} completed successfully"): 149 ↛ 152line 149 didn't jump to line 152 because the condition on line 149 was always true

150 line._kind |= LineKind.Success 

151 else: 

152 line._kind |= LineKind.Failed 

153 

154 line = yield line 

155 

156 if self._TIME is not None: # and self._processor._preamble._toolVersion > "2022.2": 

157 end = f"{self._TCL_COMMAND}: {self._TIME}" 

158 if line.StartsWith(end): 

159 line._kind = LineKind.TaskTime 

160 line = yield line 

161 

162 return line 

163 

164 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, None]: 

165 line = yield from self._CommandStart(line) 

166 

167 end = f"{self._TCL_COMMAND} " 

168 while True: 

169 if line._kind is LineKind.Empty: 

170 line = yield line 

171 continue 

172 elif isinstance(line, VivadoMessage): 

173 self._AddMessage(line) 

174 elif line.StartsWith(end): 

175 nextLine = yield from self._CommandFinish(line) 

176 return nextLine 

177 

178 line = yield line 

179 

180 def __str__(self) -> str: 

181 return f"{self._TCL_COMMAND}" 

182 

183 

184@export 

185class CommandWithSections(Command): 

186 """ 

187 A Vivado command writing sections into the output log. 

188 

189 .. rubric:: Example 

190 

191 .. code-block:: 

192 

193 [...] 

194 --------------------------------------------------------------------------------- 

195 Starting RTL Elaboration : Time (s): cpu = 00:00:03 ; elapsed = 00:00:03 . Memory (MB): peak = 847.230 ; gain = 176.500 

196 --------------------------------------------------------------------------------- 

197 INFO: [Synth 8-638] synthesizing module 'system_top' [C:/Users/tgomes/git/2019_1/src/system_top_PE1.vhd:257] 

198 [...] 

199 [...] 

200 [...] 

201 --------------------------------------------------------------------------------- 

202 Finished RTL Elaboration : Time (s): cpu = 00:00:04 ; elapsed = 00:00:04 . Memory (MB): peak = 917.641 ; gain = 246.910 

203 --------------------------------------------------------------------------------- 

204 [...] 

205 """ 

206 # _PARSERS: ClassVar[Tuple[Type[Section], ...]] 

207 

208 _sections: Dict[Type[Section], Section] 

209 

210 

211 def __init__(self, processor: "Processor") -> None: 

212 super().__init__(processor) 

213 

214 self._sections = {p: p(self) for p in self._PARSERS} 

215 

216 @readonly 

217 def Sections(self) -> Dict[Type[Section], Section]: 

218 """ 

219 Read-only property to access a dictionary of found sections within the TCL command's output. 

220 

221 :returns: A dictionary of found :class:`~pyEDAA.OutputFilter.Xilinx.SynthesizeDesign.Section`s. 

222 """ 

223 return self._sections 

224 

225 def __contains__(self, key: Any) -> bool: 

226 if not issubclass(key, Section): 226 ↛ 227line 226 didn't jump to line 227 because the condition on line 226 was never true

227 ex = TypeError(f"Parameter 'key' is not a Section.") 

228 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") 

229 raise ex 

230 

231 return key in self._sections 

232 

233 def __getitem__(self, key: Type[Section]) -> Section: 

234 try: 

235 return self._sections[key] 

236 except KeyError as ex: 

237 raise SectionNotPresentException(F"Section '{key._NAME}' not present in '{self._parent.logfile}'.") from ex 

238 

239 

240@export 

241class CommandWithTasks(Command): 

242 """ 

243 A Vivado command writing tasks into the output log. 

244 

245 .. rubric:: Example 

246 

247 .. code-block:: 

248 

249 [...] 

250 Starting Cache Timing Information Task 

251 INFO: [Timing 38-35] 79-Done setting XDC timing constraints. 

252 [...] 

253 [...] 

254 Ending Cache Timing Information Task | Checksum: 19fe8cb97 

255 [...] 

256 """ 

257 # _PARSERS: Tuple[Type[Task], ...] 

258 

259 _tasks: Dict[Type[Task], Task] 

260 

261 def __init__(self, processor: "Processor") -> None: 

262 super().__init__(processor) 

263 

264 self._tasks = {p: p(self) for p in self._PARSERS} 

265 

266 @readonly 

267 def Tasks(self) -> Dict[Type[Task], Task]: 

268 """ 

269 Read-only property to access a dictionary of found tasks within the TCL command's output. 

270 

271 :returns: A dictionary of found :class:`~pyEDAA.OutputFilter.Xilinx.Common2.Task`s. 

272 """ 

273 return self._tasks 

274 

275 def __contains__(self, key: Any) -> bool: 

276 if not issubclass(key, Task): 276 ↛ 277line 276 didn't jump to line 277 because the condition on line 276 was never true

277 ex = TypeError(f"Parameter 'key' is not a Task.") 

278 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") 

279 raise ex 

280 

281 return key in self._tasks 

282 

283 def __getitem__(self, key: Type[Task]) -> Task: 

284 try: 

285 return self._tasks[key] 

286 except KeyError as ex: 

287 raise SectionNotPresentException(F"Task '{key._NAME}' not present in '{self._parent.logfile}'.") from ex 

288 

289 

290@export 

291class SynthesizeDesign(CommandWithSections): 

292 """ 

293 A Vivado command output parser for ``synth_design``. 

294 """ 

295 _TCL_COMMAND: ClassVar[str] = "synth_design" 

296 _PARSERS: ClassVar[Tuple[Type[Section], ...]] = ( 

297 RTLElaboration, 

298 HandlingCustomAttributes1, 

299 ConstraintValidation, 

300 LoadingPart, 

301 ApplySetProperty, 

302 RTLComponentStatistics, 

303 RTLHierarchicalComponentStatistics, 

304 PartResourceSummary, 

305 CrossBoundaryAndAreaOptimization, 

306 ROM_RAM_DSP_SR_Retiming1, 

307 ApplyingXDCTimingConstraints, 

308 TimingOptimization, 

309 ROM_RAM_DSP_SR_Retiming2, 

310 TechnologyMapping, 

311 IOInsertion, 

312 FlatteningBeforeIOInsertion, 

313 FinalNetlistCleanup, 

314 RenamingGeneratedInstances, 

315 RebuildingUserHierarchy, 

316 RenamingGeneratedPorts, 

317 HandlingCustomAttributes2, 

318 RenamingGeneratedNets, 

319 ROM_RAM_DSP_SR_Retiming3, 

320 WritingSynthesisReport, 

321 ) 

322 

323 @readonly 

324 def HasLatches(self) -> bool: 

325 """ 

326 Read-only property returning if synthesis inferred latches into the design. 

327 

328 Latch detection is based on: 

329 

330 * Vivado message ``synth 8-327`` 

331 * Cells of lind ``LD`` listed in the *Cell Usage* report. 

332 

333 :returns: True, if the design contains latches. 

334 """ 

335 if (8 in self._messagesByID) and (327 in self._messagesByID[8]): 

336 return True 

337 

338 return "LD" in self._sections[WritingSynthesisReport]._cells 

339 

340 @readonly 

341 def Latches(self) -> List[VivadoMessage]: 

342 """ 

343 Read-only property to access a list of Vivado output messages for inferred latches. 

344 

345 :returns: A list of Vivado messages for interred latches. 

346 

347 .. note:: 

348 

349 This returns ``[Synth 8-327]`` messages. 

350 

351 .. code-block:: 

352 

353 WARNING: [Synth 8-327] inferring latch for variable 'Q_reg' 

354 """ 

355 if 8 in self._messagesByID: 

356 if 327 in (synthMessages := self._messagesByID[8]): 

357 return [message for message in synthMessages[327]] 

358 

359 return [] 

360 

361 @readonly 

362 def HasBlackboxes(self) -> bool: 

363 """ 

364 Read-only property returning if the design contains black-boxes. 

365 

366 :returns: True, if the design contains black-boxes. 

367 """ 

368 return len(self._sections[WritingSynthesisReport]._blackboxes) > 0 

369 

370 @readonly 

371 def Blackboxes(self) -> Dict[str, int]: 

372 """ 

373 Read-only property to access the dictionary of found blackbox statistics. 

374 

375 :returns: The dictionary of found blackbox statistics. 

376 """ 

377 return self._sections[WritingSynthesisReport]._blackboxes 

378 

379 @readonly 

380 def Cells(self) -> Dict[str, int]: 

381 """ 

382 Read-only property to access the dictionary of synthesized cell statistics. 

383 

384 :returns: The dictionary of used cell statistics. 

385 """ 

386 return self._sections[WritingSynthesisReport]._cells 

387 

388 @readonly 

389 def VHDLReportMessages(self) -> List[VHDLReportMessage]: 

390 """ 

391 Read-only property to access a list of Vivado output messages generated by VHDL report statement. 

392 

393 :returns: A list of VHDL report statement outputs. 

394 

395 .. note:: 

396 

397 This returns ``[Synth 8-6031]`` messages. 

398 

399 .. code-block:: 

400 

401 INFO: [Synth 8-6031] RTL report: "TimingToCycles(time, freq): period=10.000000 ns -- 0.000000 fs" [C:/[...]/StopWatch/src/Utilities.pkg.vhdl:118] 

402 """ 

403 if 8 in self._messagesByID: 

404 if 6031 in (synthMessages := self._messagesByID[8]): 

405 return [message for message in synthMessages[6031]] 

406 

407 return [] 

408 

409 @readonly 

410 def VHDLAssertMessages(self) -> List[VHDLReportMessage]: 

411 """ 

412 Read-only property to access a list of Vivado output messages generated by VHDL assert statement. 

413 

414 :returns: A list of VHDL assert statement outputs. 

415 

416 .. note:: 

417 

418 This returns ``[Synth 8-63]`` messages. 

419 

420 .. code-block:: 

421 

422 INFO: [Synth 8-63] RTL assertion: "CLOCK_FREQ: 100.000000 ns" [C:/[...]/StopWatch/src/Debouncer.vhdl:28] 

423 """ 

424 if 8 in self._messagesByID: 

425 if 63 in (synthMessages := self._messagesByID[8]): 

426 return [message for message in synthMessages[63]] 

427 

428 return [] 

429 

430 def SectionDetector(self, line: Line) -> Generator[Line, Line, None]: 

431 if not (isinstance(line, VivadoTclCommand) and line._command == self._TCL_COMMAND): 431 ↛ 432line 431 didn't jump to line 432 because the condition on line 431 was never true

432 raise ProcessorException() # FIXME: add exception message 

433 

434 activeParsers: List[Section] = list(self._sections.values()) 

435 

436 rtlElaboration = self._sections[RTLElaboration] 

437 # constraintValidation = self._sections[ConstraintValidation] 

438 

439 line = yield line 

440 if line == "Starting synth_design": 440 ↛ 443line 440 didn't jump to line 443 because the condition on line 440 was always true

441 line._kind = LineKind.Verbose 

442 else: 

443 raise ProcessorException() # FIXME: add exception message 

444 

445 line = yield line 

446 while True: 

447 while True: 

448 if line._kind is LineKind.Empty: 

449 line = yield line 

450 continue 

451 elif isinstance(line, VivadoMessage): 

452 self._AddMessage(line) 

453 elif line.StartsWith("Start "): 

454 for parser in activeParsers: # type: Section 454 ↛ 460line 454 didn't jump to line 460 because the loop on line 454 didn't complete

455 if line.StartsWith(parser._START): 

456 line = next(section := parser.Generator(line)) 

457 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter 

458 break 

459 else: 

460 WarningCollector.Raise(UnknownSection(f"Unknown section: '{line!r}'", line)) 

461 ex = Exception(f"How to recover from here? Unknown section: '{line!r}'") 

462 # ex.add_note(f"Current task: start pattern='{self._task}'") 

463 ex.add_note(f"Current cmd: {self}") 

464 raise ex 

465 break 

466 elif line.StartsWith("Starting "): 

467 if line.StartsWith(rtlElaboration._START): 467 ↛ 487line 467 didn't jump to line 487 because the condition on line 467 was always true

468 parser = rtlElaboration 

469 line = next(section := parser.Generator(line)) 

470 line._previousLine._kind = LineKind.SectionStart | LineKind.SectionDelimiter 

471 break 

472 elif line.StartsWith(self._TCL_COMMAND): 

473 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 473 ↛ 487line 473 didn't jump to line 487 because the condition on line 473 was always true

474 line._kind |= LineKind.Success 

475 

476 # FIXME: use similar style like for _TIME 

477 line = yield line 

478 lastLine = yield line 

479 return lastLine 

480 elif line.StartsWith("Finished RTL Optimization Phase"): 

481 line._kind = LineKind.PhaseEnd 

482 line._previousLine._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter 

483 elif line.StartsWith("----"): 

484 if LineKind.Phase in line._previousLine._kind: 

485 line._kind = LineKind.PhaseEnd | LineKind.PhaseDelimiter 

486 

487 line = yield line 

488 

489 line = yield line 

490 

491 while True: 

492 if line.StartsWith("Finished"): 

493 l = line[9:] 

494 if not (l.startswith("Flattening") or l.startswith("Final")): 

495 line = yield section.send(line) 

496 break 

497 

498 if isinstance(line, VivadoMessage): 

499 self._AddMessage(line) 

500 

501 line = yield section.send(line) 

502 

503 line = yield section.send(line) 

504 

505 activeParsers.remove(parser) 

506 

507 

508@export 

509class LinkDesign(Command): 

510 """ 

511 A Vivado command output parser for ``link_design``. 

512 """ 

513 _TCL_COMMAND: ClassVar[str] = "link_design" 

514 _TIME: ClassVar[str] = "Time (s):" 

515 

516 _ParsingXDCFile_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\]$""") 

517 _FinishedParsingXDCFile_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\]$""") 

518 _ParsingXDCFileForCell_Pattern = re_compile(r"""^Parsing XDC File \[(.*)\] for cell '(.*)'$""") 

519 _FinishedParsingXDCFileForCell_Pattern = re_compile(r"""^Finished Parsing XDC File \[(.*)\] for cell '(.*)'$""") 

520 

521 _commonXDCFiles: Dict[Path, List[VivadoMessage]] 

522 _perCellXDCFiles: Dict[Path, Dict[str, List[VivadoMessage]]] 

523 

524 def __init__(self, processor: "Processor") -> None: 

525 super().__init__(processor) 

526 

527 self._commonXDCFiles = {} 

528 self._perCellXDCFiles = {} 

529 

530 @readonly 

531 def CommonXDCFiles(self) -> Dict[Path, List[VivadoMessage]]: 

532 return self._commonXDCFiles 

533 

534 @readonly 

535 def PerCellXDCFiles(self) -> Dict[Path, Dict[str, List[VivadoMessage]]]: 

536 return self._perCellXDCFiles 

537 

538 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: 

539 line = yield from self._CommandStart(line) 

540 

541 end = f"{self._TCL_COMMAND} " 

542 while True: 

543 if line._kind is LineKind.Empty: 

544 line = yield line 

545 continue 

546 elif isinstance(line, VivadoMessage): 

547 self._AddMessage(line) 

548 elif (match := self._ParsingXDCFile_Pattern.match(line._message)) is not None: 

549 line._kind = LineKind.Normal 

550 

551 path = Path(match[1]) 

552 self._commonXDCFiles[path] = (messages := []) 

553 

554 line = yield line 

555 while True: 

556 if line._kind is LineKind.Empty: 556 ↛ 557line 556 didn't jump to line 557 because the condition on line 556 was never true

557 line = yield line 

558 continue 

559 elif isinstance(line, VivadoMessage): 

560 self._AddMessage(line) 

561 messages.append(line) 

562 elif (match := self._FinishedParsingXDCFile_Pattern.match(line._message)) is not None and path == Path(match[1]): 

563 line._kind = LineKind.Normal 

564 break 

565 elif line.StartsWith("Finished Parsing XDC File"): 565 ↛ 566line 565 didn't jump to line 566 because the condition on line 565 was never true

566 line._kind = LineKind.ProcessorError 

567 break 

568 elif line.StartsWith(end): 568 ↛ 569line 568 didn't jump to line 569 because the condition on line 568 was never true

569 break 

570 

571 line = yield line 

572 elif (match := self._ParsingXDCFileForCell_Pattern.match(line._message)) is not None: 

573 line._kind = LineKind.Normal 

574 

575 path = Path(match[1]) 

576 cell = match[2] 

577 if path in self._perCellXDCFiles: 

578 self._perCellXDCFiles[path][cell] = (messages := []) 

579 else: 

580 self._perCellXDCFiles[path] = {cell: (messages := [])} 

581 

582 line = yield line 

583 while True: 

584 if line._kind is LineKind.Empty: 584 ↛ 585line 584 didn't jump to line 585 because the condition on line 584 was never true

585 line = yield line 

586 continue 

587 elif isinstance(line, VivadoMessage): 

588 self._AddMessage(line) 

589 messages.append(line) 

590 elif (match := self._FinishedParsingXDCFileForCell_Pattern.match(line._message)) is not None and path == Path(match[1]) and cell == match[2]: 

591 line._kind = LineKind.Normal 

592 break 

593 elif line.StartsWith("Finished Parsing XDC File"): 593 ↛ 594line 593 didn't jump to line 594 because the condition on line 593 was never true

594 line._kind = LineKind.ProcessorError 

595 break 

596 elif line.StartsWith(end): 596 ↛ 597line 596 didn't jump to line 597 because the condition on line 596 was never true

597 break 

598 

599 line = yield line 

600 

601 if line.StartsWith(end): 

602 nextLine = yield from self._CommandFinish(line) 

603 return nextLine 

604 

605 line = yield line 

606 

607 

608@export 

609class OptimizeDesign(CommandWithTasks): 

610 """ 

611 A Vivado command output parser for ``opt_design``. 

612 """ 

613 _TCL_COMMAND: ClassVar[str] = "opt_design" 

614 _TIME: ClassVar[str] = None 

615 

616 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( 

617 DRCTask, 

618 CacheTimingInformationTask, 

619 LogicOptimizationTask, 

620 PowerOptimizationTask, 

621 FinalCleanupTask, 

622 NetlistObfuscationTask 

623 ) 

624 

625 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: 

626 line = yield from self._CommandStart(line) 

627 

628 activeParsers: List[Task] = list(self._tasks.values()) 

629 

630 while True: 

631 while True: 

632 if line._kind is LineKind.Empty: 

633 line = yield line 

634 continue 

635 elif isinstance(line, VivadoMessage): 

636 self._AddMessage(line) 

637 elif line.StartsWith("Starting ") and not line.StartsWith("Starting Connectivity Check Task"): 

638 for parser in activeParsers: # type: Section 638 ↛ 643line 638 didn't jump to line 643 because the loop on line 638 didn't complete

639 if line.StartsWith(parser._START): 

640 line = yield next(task := parser.Generator(line)) 

641 break 

642 else: 

643 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) 

644 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") 

645 # ex.add_note(f"Current task: start pattern='{self._task}'") 

646 ex.add_note(f"Current cmd: {self}") 

647 raise ex 

648 break 

649 elif line.StartsWith(self._TCL_COMMAND): 

650 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 650 ↛ 659line 650 didn't jump to line 659 because the condition on line 650 was always true

651 line._kind |= LineKind.Success 

652 

653 # FIXME: use similar style like for _TIME 

654 line = yield line 

655 lastLine = yield line 

656 return lastLine 

657 # line._kind = LineKind.Unprocessed 

658 

659 line = yield line 

660 

661 while True: 

662 # if line.StartsWith("Ending"): 

663 # line = yield task.send(line) 

664 # break 

665 

666 if isinstance(line, VivadoMessage): 

667 self._AddMessage(line) 

668 

669 try: 

670 line = yield task.send(line) 

671 except StopIteration as ex: 

672 task = None 

673 line = ex.value 

674 

675 if isinstance(line, VivadoMessage): 

676 line = yield line 

677 

678 break 

679 

680 if task is not None: 680 ↛ 681line 680 didn't jump to line 681 because the condition on line 680 was never true

681 line = yield task.send(line) 

682 

683 activeParsers.remove(parser) 

684 

685 

686@export 

687class PlaceDesign(CommandWithTasks): 

688 """ 

689 A Vivado command output parser for ``place_design``. 

690 """ 

691 _TCL_COMMAND: ClassVar[str] = "place_design" 

692 _TIME: ClassVar[str] = None 

693 

694 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( 

695 PlacerTask, 

696 ) 

697 

698 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: 

699 line = yield from self._CommandStart(line) 

700 

701 activeParsers: List[Task] = list(self._tasks.values()) 

702 

703 while True: 

704 while True: 

705 if line._kind is LineKind.Empty: 

706 line = yield line 

707 continue 

708 elif isinstance(line, VivadoMessage): 

709 self._AddMessage(line) 

710 elif line.StartsWith("Starting "): 

711 for parser in activeParsers: # type: Section 711 ↛ 716line 711 didn't jump to line 716 because the loop on line 711 didn't complete

712 if line.StartsWith(parser._START): 712 ↛ 711line 712 didn't jump to line 711 because the condition on line 712 was always true

713 line = yield next(task := parser.Generator(line)) 

714 break 

715 else: 

716 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) 

717 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") 

718 # ex.add_note(f"Current task: start pattern='{self._task}'") 

719 ex.add_note(f"Current cmd: {self}") 

720 raise ex 

721 break 

722 elif line.StartsWith(self._TCL_COMMAND): 

723 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 723 ↛ 732line 723 didn't jump to line 732 because the condition on line 723 was always true

724 line._kind |= LineKind.Success 

725 

726 # FIXME: use similar style like for _TIME 

727 line = yield line 

728 lastLine = yield line 

729 return lastLine 

730 # line._kind = LineKind.Unprocessed 

731 

732 line = yield line 

733 

734 while True: 

735 # if line.StartsWith("Ending"): 

736 # line = yield task.send(line) 

737 # break 

738 

739 if isinstance(line, VivadoMessage): 

740 self._AddMessage(line) 

741 

742 try: 

743 line = yield task.send(line) 

744 except StopIteration as ex: 

745 task = None 

746 line = ex.value 

747 

748 if isinstance(line, VivadoMessage): 

749 line = yield line 

750 

751 break 

752 

753 if task is not None: 753 ↛ 754line 753 didn't jump to line 754 because the condition on line 753 was never true

754 line = yield task.send(line) 

755 

756 activeParsers.remove(parser) 

757 

758 

759@export 

760class PhysicalOptimizeDesign(CommandWithTasks): 

761 """ 

762 A Vivado command output parser for ``phy_opt_design``. 

763 """ 

764 _TCL_COMMAND: ClassVar[str] = "phys_opt_design" 

765 _TIME: ClassVar[str] = None 

766 

767 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( 

768 InitialUpdateTimingTask, 

769 PhysicalSynthesisTask 

770 ) 

771 

772 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: 

773 line = yield from self._CommandStart(line) 

774 

775 activeParsers: List[Task] = list(self._tasks.values()) 

776 

777 while True: 

778 while True: 

779 if line._kind is LineKind.Empty: 

780 line = yield line 

781 continue 

782 elif isinstance(line, VivadoMessage): 

783 self._AddMessage(line) 

784 elif line.StartsWith("Starting "): 

785 for parser in activeParsers: # type: Section 785 ↛ 790line 785 didn't jump to line 790 because the loop on line 785 didn't complete

786 if line.StartsWith(parser._START): 786 ↛ 785line 786 didn't jump to line 785 because the condition on line 786 was always true

787 line = yield next(task := parser.Generator(line)) 

788 break 

789 else: 

790 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) 

791 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") 

792 # ex.add_note(f"Current task: start pattern='{self._task}'") 

793 ex.add_note(f"Current cmd: {self}") 

794 raise ex 

795 break 

796 elif line.StartsWith(self._TCL_COMMAND): 

797 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 797 ↛ 806line 797 didn't jump to line 806 because the condition on line 797 was always true

798 line._kind |= LineKind.Success 

799 

800 # FIXME: use similar style like for _TIME 

801 line = yield line 

802 lastLine = yield line 

803 return lastLine 

804 # line._kind = LineKind.Unprocessed 

805 

806 line = yield line 

807 

808 while True: 

809 # if line.StartsWith("Ending"): 

810 # line = yield task.send(line) 

811 # break 

812 

813 if isinstance(line, VivadoMessage): 

814 self._AddMessage(line) 

815 

816 try: 

817 line = yield task.send(line) 

818 except StopIteration as ex: 

819 task = None 

820 line = ex.value 

821 

822 if isinstance(line, VivadoMessage): 822 ↛ 825line 822 didn't jump to line 825 because the condition on line 822 was always true

823 line = yield line 

824 

825 break 

826 

827 if task is not None: 827 ↛ 828line 827 didn't jump to line 828 because the condition on line 827 was never true

828 line = yield task.send(line) 

829 

830 activeParsers.remove(parser) 

831 

832 

833@export 

834class RouteDesign(CommandWithTasks): 

835 """ 

836 A Vivado command output parser for ``route_design``. 

837 """ 

838 _TCL_COMMAND: ClassVar[str] = "route_design" 

839 _TIME: ClassVar[str] = "Time (s):" 

840 

841 _PARSERS: ClassVar[Tuple[Type[Task], ...]] = ( 

842 RoutingTask, 

843 ) 

844 

845 def SectionDetector(self, line: Line) -> Generator[Union[Line, ProcessorException], Line, Line]: 

846 line = yield from self._CommandStart(line) 

847 

848 activeParsers: List[Task] = list(self._tasks.values()) 

849 

850 while True: 

851 while True: 

852 if line._kind is LineKind.Empty: 

853 line = yield line 

854 continue 

855 elif isinstance(line, VivadoMessage): 

856 self._AddMessage(line) 

857 elif line.StartsWith("Starting "): 

858 for parser in activeParsers: # type: Section 858 ↛ 863line 858 didn't jump to line 863 because the loop on line 858 didn't complete

859 if line.StartsWith(parser._START): 859 ↛ 858line 859 didn't jump to line 858 because the condition on line 859 was always true

860 line = yield next(task := parser.Generator(line)) 

861 break 

862 else: 

863 WarningCollector.Raise(UnknownTask(f"Unknown task: '{line!r}'", line)) 

864 ex = Exception(f"How to recover from here? Unknown task: '{line!r}'") 

865 # ex.add_note(f"Current task: start pattern='{self._task}'") 

866 ex.add_note(f"Current cmd: {self}") 

867 raise ex 

868 break 

869 elif line.StartsWith(self._TCL_COMMAND): 

870 if line[len(self._TCL_COMMAND) + 1:].startswith("completed successfully"): 870 ↛ 879line 870 didn't jump to line 879 because the condition on line 870 was always true

871 line._kind |= LineKind.Success 

872 

873 # FIXME: use similar style like for _TIME 

874 line = yield line 

875 lastLine = yield line 

876 return lastLine 

877 # line._kind = LineKind.Unprocessed 

878 

879 line = yield line 

880 

881 while True: 

882 # if line.StartsWith("Ending"): 

883 # line = yield task.send(line) 

884 # break 

885 

886 if isinstance(line, VivadoMessage): 

887 self._AddMessage(line) 

888 

889 try: 

890 line = yield task.send(line) 

891 except StopIteration as ex: 

892 task = None 

893 line = ex.value 

894 

895 if isinstance(line, VivadoMessage): 895 ↛ 896line 895 didn't jump to line 896 because the condition on line 895 was never true

896 line = yield line 

897 

898 break 

899 

900 if task is not None: 900 ↛ 901line 900 didn't jump to line 901 because the condition on line 900 was never true

901 line = yield task.send(line) 

902 

903 activeParsers.remove(parser) 

904 

905 

906@export 

907class WriteBitstream(Command): 

908 """ 

909 A Vivado command output parser for ``write_bitstream``. 

910 """ 

911 _TCL_COMMAND: ClassVar[str] = "write_bitstream" 

912 _TIME: ClassVar[str] = "Time (s):" 

913 

914 

915@export 

916class ReportDRC(Command): 

917 """ 

918 A Vivado command output parser for ``report_drc``. 

919 """ 

920 _TCL_COMMAND: ClassVar[str] = "report_drc" 

921 _TIME: ClassVar[str] = "Time (s):" 

922 

923 

924@export 

925class ReportMethodology(Command): 

926 """ 

927 A Vivado command output parser for ``report_methodology``. 

928 """ 

929 _TCL_COMMAND: ClassVar[str] = "report_methodology" 

930 _TIME: ClassVar[str] = None 

931 

932 

933@export 

934class ReportPower(Command): 

935 """ 

936 A Vivado command output parser for ``report_power``. 

937 """ 

938 _TCL_COMMAND: ClassVar[str] = "report_power" 

939 _TIME: ClassVar[str] = None