Coverage for pyEDAA/OutputFilter/Xilinx/RouteDesign.py: 89%

330 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"""A filtering anc classification processor for AMD/Xilinx Vivado Synthesis outputs.""" 

32from typing import Generator, ClassVar, List, Type, Dict, Tuple 

33 

34from pyTooling.Decorators import export 

35 

36from pyEDAA.OutputFilter.Xilinx import Line, VivadoMessage, LineKind 

37from pyEDAA.OutputFilter.Xilinx.Common2 import Task, Phase, SubPhase 

38 

39 

40@export 

41class Phase1_BuildRTDesign(Phase): 

42 _START: ClassVar[str] = "Phase 1 Build RT Design" 

43 _FINISH: ClassVar[str] = "Phase 1 Build RT Design | Checksum:" 

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

45 

46 

47@export 

48class Phase21_FixTopologyConstraints(SubPhase): 

49 _START: ClassVar[str] = "Phase 2.1 Fix Topology Constraints" 

50 _FINISH: ClassVar[str] = "Phase 2.1 Fix Topology Constraints | Checksum:" 

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

52 

53 

54@export 

55class Phase22_PreRouteCleanup(SubPhase): 

56 _START: ClassVar[str] = "Phase 2.2 Pre Route Cleanup" 

57 _FINISH: ClassVar[str] = "Phase 2.2 Pre Route Cleanup | Checksum:" 

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

59 

60 

61@export 

62class Phase23_UpdateTiming(SubPhase): 

63 _START: ClassVar[str] = "Phase 2.3 Update Timing" 

64 _FINISH: ClassVar[str] = "Phase 2.3 Update Timing | Checksum:" 

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

66 

67 

68@export 

69class Phase24_SoftConstraintPins_FastBudgeting(SubPhase): 

70 _START: ClassVar[str] = "Phase 2.4 Soft Constraint Pins - Fast Budgeting" 

71 _FINISH: ClassVar[str] = "Phase 2.4 Soft Constraint Pins - Fast Budgeting | Checksum:" 

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

73 

74 

75@export 

76class Phase2_RouterInitialization(Phase): 

77 _START: ClassVar[str] = "Phase 2 Router Initialization" 

78 _FINISH: ClassVar[str] = "Phase 2 Router Initialization | Checksum:" 

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

80 

81 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

82 Phase21_FixTopologyConstraints, 

83 Phase22_PreRouteCleanup, 

84 Phase23_UpdateTiming, 

85 Phase24_SoftConstraintPins_FastBudgeting 

86 ) 

87 

88 _subphases: Dict[Type[SubPhase], SubPhase] 

89 

90 def __init__(self, phase: Phase): 

91 super().__init__(phase) 

92 

93 self._subphases = {p: p(self) for p in self._PARSERS} 

94 

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

96 line = yield from self._PhaseStart(line) 

97 

98 activeParsers: List[Phase] = list(self._subphases.values()) 

99 

100 while True: 

101 while True: 

102 if line._kind is LineKind.Empty: 

103 line = yield line 

104 continue 

105 elif isinstance(line, VivadoMessage): 

106 self._AddMessage(line) 

107 elif line.StartsWith("Phase 2."): 

108 for parser in activeParsers: # type: SubPhase 108 ↛ 113line 108 didn't jump to line 113 because the loop on line 108 didn't complete

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

110 line = yield next(phase := parser.Generator(line)) 

111 break 

112 else: 

113 raise Exception(f"Unknown subphase: {line!r}") 

114 break 

115 elif line.StartsWith(self._FINISH): 

116 nextLine = yield from self._PhaseFinish(line) 

117 return nextLine 

118 

119 line = yield line 

120 

121 while phase is not None: 121 ↛ 100line 121 didn't jump to line 100 because the condition on line 121 was always true

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

123 # line = yield task.send(line) 

124 # break 

125 

126 if isinstance(line, VivadoMessage): 

127 self._AddMessage(line) 

128 

129 try: 

130 line = yield phase.send(line) 

131 except StopIteration as ex: 

132 activeParsers.remove(parser) 

133 line = ex.value 

134 break 

135 

136 

137@export 

138class Phase3_GlobalRouting(Phase): 

139 _START: ClassVar[str] = "Phase 3 Global Routing" 

140 _FINISH: ClassVar[str] = "Phase 3 Global Routing | Checksum:" 

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

142 

143 

144@export 

145class Phase41_InitialNetRoutingPass(SubPhase): 

146 _START: ClassVar[str] = "Phase 4.1 Initial Net Routing Pass" 

