Coverage for pyEDAA/OutputFilter/Xilinx/SynthesizeDesign.py: 79%

385 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 re import compile as re_compile 

33from typing import ClassVar, Dict, Generator, List, Type 

34 

35from pyTooling.Decorators import export, readonly 

36from pyTooling.MetaClasses import ExtendedType, abstractmethod 

37 

38from pyEDAA.OutputFilter.Xilinx.Common import VHDLAssertionMessage, Line, LineKind, VivadoInfoMessage, \ 

39 VHDLReportMessage, VivadoMessage, VivadoWarningMessage, VivadoCriticalWarningMessage, VivadoErrorMessage 

40from pyEDAA.OutputFilter.Xilinx.Common2 import BaseParser 

41 

42TIME_MEMORY_PATTERN = re_compile(r"""Time \(s\): cpu = (\d{2}:\d{2}:\d{2}) ; elapsed = (\d{2}:\d{2}:\d{2}) . Memory \(MB\): peak = (\d+\.\d+) ; gain = (\d+\.\d+)""") 

43 

44 

45@export 

46class BaseSection(metaclass=ExtendedType, mixin=True): 

47 @abstractmethod 

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

49 pass 

50 

51 @abstractmethod 

52 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]: 

53 pass 

54 

55 @abstractmethod 

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

57 pass 

58 

59 

60@export 

61class Section(BaseParser, BaseSection): 

62 # _START: ClassVar[str] 

63 # _FINISH: ClassVar[str] 

64 

65 _command: "Command" 

66 _duration: float 

67 

68 def __init__(self, command: "Command"): 

69 super().__init__() #command._processor) 

70 

71 self._command = command 

72 self._duration = 0.0 

73 

74 @readonly 

75 def Duration(self) -> float: 

76 return self._duration 

77 

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

79 line._kind = LineKind.SectionStart 

80 

81 line = yield line 

82 if line.StartsWith("----"): 82 ↛ 85line 82 didn't jump to line 85 because the condition on line 82 was always true

83 line._kind = LineKind.SectionStart | LineKind.SectionDelimiter 

84 else: 

85 line._kind |= LineKind.ProcessorError 

86 

87 nextLine = yield line 

88 return nextLine 

89 

90 def _SectionFinish(self, line: Line, skipDashes: bool = False) -> Generator[Line, Line, None]: 

91 if not skipDashes: 

92 if line.StartsWith("----"): 92 ↛ 95line 92 didn't jump to line 95 because the condition on line 92 was always true

93 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

94 else: 

95 line._kind |= LineKind.ProcessorError 

96 

97 line = yield line 

98 

99 if line.StartsWith(self._FINISH): 99 ↛ 102line 99 didn't jump to line 102 because the condition on line 99 was always true

100 line._kind = LineKind.SectionEnd 

101 else: 

102 line._kind |= LineKind.ProcessorError 

103 

104 line = yield line 

105 if line.StartsWith("----"): 105 ↛ 108line 105 didn't jump to line 108 because the condition on line 105 was always true

106 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

107 else: 

108 line._kind |= LineKind.ProcessorError 

109 

110 nextLine = yield line 

111 return nextLine 

112 

113 # @mustoverride 

114 # def ParseLine(self, lineNumber: int, line: str) -> ProcessingState: 

115 # if len(line) == 0: 

116 # return ProcessingState.EmptyLine 

117 # elif line.startswith("----"): 

118 # return ProcessingState.DelimiterLine 

119 # elif line.startswith(self._START): 

120 # return ProcessingState.Skipped 

121 # elif line.startswith(self._FINISH): 

122 # l = line[len(self._FINISH):] 

123 # if (match := TIME_MEMORY_PATTERN.match(l)) is not None: 

124 # # cpuParts = match[1].split(":") 

125 # elapsedParts = match[2].split(":") 

126 # # peakMemory = float(match[3]) 

127 # # gainMemory = float(match[4]) 

128 # self._duration = int(elapsedParts[0]) * 3600 + int(elapsedParts[1]) * 60 + int(elapsedParts[2]) 

129 # 

