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

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 

35 

36from pyTooling.Decorators import export, readonly 

37from pyTooling.Versioning import YearReleaseVersion 

38from pyTooling.Warning import WarningCollector 

39 

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 

60 

61 

62@export 

63class NotPresentException(ProcessorException): 

64 pass 

65 

66 

67@export 

68class SectionNotPresentException(NotPresentException): 

69 pass 

70 

71 

72@export 

73class HandlingCustomAttributes1(HandlingCustomAttributes): 

74 pass 

75 

76 

77@export 

78class HandlingCustomAttributes2(HandlingCustomAttributes): 

79 pass 

80 

81 

82@export 

83class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming): 

84 pass 

85 

86 

87@export 

88class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming): 

89 pass 

90 

91 

92@export 

93class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming): 

94 pass 

95 

96 

97@export 

98class Command(Parser): 

99 # _TCL_COMMAND: ClassVar[str] 

100 

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() 

104 

105 nextLine = yield line 

106 return nextLine 

107 

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 

113 

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 

120 

121 line = yield line 

122 

123 nextLine = yield line 

124 return nextLine 

125 

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

127 line = yield from self._CommandStart(line) 

128 

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 

139 

140 line = yield line 

141 

142 def __str__(self) -> str: 

143 return f"{self._TCL_COMMAND}" 

144 

145 

146@export 

147class CommandWithSections(Command): 

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

149 

150 _PARSERS: ClassVar[Tuple[Type[Section], ...]] = dict() 

151 

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

153 super().__init__(processor) 

154 

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

156 

157 @readonly 

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

159 return self._sections 

160 

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

162 return self._sections[key] 

163 

164 

165@export 

166class CommandWithTasks(Command): 

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

168 

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

170 super().__init__(processor) 

171 

172 self._tasks = {t: t(self) for t in self._PARSERS} 

173 

174 @readonly 

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

176 return self._tasks 

177 

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

179 return self._tasks[key] 

180 

181 

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 ) 

211 

212 @readonly 

213 def HasLatches(self) -> bool: 

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

215 return True 

216 

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

218 

219 @readonly 

220 def Latches(self) -> Iterator[VivadoMessage]: 

221 try: 

222 yield from iter(self._messagesByID[8][327]) 

223 except KeyError: 

224 yield from () 

225 

226 @readonly 

227 def HasBlackboxes(self) -> bool: 

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

229 

230 @readonly 

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

232 return self._sections[WritingSynthesisReport]._blackboxes 

233 

234 @readonly 

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

236 return self._sections[WritingSynthesisReport]._cells 

237 

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]] 

243 

244 return [] 

245 

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]] 

251 

252 return [] 

253 

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 

259 

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() 

263 

264 activeParsers: List[Parser] = list(self._sections.values()) 

265 

266 rtlElaboration = self._sections[RTLElaboration] 

267 # constraintValidation = self._sections[ConstraintValidation] 

268 

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() 

274 

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 

303 

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 

317 

318 line = yield line 

319 

320 line = yield line 

321 

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 

328 

329 if isinstance(line, VivadoMessage): 

330 self._AddMessage(line) 

331 

332 line = yield section.send(line) 

333 

334 line = yield section.send(line) 

335 

336 activeParsers.remove(parser) 

337 

338 

339@export 

340class LinkDesign(Command): 

341 _TCL_COMMAND: ClassVar[str] = "link_design" 

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

