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

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 

35 

36from pyTooling.Decorators import export, readonly 

37 

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 

56 

57 

58@export 

59class NotPresentException(ProcessorException): 

60 pass 

61 

62 

63@export 

64class SectionNotPresentException(NotPresentException): 

65 pass 

66 

67 

68@export 

69class HandlingCustomAttributes1(HandlingCustomAttributes): 

70 pass 

71 

72 

73@export 

74class HandlingCustomAttributes2(HandlingCustomAttributes): 

75 pass 

76 

77 

78@export 

79class ROM_RAM_DSP_SR_Retiming1(ROM_RAM_DSP_SR_Retiming): 

80 pass 

81 

82 

83@export 

84class ROM_RAM_DSP_SR_Retiming2(ROM_RAM_DSP_SR_Retiming): 

85 pass 

86 

87 

88@export 

89class ROM_RAM_DSP_SR_Retiming3(ROM_RAM_DSP_SR_Retiming): 

90 pass 

91 

92 

93@export 

94class Command(Parser): 

95 # _TCL_COMMAND: ClassVar[str] 

96 

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

100 

101 nextLine = yield line 

102 return nextLine 

103 

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 

109 

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 

116 

117 line = yield line 

118 

119 nextLine = yield line 

120 return nextLine 

121 

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

123 line = yield from self._CommandStart(line) 

124 

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 

135 

136 line = yield line 

137 

138 

139@export 

140class CommandWithSections(Command): 

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

142 

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

144 super().__init__(processor) 

145 

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

147 

148 @readonly 

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

150 return self._sections 

151 

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

153 return self._sections[key] 

154 

155 

156@export 

157class CommandWithTasks(Command): 

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

159 

160 def __init__(self, processor: "Processor"): 

161 super().__init__(processor) 

162 

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

164 

165 @readonly 

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

167 return self._tasks 

168 

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

170 return self._tasks[key] 

171 

172 

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 ) 

201 

202 @readonly 

203 def HasLatches(self) -> bool: 

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

205 return True 

206 

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

208 

209 @readonly 

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

211 try: 

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

213 except KeyError: 

214 yield from () 

215 

216 @readonly 

217 def HasBlackboxes(self) -> bool: 

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

219 

220 @readonly 

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

222 return self._sections[WritingSynthesisReport]._blackboxes 

223 

224 @readonly 

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

226 return self._sections[WritingSynthesisReport]._cells 

227 

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

233 

234 return [] 

235 

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

241 

242 return [] 

243 

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 

249 

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

253 

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

255 

256 rtlElaboration = self._sections[RTLElaboration] 

257 # constraintValidation = self._sections[ConstraintValidation] 

258 

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

264 

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 

289 

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 

303 

304 line = yield line 

305 

306 line = yield line 

307 

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 

314 

315 if isinstance(line, VivadoMessage): 

316 self._AddMessage(line) 

317 

318 line = yield section.send(line) 

319 

320 line = yield section.send(line) 

321 

322 activeParsers.remove(parser) 

323 

324 

325@export 

326class LinkDesign(Command): 

327 _TCL_COMMAND: ClassVar[str] = "link_design" 

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

329 

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

334 

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

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

337 

338 def __init__(self, processor: "Processor"): 

339 super().__init__(processor) 

340 

341 self._commonXDCFiles = {} 

342 self._perCellXDCFiles = {} 

343 

344 @readonly 

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

346 return self._commonXDCFiles 

347 

348 @readonly 

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

350 return self._perCellXDCFiles 

351 

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

353 line = yield from self._CommandStart(line) 

354 

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 

364 

365 path = Path(match[1]) 

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

367 

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 

384 

385 line = yield line 

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

387 line._kind = LineKind.Normal 

388 

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

395 

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 

412 

413 line = yield line 

414 

415 if line.StartsWith(end): 

416 nextLine = yield from self._CommandFinish(line) 

417 return nextLine 

418 

419 line = yield line 

420 

421 

422@export 

423class OptimizeDesign(CommandWithTasks): 

424 _TCL_COMMAND: ClassVar[str] = "opt_design" 

425 _TIME: ClassVar[str] = None 

426 

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

428 DRCTask, 

429 CacheTimingInformationTask, 

430 LogicOptimizationTask, 

431 PowerOptimizationTask, 

432 FinalCleanupTask, 