147 _FINISH: ClassVar[str] = "Phase 4.1 Initial Net Routing Pass | Checksum:" 

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

149 

150 

151@export 

152class Phase4_InitialRouting(Phase): 

153 _START: ClassVar[str] = "Phase 4 Initial Routing" 

154 _FINISH: ClassVar[str] = "Phase 4 Initial Routing | Checksum:" 

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

156 

157 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

158 Phase41_InitialNetRoutingPass, 

159 ) 

160 

161 _subphases: Dict[Type[SubPhase], SubPhase] 

162 

163 def __init__(self, phase: Phase): 

164 super().__init__(phase) 

165 

166 self._subphases = {p: p(self) for p in self._PARSERS} 

167 

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

169 line = yield from self._PhaseStart(line) 

170 

171 activeParsers: List[Phase] = list(self._subphases.values()) 

172 

173 while True: 

174 while True: 

175 if line._kind is LineKind.Empty: 

176 line = yield line 

177 continue 

178 elif isinstance(line, VivadoMessage): 178 ↛ 179line 178 didn't jump to line 179 because the condition on line 178 was never true

179 self._AddMessage(line) 

180 elif line.StartsWith("Phase 4."): 

181 for parser in activeParsers: # type: SubPhase 181 ↛ 186line 181 didn't jump to line 186 because the loop on line 181 didn't complete

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

183 line = yield next(phase := parser.Generator(line)) 

184 break 

185 else: 

186 raise Exception(f"Unknown subphase: {line!r}") 

187 break 

188 elif line.StartsWith(self._FINISH): 188 ↛ 192line 188 didn't jump to line 192 because the condition on line 188 was always true

189 nextLine = yield from self._PhaseFinish(line) 

190 return nextLine 

191 

192 line = yield line 

193 

194 while phase is not None: 194 ↛ 173line 194 didn't jump to line 173 because the condition on line 194 was always true

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

196 # line = yield task.send(line) 

197 # break 

198 

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

200 self._AddMessage(line) 

201 

202 try: 

203 line = yield phase.send(line) 

204 except StopIteration as ex: 

205 activeParsers.remove(parser) 

206 line = ex.value 

207 break 

208 

209 

210@export 

211class Phase51_GlobalIteration0(SubPhase): 

212 _START: ClassVar[str] = "Phase 5.1 Global Iteration 0" 

213 _FINISH: ClassVar[str] = "Phase 5.1 Global Iteration 0 | Checksum:" 

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

215 

216 

217@export 

218class Phase52_GlobalIteration1(SubPhase): 

219 _START: ClassVar[str] = "Phase 5.2 Global Iteration 1" 

220 _FINISH: ClassVar[str] = "Phase 5.2 Global Iteration 1 | Checksum:" 

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

222 

223 

224@export 

225class Phase5_RipUpAndReroute(Phase): 

226 _START: ClassVar[str] = "Phase 5 Rip-up And Reroute" 

227 _FINISH: ClassVar[str] = "Phase 5 Rip-up And Reroute | Checksum:" 

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

229 

230 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

231 Phase51_GlobalIteration0, 

232 Phase52_GlobalIteration1 

233 ) 

234 

235 _subphases: Dict[Type[SubPhase], SubPhase] 

236 

237 def __init__(self, phase: Phase): 

238 super().__init__(phase) 

239 

240 self._subphases = {p: p(self) for p in self._PARSERS} 

241 

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

243 line = yield from self._PhaseStart(line) 

244 

245 activeParsers: List[Phase] = list(self._subphases.values()) 

246 

247 while True: 

248 while True: 

249 if line._kind is LineKind.Empty: 

250 line = yield line 

251 continue 

252 elif isinstance(line, VivadoMessage): 252 ↛ 253line 252 didn't jump to line 253 because the condition on line 252 was never true

253 self._AddMessage(line) 

254 elif line.StartsWith("Phase 5."): 

255 for parser in activeParsers: # type: SubPhase 255 ↛ 260line 255 didn't jump to line 260 because the loop on line 255 didn't complete

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

257 line = yield next(phase := parser.Generator(line)) 

258 break 

259 else: 

260 raise Exception(f"Unknown subphase: {line!r}") 

261 break 

262 elif line.StartsWith(self._FINISH): 262 ↛ 266line 262 didn't jump to line 266 because the condition on line 262 was always true

263 nextLine = yield from self._PhaseFinish(line) 

264 return nextLine 

265 

266 line = yield line 

267 

268 while phase is not None: 268 ↛ 247line 268 didn't jump to line 247 because the condition on line 268 was always true

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

270 # line = yield task.send(line) 

271 # break 

272 