130 # return ProcessingState.Skipped | ProcessingState.Last 

131 # elif line.startswith("Start") or line.startswith("Starting"): 

132 # print(f"ERROR: didn't find finish\n {line}") 

133 # return ProcessingState.Reprocess 

134 # 

135 # return ProcessingState.Skipped 

136 

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

138 line = yield from self._SectionStart(line) 

139 

140 while True: 

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

142 line = yield line 

143 continue 

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

145 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

146 break 

147 elif isinstance(line, VivadoMessage): 

148 self._AddMessage(line) 

149 else: 

150 line._kind = LineKind.Verbose 

151 

152 line = yield line 

153 

154 # line = yield line 

155 nextLine = yield from self._SectionFinish(line) 

156 return nextLine 

157 

158 

159@export 

160class SubSection(BaseParser, BaseSection): 

161 _section: Section 

162 

163 def __init__(self, section: Section) -> None: 

164 super().__init__() 

165 self._section = section 

166 

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

168 line._kind = LineKind.SubSectionStart 

169 

170 line = yield line 

171 if line.StartsWith("----"): 171 ↛ 174line 171 didn't jump to line 174 because the condition on line 171 was always true

172 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter 

173 else: 

174 line._kind |= LineKind.ProcessorError 

175 

176 nextLine = yield line 

177 return nextLine 

178 

179 def _SectionFinish(self, line: Line) -> Generator[Line, Line, None]: 

180 if line.StartsWith("----"): 180 ↛ 183line 180 didn't jump to line 183 because the condition on line 180 was always true

181 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter 

182 else: 

183 line._kind |= LineKind.ProcessorError 

184 

185 line = yield line 

186 if line.StartsWith(self._FINISH): 186 ↛ 189line 186 didn't jump to line 189 because the condition on line 186 was always true

187 line._kind = LineKind.SubSectionEnd 

188 else: 

189 line._kind |= LineKind.ProcessorError 

190 

191 line = yield line 

192 if line.StartsWith("----"): 192 ↛ 195line 192 didn't jump to line 195 because the condition on line 192 was always true

193 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter 

194 else: 

195 line._kind |= LineKind.ProcessorError 

196 

197 nextLine = yield line 

198 return nextLine 

199 

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

201 line = yield from self._SectionStart(line) 

202 

203 while True: 

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

205 line = yield line 

206 continue 

207 elif line.StartsWith("----"): 207 ↛ 210line 207 didn't jump to line 210 because the condition on line 207 was always true

208 line._kind = LineKind.SubSectionEnd | LineKind.SubSectionDelimiter 

209 break 

210 elif isinstance(line, VivadoMessage): 

211 self._AddMessage(line) 

212 else: 

213 line._kind = LineKind.Verbose 

214 

215 line = yield line 

216 

217 nextLine = yield from self._SectionFinish(line) 

218 return nextLine 

219 

220 

221@export 

222class RTLElaboration(Section): 

223 _START: ClassVar[str] = "Starting RTL Elaboration : " 

224 _FINISH: ClassVar[str] = "Finished RTL Elaboration : " 

225 

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

227 line = yield from self._SectionStart(line) 

228 

229 while True: 

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

231 line = yield line 

232 continue 

233 elif isinstance(line, VivadoInfoMessage): 

234 if line._toolID == 8: 234 ↛ 248line 234 didn't jump to line 248 because the condition on line 234 was always true

235 if line._messageKindID == 63: # VHDL assert statement 

236 newLine = VHDLAssertionMessage.Convert(line) 

237 if newLine is None: 237 ↛ 238line 237 didn't jump to line 238 because the condition on line 237 was never true

238 pass 

239 else: 

240 line = newLine 

241 elif line._messageKindID == 6031: # VHDL report statement 

242 newLine = VHDLReportMessage.Convert(line) 

243 if newLine is None: 243 ↛ 244line 243 didn't jump to line 244 because the condition on line 243 was never true

244 pass 

245 else: 

246 line = newLine 

247 

248 if line.StartsWith("----"): 

249 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

250 break 

251 elif isinstance(line, VivadoMessage): 

252 self._AddMessage(line) 