343 

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 '(.*)'$""") 

348 

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

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

351 

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

353 super().__init__(processor) 

354 

355 self._commonXDCFiles = {} 

356 self._perCellXDCFiles = {} 

357 

358 @readonly 

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

360 return self._commonXDCFiles 

361 

362 @readonly 

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

364 return self._perCellXDCFiles 

365 

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

367 line = yield from self._CommandStart(line) 

368 

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 

378 

379 path = Path(match[1]) 

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

381 

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 

398 

399 line = yield line 

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

401 line._kind = LineKind.Normal 

402 

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 := [])} 

409 

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 

426 

427 line = yield line 

428 

429 if line.StartsWith(end): 

430 nextLine = yield from self._CommandFinish(line) 

431 return nextLine 

432 

433 line = yield line 

434 

435 

436@export 

437class OptimizeDesign(CommandWithTasks): 

438 _TCL_COMMAND: ClassVar[str] = "opt_design" 

439 _TIME: ClassVar[str] = None 

440 

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

442 DRCTask, 

443 CacheTimingInformationTask, 

444 LogicOptimizationTask, 

445 PowerOptimizationTask, 

446 FinalCleanupTask, 

447 NetlistObfuscationTask 

448 ) 

449 

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

451 line = yield from self._CommandStart(line) 

452 

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

454 

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 

477 

478 # FIXME: use similar style like for _TIME 

479 line = yield line 

480 lastLine = yield line 

481 return lastLine 

482 # line._kind = LineKind.Unprocessed 

483 

484 line = yield line 

485 

486 while True: 

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

488 # line = yield task.send(line) 

489 # break 

490 

491 if isinstance(line, VivadoMessage): 

492 self._AddMessage(line) 

493 

494 try: 

495 line = yield task.send(line) 

496 except StopIteration as ex: 

497 task = None 

498 line = ex.value 

499 

500 if isinstance(line, VivadoMessage): 

501 line = yield line 

502 

503 break 

504 

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) 

507 

508 activeParsers.remove(parser) 

509 

510 

511@export 

512class PlaceDesign(CommandWithTasks): 

513 _TCL_COMMAND: ClassVar[str] = "place_design" 

514 _TIME: ClassVar[str] = None 

515 

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

517 PlacerTask, 

518 ) 

519 

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

521 line = yield from self._CommandStart(line) 

522 

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

524 

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 

547 

548 # FIXME: use similar style like for _TIME 

549 line = yield line 

550 lastLine = yield line 

551 return lastLine 

552 # line._kind = LineKind.Unprocessed 

553 

554 line = yield line 

555 

556 while True: 

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

558 # line = yield task.send(line) 

559 # break 

560 

561 if isinstance(line, VivadoMessage): 

562 self._AddMessage(line) 

563 

564 try: 

565 line = yield task.send(line) 

566 except StopIteration as ex: 

567 task = None 

568 line = ex.value 

569 

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 

572 

573 break 

574 

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) 

577 

578 activeParsers.remove(parser) 

579 

580 

581@export 

582class PhysicalOptimizeDesign(CommandWithTasks): 

583 _TCL_COMMAND: ClassVar[str] = "phys_opt_design" 

584 _TIME: ClassVar[str] = None 

585 

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

587 InitialUpdateTimingTask, 

588 PhysicalSynthesisTask 

589 ) 

590 

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

592 line = yield from self._CommandStart(line) 

593 

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

595 

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 

618 

619 # FIXME: use similar style like for _TIME 

620 line = yield line 

621 lastLine = yield line 

622 return lastLine 

623 # line._kind = LineKind.Unprocessed 

624 

625 line = yield line 

626 

627 while True: 

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

629 # line = yield task.send(line) 

630 # break 

631 

632 if isinstance(line, VivadoMessage): 

633 self._AddMessage(line) 

634 

635 try: 

636 line = yield task.send(line) 

637 except StopIteration as ex: 

638 task = None 

639 line = ex.value 

640 

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 

643 

644 break 

645 

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) 

648 

649 activeParsers.remove(parser) 

650 

651 

652@export 

653class RouteDesign(CommandWithTasks): 

654 _TCL_COMMAND: ClassVar[str] = "route_design" 

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

656 

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

658 RoutingTask, 

659 ) 

660 

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

662 line = yield from self._CommandStart(line) 

663 

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

665 

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 

688 

689 # FIXME: use similar style like for _TIME 

690 line = yield line 

691 lastLine = yield line 

692 return lastLine 

693 # line._kind = LineKind.Unprocessed 

694 

695 line = yield line 

696 

697 while True: 

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

699 # line = yield task.send(line) 

700 # break 

701 

702 if isinstance(line, VivadoMessage): 

703 self._AddMessage(line) 

704 

705 try: 

706 line = yield task.send(line) 

707 except StopIteration as ex: 

708 task = None 

709 line = ex.value 

710 

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 

713 

714 break 

715 

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) 

718 

719 activeParsers.remove(parser) 

720 

721 

722@export 

723class WriteBitstream(Command): 

724 _TCL_COMMAND: ClassVar[str] = "write_bitstream" 

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

726 

727 

728@export 

729class ReportDRC(Command): 

730 _TCL_COMMAND: ClassVar[str] = "report_drc" 

731 _TIME: ClassVar[str] = None 

732 

733 

734@export 

735class ReportMethodology(Command): 

736 _TCL_COMMAND: ClassVar[str] = "report_methodology" 

737 _TIME: ClassVar[str] = None 

738 

739 

740@export 

741class ReportPower(Command): 

742 _TCL_COMMAND: ClassVar[str] = "report_power" 

743 _TIME: ClassVar[str] = None