273 if isinstance(line, VivadoMessage): 

274 self._AddMessage(line) 

275 

276 try: 

277 line = yield phase.send(line) 

278 except StopIteration as ex: 

279 activeParsers.remove(parser) 

280 line = ex.value 

281 break 

282 

283 

284@export 

285class Phase61_DelayCleanUp(SubPhase): 

286 _START: ClassVar[str] = "Phase 6.1 Delay CleanUp" 

287 _FINISH: ClassVar[str] = "Phase 6.1 Delay CleanUp | Checksum:" 

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

289 

290 

291@export 

292class Phase62_ClockSkewOptimization(SubPhase): 

293 _START: ClassVar[str] = "Phase 6.2 Clock Skew Optimization" 

294 _FINISH: ClassVar[str] = "Phase 6.2 Clock Skew Optimization | Checksum:" 

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

296 

297 

298@export 

299class Phase6_DelayAndSkewOptimization(Phase): 

300 _START: ClassVar[str] = "Phase 6 Delay and Skew Optimization" 

301 _FINISH: ClassVar[str] = "Phase 6 Delay and Skew Optimization | Checksum:" 

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

303 

304 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

305 Phase61_DelayCleanUp, 

306 Phase62_ClockSkewOptimization 

307 ) 

308 

309 _subphases: Dict[Type[SubPhase], SubPhase] 

310 

311 def __init__(self, phase: Phase): 

312 super().__init__(phase) 

313 

314 self._subphases = {p: p(self) for p in self._PARSERS} 

315 

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

317 line = yield from self._PhaseStart(line) 

318 

319 activeParsers: List[Phase] = list(self._subphases.values()) 

320 

321 while True: 

322 while True: 

323 if line._kind is LineKind.Empty: 

324 line = yield line 

325 continue 

326 elif isinstance(line, VivadoMessage): 326 ↛ 327line 326 didn't jump to line 327 because the condition on line 326 was never true

327 self._AddMessage(line) 

328 elif line.StartsWith("Phase 6."): 

329 for parser in activeParsers: # type: SubPhase 329 ↛ 334line 329 didn't jump to line 334 because the loop on line 329 didn't complete

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

331 line = yield next(phase := parser.Generator(line)) 

332 break 

333 else: 

334 raise Exception(f"Unknown subphase: {line!r}") 

335 break 

336 elif line.StartsWith(self._FINISH): 336 ↛ 340line 336 didn't jump to line 340 because the condition on line 336 was always true

337 nextLine = yield from self._PhaseFinish(line) 

338 return nextLine 

339 

340 line = yield line 

341 

342 while phase is not None: 342 ↛ 321line 342 didn't jump to line 321 because the condition on line 342 was always true

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

344 # line = yield task.send(line) 

345 # break 

346 

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

348 self._AddMessage(line) 

349 

350 try: 

351 line = yield phase.send(line) 

352 except StopIteration as ex: 

353 activeParsers.remove(parser) 

354 line = ex.value 

355 break 

356 

357 

358@export 

359class Phase71_HoldFixIter(SubPhase): 

360 _START: ClassVar[str] = "Phase 7.1 Hold Fix Iter" 

361 _FINISH: ClassVar[str] = "Phase 7.1 Hold Fix Iter | Checksum:" 

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

363 

364 

365@export 

366class Phase7_PostHoldFix(Phase): 

367 _START: ClassVar[str] = "Phase 7 Post Hold Fix" 

368 _FINISH: ClassVar[str] = "Phase 7 Post Hold Fix | Checksum:" 

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

370 

371 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

372 Phase71_HoldFixIter, 

373 ) 

374 

375 _subphases: Dict[Type[SubPhase], SubPhase] 

376 

377 def __init__(self, phase: Phase): 

378 super().__init__(phase) 

379 

380 self._subphases = {p: p(self) for p in self._PARSERS} 

381 

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

383 line = yield from self._PhaseStart(line) 

384 

385 activeParsers: List[Phase] = list(self._subphases.values()) 

386 

387 while True: 

388 while True: 

389 if line._kind is LineKind.Empty: 

390 line = yield line 

391 continue 

392 elif isinstance(line, VivadoMessage): 392 ↛ 393line 392 didn't jump to line 393 because the condition on line 392 was never true

393 self._AddMessage(line) 

394 elif line.StartsWith("Phase 7."): 

395 for parser in activeParsers: # type: SubPhase 395 ↛ 400line 395 didn't jump to line 400 because the loop on line 395 didn't complete

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

397 line = yield next(phase := parser.Generator(line)) 

398 break 

399 else: 