253 else: 

254 line._kind = LineKind.Verbose 

255 

256 line = yield line 

257 

258 # line = yield line 

259 nextLine = yield from self._SectionFinish(line) 

260 return nextLine 

261 

262 

263@export 

264class HandlingCustomAttributes(Section): 

265 _START: ClassVar[str] = "Start Handling Custom Attributes" 

266 _FINISH: ClassVar[str] = "Finished Handling Custom Attributes : " 

267 

268 

269@export 

270class ConstraintValidation(Section): 

271 _START: ClassVar[str] = "Finished RTL Optimization Phase 1" 

272 _FINISH: ClassVar[str] = "Finished Constraint Validation : " 

273 

274 

275@export 

276class LoadingPart(Section): 

277 _START: ClassVar[str] = "Start Loading Part and Timing Information" 

278 _FINISH: ClassVar[str] = "Finished Loading Part and Timing Information : " 

279 

280 _part: str 

281 

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

283 super().__init__(processor) 

284 

285 self._part = None 

286 

287 @readonly 

288 def Part(self) -> str: 

289 return self._part 

290 

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

292 line = yield from self._SectionStart(line) 

293 

294 while True: 

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

296 line = yield line 

297 continue 

298 elif line.StartsWith("Loading part: "): 

299 line._kind = LineKind.Normal 

300 self._part = line._message[14:].strip() 

301 elif line.StartsWith("----"): 301 ↛ 304line 301 didn't jump to line 304 because the condition on line 301 was always true

302 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

303 break 

304 elif isinstance(line, VivadoMessage): 

305 self._AddMessage(line) 

306 else: 

307 line._kind = LineKind.Verbose 

308 

309 line = yield line 

310 

311 nextLine = yield from self._SectionFinish(line) 

312 return nextLine 

313 

314@export 

315class ApplySetProperty(Section): 

316 _START: ClassVar[str] = "Start Applying 'set_property' XDC Constraints" 

317 _FINISH: ClassVar[str] = "Finished applying 'set_property' XDC Constraints : " 

318 

319 

320@export 

321class RTLComponentStatistics(Section): 

322 _START: ClassVar[str] = "Start RTL Component Statistics" 

323 _FINISH: ClassVar[str] = "Finished RTL Component Statistics" 

324 

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

326 line = yield from self._SectionStart(line) 

327 

328 while True: 

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

330 line = yield line 

331 continue 

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

333 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

334 break 

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

336 self._AddMessage(line) 

337 else: 

338 line._kind = LineKind.Verbose 

339 

340 line = yield line 

341 

342 nextLine = yield from self._SectionFinish(line) 

343 return nextLine 

344 

345 

346@export 

347class PartResourceSummary(Section): 

348 _START: ClassVar[str] = "Start Part Resource Summary" 

349 _FINISH: ClassVar[str] = "Finished Part Resource Summary" 

350 

351 

352@export 

353class CrossBoundaryAndAreaOptimization(Section): 

354 _START: ClassVar[str] = "Start Cross Boundary and Area Optimization" 

355 _FINISH: ClassVar[str] = "Finished Cross Boundary and Area Optimization : " 

356 

357 

358@export 

359class ROM_RAM_DSP_SR_Retiming(Section): 

360 _START: ClassVar[str] = "Start ROM, RAM, DSP, Shift Register and Retiming Reporting" 

361 _FINISH: ClassVar[str] = "Finished ROM, RAM, DSP, Shift Register and Retiming Reporting" 

362 

363 

364@export 

365class ApplyingXDCTimingConstraints(Section): 

366 _START: ClassVar[str] = "Start Applying XDC Timing Constraints" 

367 _FINISH: ClassVar[str] = "Finished Applying XDC Timing Constraints : " 

368 

369 

370@export 

371class TimingOptimization(Section): 

372 _START: ClassVar[str] = "Start Timing Optimization" 

373 _FINISH: ClassVar[str] = "Finished Timing Optimization : " 

374 

375 

376@export 

377class TechnologyMapping(Section): 

378 _START: ClassVar[str] = "Start Technology Mapping" 