433 NetlistObfuscationTask 

434 ) 

435 

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

437 line = yield from self._CommandStart(line) 

438 

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

440 

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 

459 

460 # FIXME: use similar style like for _TIME 

461 line = yield line 

462 lastLine = yield line 

463 return lastLine 

464 # line._kind = LineKind.Unprocessed 

465 

466 line = yield line 

467 

468 while True: 

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

470 # line = yield task.send(line) 

471 # break 

472 

473 if isinstance(line, VivadoMessage): 

474 self._AddMessage(line) 

475 

476 try: 

477 line = yield task.send(line) 

478 except StopIteration as ex: 

479 task = None 

480 line = ex.value 

481 break 

482 

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) 

485 

486 activeParsers.remove(parser) 

487 

488 

489@export 

490class PlaceDesign(CommandWithTasks): 

491 _TCL_COMMAND: ClassVar[str] = "place_design" 

492 _TIME: ClassVar[str] = None 

493 

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

495 PlacerTask, 

496 ) 

497 

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

499 line = yield from self._CommandStart(line) 

500 

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

502 

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 

521 

522 # FIXME: use similar style like for _TIME 

523 line = yield line 

524 lastLine = yield line 

525 return lastLine 

526 # line._kind = LineKind.Unprocessed 

527 

528 line = yield line 

529 

530 while True: 

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

532 # line = yield task.send(line) 

533 # break 

534 

535 if isinstance(line, VivadoMessage): 

536 self._AddMessage(line) 

537 

538 try: 

539 line = yield task.send(line) 

540 except StopIteration as ex: 

541 task = None 

542 line = ex.value 

543 break 

544 

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) 

547 

548 activeParsers.remove(parser) 

549 

550 

551@export 

552class PhysicalOptimizeDesign(CommandWithTasks): 

553 _TCL_COMMAND: ClassVar[str] = "phys_opt_design" 

554 _TIME: ClassVar[str] = None 

555 

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

557 InitialUpdateTimingTask, 

558 PhysicalSynthesisTask 

559 ) 

560 

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

562 line = yield from self._CommandStart(line) 

563 

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

565 

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 

584 

585 # FIXME: use similar style like for _TIME 

586 line = yield line 

587 lastLine = yield line 

588 return lastLine 

589 # line._kind = LineKind.Unprocessed 

590 

591 line = yield line 

592 

593 while True: 

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

595 # line = yield task.send(line) 

596 # break 

597 

598 if isinstance(line, VivadoMessage): 

599 self._AddMessage(line) 

600 

601 try: 

602 line = yield task.send(line) 

603 except StopIteration as ex: 

604 task = None 

605 line = ex.value 

606 break 

607 

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) 

610 

611 activeParsers.remove(parser) 

612 

613 

614@export 

615class RouteDesign(CommandWithTasks): 

616 _TCL_COMMAND: ClassVar[str] = "route_design" 

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

618 

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

620 RoutingTask, 

621 ) 

622 

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

624 line = yield from self._CommandStart(line) 

625 

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

627 

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 

646 

647 # FIXME: use similar style like for _TIME 

648 line = yield line 

649 lastLine = yield line 

650 return lastLine 

651 # line._kind = LineKind.Unprocessed 

652 

653 line = yield line 

654 

655 while True: 

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

657 # line = yield task.send(line) 

658 # break 

659 

660 if isinstance(line, VivadoMessage): 

661 self._AddMessage(line) 

662 

663 try: 

664 line = yield task.send(line) 

665 except StopIteration as ex: 

666 task = None 

667 line = ex.value 

668 break 

669 

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) 

672 

673 activeParsers.remove(parser) 

674 

675 

676@export 

677class WriteBitstream(Command): 

678 _TCL_COMMAND: ClassVar[str] = "write_bitstream" 

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

680 

681 

682@export 

683class ReportDRC(Command): 

684 _TCL_COMMAND: ClassVar[str] = "report_drc" 

685 _TIME: ClassVar[str] = None 

686 

687 

688@export 

689class ReportMethodology(Command): 

690 _TCL_COMMAND: ClassVar[str] = "report_methodology" 

691 _TIME: ClassVar[str] = None 

692 

693 

694@export 

695class ReportPower(Command): 

696 _TCL_COMMAND: ClassVar[str] = "report_power" 

697 _TIME: ClassVar[str] = None