400 raise Exception(f"Unknown subphase: {line!r}") 

401 break 

402 elif line.StartsWith(self._FINISH): 402 ↛ 406line 402 didn't jump to line 406 because the condition on line 402 was always true

403 nextLine = yield from self._PhaseFinish(line) 

404 return nextLine 

405 

406 line = yield line 

407 

408 while phase is not None: 408 ↛ 387line 408 didn't jump to line 387 because the condition on line 408 was always true

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

410 # line = yield task.send(line) 

411 # break 

412 

413 if isinstance(line, VivadoMessage): 

414 self._AddMessage(line) 

415 

416 try: 

417 line = yield phase.send(line) 

418 except StopIteration as ex: 

419 activeParsers.remove(parser) 

420 line = ex.value 

421 break 

422 

423 

424@export 

425class Phase8_RouteFinalize(Phase): 

426 _START: ClassVar[str] = "Phase 8 Route finalize" 

427 _FINISH: ClassVar[str] = "Phase 8 Route finalize | Checksum:" 

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

429 

430 

431@export 

432class Phase9_VerifyingRoutedNets(Phase): 

433 _START: ClassVar[str] = "Phase 9 Verifying routed nets" 

434 _FINISH: ClassVar[str] = "Phase 9 Verifying routed nets | Checksum:" 

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

436 

437 

438@export 

439class Phase10_DepositingRoutes(Phase): 

440 _START: ClassVar[str] = "Phase 10 Depositing Routes" 

441 _FINISH: ClassVar[str] = "Phase 10 Depositing Routes | Checksum:" 

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

443 

444 

445@export 

446class Phase11_PostProcessRouting(Phase): 

447 _START: ClassVar[str] = "Phase 11 Post Process Routing" 

448 _FINISH: ClassVar[str] = "Phase 11 Post Process Routing | Checksum:" 

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

450 

451 

452@export 

453class Phase12_PostRouterTiming(Phase): 

454 _START: ClassVar[str] = "Phase 12 Post Router Timing" 

455 _FINISH: ClassVar[str] = "Phase 12 Post Router Timing | Checksum:" 

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

457 

458 

459@export 

460class Phase13_PostRouteEventProcessing(Phase): 

461 _START: ClassVar[str] = "Phase 13 Post-Route Event Processing" 

462 _FINISH: ClassVar[str] = "Phase 13 Post-Route Event Processing | Checksum:" 

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

464 

465 

466@export 

467class RoutingTask(Task): 

468 _START: ClassVar[str] = "Starting Routing Task" 

469 _FINISH: ClassVar[str] = "Ending Routing Task" 

470 

471 _PARSERS: ClassVar[Tuple[Type[Phase], ...]] = ( 

472 Phase1_BuildRTDesign, 

473 Phase2_RouterInitialization, 

474 Phase3_GlobalRouting, 

475 Phase4_InitialRouting, 

476 Phase5_RipUpAndReroute, 

477 Phase6_DelayAndSkewOptimization, 

478 Phase7_PostHoldFix, 

479 Phase8_RouteFinalize, 

480 Phase9_VerifyingRoutedNets, 

481 Phase10_DepositingRoutes, 

482 Phase11_PostProcessRouting, 

483 Phase12_PostRouterTiming, 

484 Phase13_PostRouteEventProcessing 

485 ) 

486 

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

488 line = yield from self._TaskStart(line) 

489 

490 activeParsers: List[Phase] = list(self._phases.values()) 

491 

492 while True: 

493 while True: 

494 if isinstance(line, VivadoMessage): 

495 self._AddMessage(line) 

496 elif line.StartsWith("Phase "): 

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

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

499 line = yield next(phase := parser.Generator(line)) 

500 break 

501 else: 

502 raise Exception(f"Unknown phase: {line!r}") 

503 break 

504 elif line.StartsWith("Ending"): 

505 nextLine = yield from self._TaskFinish(line) 

506 return nextLine 

507 elif line.StartsWith(self._TIME): 507 ↛ 508line 507 didn't jump to line 508 because the condition on line 507 was never true

508 line._kind = LineKind.TaskTime 

509 nextLine = yield line 

510 return nextLine 

511 

512 line = yield line 

513 

514 while phase is not None: 514 ↛ 492line 514 didn't jump to line 492 because the condition on line 514 was always true

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

516 # line = yield task.send(line) 

517 # break 

518 

519 if isinstance(line, VivadoMessage): 

520 self._AddMessage(line) 

521 

522 try: 

523 line = yield phase.send(line) 

524 except StopIteration as ex: 

525 activeParsers.remove(parser) 

526 line = ex.value 

527 break