379 _FINISH: ClassVar[str] = "Finished Technology Mapping : " 

380 

381 

382@export 

383class FlatteningBeforeIOInsertion(SubSection): 

384 _START: ClassVar[str] = "Start Flattening Before IO Insertion" 

385 _FINISH: ClassVar[str] = "Finished Flattening Before IO Insertion" 

386 

387 

388@export 

389class FinalNetlistCleanup(SubSection): 

390 _START: ClassVar[str] = "Start Final Netlist Cleanup" 

391 _FINISH: ClassVar[str] = "Finished Final Netlist Cleanup" 

392 

393 

394@export 

395class IOInsertion(Section): 

396 _START: ClassVar[str] = "Start IO Insertion" 

397 _FINISH: ClassVar[str] = "Finished IO Insertion : " 

398 

399 _subsections: Dict[Type[SubSection], SubSection] 

400 

401 def __init__(self, command: "Command"): 

402 super().__init__(command) 

403 

404 self._subsections = {} 

405 

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

407 line = yield from self._SectionStart(line) 

408 

409 while True: 

410 while True: 

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

412 line = yield line 

413 continue 

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

415 line._kind = LineKind.SubSectionStart | LineKind.SubSectionDelimiter 

416 elif line.StartsWith("Start "): 

417 if line == FlatteningBeforeIOInsertion._START: 

418 self._subsections[FlatteningBeforeIOInsertion] = (subsection := FlatteningBeforeIOInsertion(self)) 

419 line = yield next(parser := subsection.Generator(line)) 

420 break 

421 elif line == FinalNetlistCleanup._START: 421 ↛ 432line 421 didn't jump to line 432 because the condition on line 421 was always true

422 self._subsections[FinalNetlistCleanup] = (subsection := FinalNetlistCleanup(self)) 

423 line = yield next(parser := subsection.Generator(line)) 

424 break 

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

426 self._AddMessage(line) 

427 elif line.StartsWith("Finished "): 427 ↛ 430line 427 didn't jump to line 430 because the condition on line 427 was always true

428 break 

429 else: 

430 line._kind |= LineKind.ProcessorError 

431 

432 line = yield line 

433 

434 if line.StartsWith(self._FINISH): 

435 break 

436 

437 while True: 

438 if line.StartsWith(subsection._FINISH): 

439 line = yield parser.send(line) 

440 line = yield parser.send(line) 

441 

442 subsection = None 

443 parser = None 

444 break 

445 

446 line = parser.send(line) 

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

448 self._AddMessage(line) 

449 

450 line = yield line 

451 

452 nextLine = yield from self._SectionFinish(line, True) 

453 return nextLine 

454 

455 

456@export 

457class RenamingGeneratedInstances(Section): 

458 _START: ClassVar[str] = "Start Renaming Generated Instances" 

459 _FINISH: ClassVar[str] = "Finished Renaming Generated Instances : " 

460 

461 

462@export 

463class RebuildingUserHierarchy(Section): 

464 _START: ClassVar[str] = "Start Rebuilding User Hierarchy" 

465 _FINISH: ClassVar[str] = "Finished Rebuilding User Hierarchy : " 

466 

467 

468@export 

469class RenamingGeneratedPorts(Section): 

470 _START: ClassVar[str] = "Start Renaming Generated Ports" 

471 _FINISH: ClassVar[str] = "Finished Renaming Generated Ports : " 

472 

473 

474@export 

475class RenamingGeneratedNets(Section): 

476 _START: ClassVar[str] = "Start Renaming Generated Nets" 

477 _FINISH: ClassVar[str] = "Finished Renaming Generated Nets : " 

478 

479 

480@export 

481class WritingSynthesisReport(Section): 

482 _START: ClassVar[str] = "Start Writing Synthesis Report" 

483 _FINISH: ClassVar[str] = "Finished Writing Synthesis Report : " 

484 

485 _blackboxes: Dict[str, int] 

486 _cells: Dict[str, int] 

487 

488 def __init__(self, command: "Command"): 

489 super().__init__(command) 

490 

491 self._blackboxes = {} 

492 self._cells = {} 

493 

494 @readonly 

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

496 return self._cells 

497 

498 @readonly 

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

500 return self._blackboxes 

501 

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

503 if line.StartsWith("+-"): 503 ↛ 506line 503 didn't jump to line 506 because the condition on line 503 was always true

504 line._kind = LineKind.TableFrame 

505 else: 

506 line._kind = LineKind.ProcessorError 

507 

508 line = yield line 

509 if line.StartsWith("| "): 509 ↛ 512line 509 didn't jump to line 512 because the condition on line 509 was always true

510 line._kind = LineKind.TableHeader 

511 else: 

512 line._kind = LineKind.ProcessorError 

513 

514 line = yield line 

515 if line.StartsWith("+-"): 515 ↛ 518line 515 didn't jump to line 518 because the condition on line 515 was always true

516 line._kind = LineKind.TableFrame 

517 else: 

518 line._kind = LineKind.ProcessorError 

519 

520 line = yield line 

521 while True: 

522 if line.StartsWith("|"): 522 ↛ 523line 522 didn't jump to line 523 because the condition on line 522 was never true

523 line._kind = LineKind.TableRow 

524 

525 columns = line._message.strip("|").split("|") 

526 self._blackboxes[columns[1].strip()] = int(columns[2].strip()) 

527 elif line.StartsWith("+-"): 527 ↛ 531line 527 didn't jump to line 531 because the condition on line 527 was always true

528 line._kind = LineKind.TableFrame 

529 break 

530 else: 

531 line._kind = LineKind.ProcessorError 

532 

533 line = yield line 

534 

535 nextLine = yield line 

536 return nextLine 

537 

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

539 if line.StartsWith("+-"): 539 ↛ 542line 539 didn't jump to line 542 because the condition on line 539 was always true

540 line._kind = LineKind.TableFrame 

541 else: 

542 line._kind = LineKind.ProcessorError 

543 

544 line = yield line 

545 if line.StartsWith("| "): 545 ↛ 548line 545 didn't jump to line 548 because the condition on line 545 was always true

546 line._kind = LineKind.TableHeader 

547 else: 

548 line._kind = LineKind.ProcessorError 

549 

550 line = yield line 

551 if line.StartsWith("+-"): 551 ↛ 554line 551 didn't jump to line 554 because the condition on line 551 was always true

552 line._kind = LineKind.TableFrame 

553 else: 

554 line._kind = LineKind.ProcessorError 

555 

556 line = yield line 

557 while True: 

558 if line.StartsWith("|"): 

559 line._kind = LineKind.TableRow 

560 

561 columns = line._message.strip("|").split("|") 

562 self._cells[columns[1].strip()] = int(columns[2].strip()) 

563 elif line.StartsWith("+-"): 563 ↛ 567line 563 didn't jump to line 567 because the condition on line 563 was always true

564 line._kind = LineKind.TableFrame 

565 break 

566 else: 

567 line._kind = LineKind.ProcessorError 

568 

569 line = yield line 

570 

571 nextLine = yield line 

572 return nextLine 

573 

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

575 line = yield from self._SectionStart(line) 

576 

577 while True: 

578 if line._kind is LineKind.Empty: 

579 line = yield line 

580 continue 

581 elif line.StartsWith("Report BlackBoxes:"): 

582 line._kind = LineKind.ParagraphHeadline 

583 line = yield line 

584 line = yield from self._BlackboxesGenerator(line) 

585 elif line.StartsWith("Report Cell Usage:"): 

586 line._kind = LineKind.ParagraphHeadline 

587 line = yield line 

588 line = yield from self._CellGenerator(line) 

589 elif line.StartsWith("----"): 589 ↛ 592line 589 didn't jump to line 592 because the condition on line 589 was always true

590 line._kind = LineKind.SectionEnd | LineKind.SectionDelimiter 

591 break 

592 elif isinstance(line, VivadoMessage): 

593 self._AddMessage(line) 

594 else: 

595 line._kind = LineKind.Verbose 

596 line = yield line 

597 

598 nextLine = yield from self._SectionFinish(line) 

599 return nextLine