pyEDAA.OutputFilter 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyEDAA/OutputFilter/CLI/Vivado.py +167 -0
- pyEDAA/OutputFilter/CLI/__init__.py +183 -0
- pyEDAA/OutputFilter/Xilinx/Synthesis.py +573 -0
- pyEDAA/OutputFilter/Xilinx/__init__.py +179 -0
- pyEDAA/OutputFilter/__init__.py +52 -0
- pyedaa_outputfilter-0.1.0.dist-info/METADATA +148 -0
- pyedaa_outputfilter-0.1.0.dist-info/RECORD +11 -0
- pyedaa_outputfilter-0.1.0.dist-info/WHEEL +5 -0
- pyedaa_outputfilter-0.1.0.dist-info/entry_points.txt +2 -0
- pyedaa_outputfilter-0.1.0.dist-info/licenses/LICENSE.md +82 -0
- pyedaa_outputfilter-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,573 @@
|
|
|
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."""
|
|
32
|
+
from datetime import datetime
|
|
33
|
+
from enum import Flag
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from re import compile as re_compile, Pattern
|
|
36
|
+
from typing import ClassVar, List, Optional as Nullable, Callable, Dict, Type
|
|
37
|
+
|
|
38
|
+
from pyTooling.Decorators import export, readonly
|
|
39
|
+
from pyTooling.MetaClasses import ExtendedType, abstractmethod, mustoverride
|
|
40
|
+
from pyTooling.Common import firstValue
|
|
41
|
+
from pyTooling.Stopwatch import Stopwatch
|
|
42
|
+
from pyTooling.Versioning import YearReleaseVersion
|
|
43
|
+
|
|
44
|
+
from pyEDAA.OutputFilter.Xilinx import VivadoMessage, VivadoInfoMessage, VivadoWarningMessage, VivadoCriticalWarningMessage, VivadoErrorMessage
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@export
|
|
48
|
+
class ProcessingState(Flag):
|
|
49
|
+
Processed = 1
|
|
50
|
+
Skipped = 2
|
|
51
|
+
EmptyLine = 4
|
|
52
|
+
CommentLine = 8
|
|
53
|
+
DelimiterLine = 16
|
|
54
|
+
TableLine = 32
|
|
55
|
+
TableHeader = 64
|
|
56
|
+
Reprocess = 512
|
|
57
|
+
Last = 1024
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
TIME_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+)""")
|
|
61
|
+
|
|
62
|
+
@export
|
|
63
|
+
class Parser(metaclass=ExtendedType, slots=True):
|
|
64
|
+
_processor: "Processor"
|
|
65
|
+
|
|
66
|
+
def __init__(self, processor: "Processor"):
|
|
67
|
+
self._processor = processor
|
|
68
|
+
|
|
69
|
+
@readonly
|
|
70
|
+
def Processor(self) -> "Processor":
|
|
71
|
+
return self._processor
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@export
|
|
79
|
+
class Preamble(Parser):
|
|
80
|
+
_toolVersion: Nullable[YearReleaseVersion]
|
|
81
|
+
_startDatetime: Nullable[datetime]
|
|
82
|
+
|
|
83
|
+
_VERSION: ClassVar[Pattern] = re_compile(r"""# Vivado v(\d+\.\d(\.\d)?) \(64-bit\)""")
|
|
84
|
+
_STARTTIME: ClassVar[Pattern] = re_compile(r"""# Start of session at: Thu (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)""")
|
|
85
|
+
|
|
86
|
+
def __init__(self, processor: "Processor"):
|
|
87
|
+
super().__init__(processor)
|
|
88
|
+
|
|
89
|
+
self._toolVersion = None
|
|
90
|
+
self._startDatetime = None
|
|
91
|
+
|
|
92
|
+
@readonly
|
|
93
|
+
def ToolVersion(self) -> YearReleaseVersion:
|
|
94
|
+
return self._toolVersion
|
|
95
|
+
|
|
96
|
+
@readonly
|
|
97
|
+
def StartDatetime(self) -> datetime:
|
|
98
|
+
return self._startDatetime
|
|
99
|
+
|
|
100
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
101
|
+
if self._toolVersion is not None and line.startswith("#----"):
|
|
102
|
+
return ProcessingState.DelimiterLine | ProcessingState.Last
|
|
103
|
+
elif (match := self._VERSION.match(line)) is not None:
|
|
104
|
+
self._toolVersion = YearReleaseVersion.Parse(match[1])
|
|
105
|
+
return ProcessingState.Processed
|
|
106
|
+
elif (match := self._VERSION.match(line)) is not None:
|
|
107
|
+
self._startDatetime = datetime(int(match[6]), int(match[1]), int(match[2]), int(match[3]), int(match[4]), int(match[5]))
|
|
108
|
+
return ProcessingState.Processed
|
|
109
|
+
|
|
110
|
+
return ProcessingState.Skipped
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@export
|
|
114
|
+
class Initialize(Parser):
|
|
115
|
+
_command: str
|
|
116
|
+
_license: VivadoMessage
|
|
117
|
+
|
|
118
|
+
def ParseLine(self, lineNumber: int, line: str) -> bool:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@export
|
|
123
|
+
class Section(Parser):
|
|
124
|
+
# _START: ClassVar[str]
|
|
125
|
+
# _FINISH: ClassVar[str]
|
|
126
|
+
|
|
127
|
+
_duration: float
|
|
128
|
+
|
|
129
|
+
def __init__(self, processor: "Processor"):
|
|
130
|
+
super().__init__(processor)
|
|
131
|
+
|
|
132
|
+
self._duration = 0.0
|
|
133
|
+
|
|
134
|
+
@readonly
|
|
135
|
+
def Duration(self) -> float:
|
|
136
|
+
return self._duration
|
|
137
|
+
|
|
138
|
+
@mustoverride
|
|
139
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
140
|
+
if len(line) == 0:
|
|
141
|
+
return ProcessingState.EmptyLine
|
|
142
|
+
elif line.startswith("----"):
|
|
143
|
+
return ProcessingState.DelimiterLine
|
|
144
|
+
elif line.startswith(self._START):
|
|
145
|
+
return ProcessingState.Skipped
|
|
146
|
+
elif line.startswith(self._FINISH):
|
|
147
|
+
l = line[len(self._FINISH):]
|
|
148
|
+
if (match := TIME_MEMORY_PATTERN.match(l)) is not None:
|
|
149
|
+
# cpuParts = match[1].split(":")
|
|
150
|
+
elapsedParts = match[2].split(":")
|
|
151
|
+
# peakMemory = float(match[3])
|
|
152
|
+
# gainMemory = float(match[4])
|
|
153
|
+
self._duration = int(elapsedParts[0]) * 3600 + int(elapsedParts[1]) * 60 + int(elapsedParts[2])
|
|
154
|
+
|
|
155
|
+
return ProcessingState.Skipped | ProcessingState.Last
|
|
156
|
+
elif line.startswith("Start") or line.startswith("Starting"):
|
|
157
|
+
print(f"ERROR: didn't find finish\n {line}")
|
|
158
|
+
return ProcessingState.Reprocess
|
|
159
|
+
|
|
160
|
+
return ProcessingState.Skipped
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@export
|
|
164
|
+
class RTLElaboration(Section):
|
|
165
|
+
_START: ClassVar[str] = "Starting RTL Elaboration : "
|
|
166
|
+
_FINISH: ClassVar[str] = "Finished RTL Elaboration : "
|
|
167
|
+
|
|
168
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
169
|
+
return super().ParseLine(lineNumber, line)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@export
|
|
173
|
+
class HandlingCustomAttributes1(Section):
|
|
174
|
+
_START: ClassVar[str] = "Start Handling Custom Attributes"
|
|
175
|
+
_FINISH: ClassVar[str] = "Finished Handling Custom Attributes : "
|
|
176
|
+
|
|
177
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
178
|
+
return super().ParseLine(lineNumber, line)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@export
|
|
182
|
+
class LoadingPart(Section):
|
|
183
|
+
_START: ClassVar[str] = "Start Loading Part and Timing Information"
|
|
184
|
+
_FINISH: ClassVar[str] = "Finished Loading Part and Timing Information : "
|
|
185
|
+
|
|
186
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
187
|
+
return super().ParseLine(lineNumber, line)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@export
|
|
191
|
+
class ApplySetProperty(Section):
|
|
192
|
+
_START: ClassVar[str] = "Start Applying 'set_property' XDC Constraints"
|
|
193
|
+
_FINISH: ClassVar[str] = "Finished applying 'set_property' XDC Constraints : "
|
|
194
|
+
|
|
195
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
196
|
+
return super().ParseLine(lineNumber, line)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@export
|
|
200
|
+
class RTLComponentStatistics(Section):
|
|
201
|
+
_START: ClassVar[str] = "Start RTL Component Statistics"
|
|
202
|
+
_FINISH: ClassVar[str] = "Finished RTL Component Statistics"
|
|
203
|
+
|
|
204
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
205
|
+
return super().ParseLine(lineNumber, line)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@export
|
|
209
|
+
class PartResourceSummary(Section):
|
|
210
|
+
_START: ClassVar[str] = "Start Part Resource Summary"
|
|
211
|
+
_FINISH: ClassVar[str] = "Finished Part Resource Summary"
|
|
212
|
+
|
|
213
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
214
|
+
return super().ParseLine(lineNumber, line)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@export
|
|
218
|
+
class CrossBoundaryAndAreaOptimization(Section):
|
|
219
|
+
_START: ClassVar[str] = "Start Cross Boundary and Area Optimization"
|
|
220
|
+
_FINISH: ClassVar[str] = "Finished Cross Boundary and Area Optimization : "
|
|
221
|
+
|
|
222
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
223
|
+
return super().ParseLine(lineNumber, line)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@export
|
|
227
|
+
class ApplyingXDCTimingConstraints(Section):
|
|
228
|
+
_START: ClassVar[str] = "Start Applying XDC Timing Constraints"
|
|
229
|
+
_FINISH: ClassVar[str] = "Finished Applying XDC Timing Constraints : "
|
|
230
|
+
|
|
231
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
232
|
+
return super().ParseLine(lineNumber, line)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@export
|
|
236
|
+
class TimingOptimization(Section):
|
|
237
|
+
_START: ClassVar[str] = "Start Timing Optimization"
|
|
238
|
+
_FINISH: ClassVar[str] = "Finished Timing Optimization : "
|
|
239
|
+
|
|
240
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
241
|
+
return super().ParseLine(lineNumber, line)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@export
|
|
245
|
+
class TechnologyMapping(Section):
|
|
246
|
+
_START: ClassVar[str] = "Start Technology Mapping"
|
|
247
|
+
_FINISH: ClassVar[str] = "Finished Technology Mapping : "
|
|
248
|
+
|
|
249
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
250
|
+
return super().ParseLine(lineNumber, line)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@export
|
|
254
|
+
class IOInsertion(Section):
|
|
255
|
+
_START: ClassVar[str] = "Start IO Insertion"
|
|
256
|
+
_FINISH: ClassVar[str] = "Finished IO Insertion : "
|
|
257
|
+
|
|
258
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
259
|
+
return super().ParseLine(lineNumber, line)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@export
|
|
263
|
+
class FlatteningBeforeIOInsertion(Section):
|
|
264
|
+
_START: ClassVar[str] = "Start Flattening Before IO Insertion"
|
|
265
|
+
_FINISH: ClassVar[str] = "Finished Flattening Before IO Insertion"
|
|
266
|
+
|
|
267
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
268
|
+
return super().ParseLine(lineNumber, line)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@export
|
|
272
|
+
class FinalNetlistCleanup(Section):
|
|
273
|
+
_START: ClassVar[str] = "Start Final Netlist Cleanup"
|
|
274
|
+
_FINISH: ClassVar[str] = "Finished Final Netlist Cleanup"
|
|
275
|
+
|
|
276
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
277
|
+
return super().ParseLine(lineNumber, line)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@export
|
|
281
|
+
class RenamingGeneratedInstances(Section):
|
|
282
|
+
_START: ClassVar[str] = "Start Renaming Generated Instances"
|
|
283
|
+
_FINISH: ClassVar[str] = "Finished Renaming Generated Instances : "
|
|
284
|
+
|
|
285
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
286
|
+
return super().ParseLine(lineNumber, line)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@export
|
|
290
|
+
class RebuildingUserHierarchy(Section):
|
|
291
|
+
_START: ClassVar[str] = "Start Rebuilding User Hierarchy"
|
|
292
|
+
_FINISH: ClassVar[str] = "Finished Rebuilding User Hierarchy : "
|
|
293
|
+
|
|
294
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
295
|
+
return super().ParseLine(lineNumber, line)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@export
|
|
299
|
+
class RenamingGeneratedPorts(Section):
|
|
300
|
+
_START: ClassVar[str] = "Start Renaming Generated Ports"
|
|
301
|
+
_FINISH: ClassVar[str] = "Finished Renaming Generated Ports : "
|
|
302
|
+
|
|
303
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
304
|
+
return super().ParseLine(lineNumber, line)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
@export
|
|
308
|
+
class HandlingCustomAttributes2(Section):
|
|
309
|
+
_START: ClassVar[str] = "Start Handling Custom Attributes"
|
|
310
|
+
_FINISH: ClassVar[str] = "Finished Handling Custom Attributes : "
|
|
311
|
+
|
|
312
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
313
|
+
return super().ParseLine(lineNumber, line)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@export
|
|
317
|
+
class RenamingGeneratedNets(Section):
|
|
318
|
+
_START: ClassVar[str] = "Start Renaming Generated Nets"
|
|
319
|
+
_FINISH: ClassVar[str] = "Finished Renaming Generated Nets : "
|
|
320
|
+
|
|
321
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
322
|
+
return super().ParseLine(lineNumber, line)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@export
|
|
326
|
+
class WritingSynthesisReport(Section):
|
|
327
|
+
_START: ClassVar[str] = "Start Writing Synthesis Report"
|
|
328
|
+
_FINISH: ClassVar[str] = "Finished Writing Synthesis Report : "
|
|
329
|
+
|
|
330
|
+
_state: int
|
|
331
|
+
_blackboxes: Dict[str, int]
|
|
332
|
+
_cells: Dict[str, int]
|
|
333
|
+
|
|
334
|
+
def __init__(self, processor: "Processor"):
|
|
335
|
+
super().__init__(processor)
|
|
336
|
+
|
|
337
|
+
self._state = 0
|
|
338
|
+
self._blackboxes = {}
|
|
339
|
+
self._cells = {}
|
|
340
|
+
|
|
341
|
+
@readonly
|
|
342
|
+
def Cells(self) -> Dict[str, int]:
|
|
343
|
+
return self._cells
|
|
344
|
+
|
|
345
|
+
@readonly
|
|
346
|
+
def Blackboxes(self) -> Dict[str, int]:
|
|
347
|
+
return self._blackboxes
|
|
348
|
+
|
|
349
|
+
def ParseLine(self, lineNumber: int, line: str) -> ProcessingState:
|
|
350
|
+
if self._state == 0:
|
|
351
|
+
if line.startswith("Report BlackBoxes:"):
|
|
352
|
+
self._state = 10
|
|
353
|
+
return ProcessingState.Processed
|
|
354
|
+
elif line.startswith("Report Cell Usage:"):
|
|
355
|
+
self._state = 20
|
|
356
|
+
return ProcessingState.Processed
|
|
357
|
+
elif 10 <= self._state < 20:
|
|
358
|
+
if self._state == 10 or self._state == 12 and line.startswith("+-"):
|
|
359
|
+
self._state += 1
|
|
360
|
+
return ProcessingState.TableLine
|
|
361
|
+
elif self._state == 11 and line.startswith("| "):
|
|
362
|
+
self._state += 1
|
|
363
|
+
return ProcessingState.TableHeader
|
|
364
|
+
elif self._state == 13:
|
|
365
|
+
if line.startswith("+-"):
|
|
366
|
+
self._state = 0
|
|
367
|
+
return ProcessingState.TableLine
|
|
368
|
+
else:
|
|
369
|
+
columns = line.strip("|").split("|")
|
|
370
|
+
self._blackboxes[columns[1].strip()] = int(columns[2].strip())
|
|
371
|
+
elif 20 <= self._state < 30:
|
|
372
|
+
if self._state == 20 or self._state == 22 and line.startswith("+-"):
|
|
373
|
+
self._state += 1
|
|
374
|
+
return ProcessingState.TableLine
|
|
375
|
+
elif self._state == 21 and line.startswith("| "):
|
|
376
|
+
self._state += 1
|
|
377
|
+
return ProcessingState.TableHeader
|
|
378
|
+
elif self._state == 23:
|
|
379
|
+
if line.startswith("+-"):
|
|
380
|
+
self._state = 0
|
|
381
|
+
return ProcessingState.TableLine
|
|
382
|
+
else:
|
|
383
|
+
columns = line.strip("|").split("|")
|
|
384
|
+
self._cells[columns[1].strip()] = int(columns[2].strip())
|
|
385
|
+
|
|
386
|
+
return super().ParseLine(lineNumber, line)
|
|
387
|
+
|
|
388
|
+
PARSERS = (
|
|
389
|
+
Preamble,
|
|
390
|
+
RTLElaboration,
|
|
391
|
+
HandlingCustomAttributes1,
|
|
392
|
+
LoadingPart,
|
|
393
|
+
ApplySetProperty,
|
|
394
|
+
RTLComponentStatistics,
|
|
395
|
+
PartResourceSummary,
|
|
396
|
+
CrossBoundaryAndAreaOptimization,
|
|
397
|
+
ApplyingXDCTimingConstraints,
|
|
398
|
+
TimingOptimization,
|
|
399
|
+
TechnologyMapping,
|
|
400
|
+
IOInsertion,
|
|
401
|
+
FlatteningBeforeIOInsertion,
|
|
402
|
+
FinalNetlistCleanup,
|
|
403
|
+
RenamingGeneratedInstances,
|
|
404
|
+
RebuildingUserHierarchy,
|
|
405
|
+
RenamingGeneratedPorts,
|
|
406
|
+
HandlingCustomAttributes2,
|
|
407
|
+
RenamingGeneratedNets,
|
|
408
|
+
WritingSynthesisReport,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
@export
|
|
412
|
+
class Processor(metaclass=ExtendedType, slots=True):
|
|
413
|
+
_logfile: Path
|
|
414
|
+
_parsers: Dict[Type[Parser], Parser]
|
|
415
|
+
_state: Callable[[int, str], bool]
|
|
416
|
+
_duration: float
|
|
417
|
+
|
|
418
|
+
_infoMessages: List[VivadoInfoMessage]
|
|
419
|
+
_warningMessages: List[VivadoWarningMessage]
|
|
420
|
+
_criticalWarningMessages: List[VivadoCriticalWarningMessage]
|
|
421
|
+
_errorMessages: List[VivadoErrorMessage]
|
|
422
|
+
_toolIDs: Dict[int, str]
|
|
423
|
+
_toolNames: Dict[str, int]
|
|
424
|
+
_messagesByID: Dict[int, Dict[int, List[VivadoMessage]]]
|
|
425
|
+
|
|
426
|
+
def __init__(self, synthesisLogfile: Path):
|
|
427
|
+
self._logfile = synthesisLogfile
|
|
428
|
+
self._parsers = {p: p(self) for p in PARSERS}
|
|
429
|
+
self._state = firstValue(self._parsers).ParseLine
|
|
430
|
+
self._duration = 0.0
|
|
431
|
+
|
|
432
|
+
self._infoMessages = []
|
|
433
|
+
self._warningMessages = []
|
|
434
|
+
self._criticalWarningMessages = []
|
|
435
|
+
self._errorMessages = []
|
|
436
|
+
self._toolIDs = {}
|
|
437
|
+
self._toolNames = {}
|
|
438
|
+
self._messagesByID = {}
|
|
439
|
+
|
|
440
|
+
@readonly
|
|
441
|
+
def Duration(self) -> float:
|
|
442
|
+
return self._duration
|
|
443
|
+
|
|
444
|
+
@readonly
|
|
445
|
+
def InfoMessages(self) -> List[VivadoInfoMessage]:
|
|
446
|
+
return self._infoMessages
|
|
447
|
+
|
|
448
|
+
@readonly
|
|
449
|
+
def WarningMessages(self) -> List[VivadoWarningMessage]:
|
|
450
|
+
return self._warningMessages
|
|
451
|
+
|
|
452
|
+
@readonly
|
|
453
|
+
def CriticalWarningMessages(self) -> List[VivadoCriticalWarningMessage]:
|
|
454
|
+
return self._criticalWarningMessages
|
|
455
|
+
|
|
456
|
+
@readonly
|
|
457
|
+
def ErrorMessages(self) -> List[VivadoErrorMessage]:
|
|
458
|
+
return self._errorMessages
|
|
459
|
+
|
|
460
|
+
def __getitem__(self, item: Type[Parser]) -> Parser:
|
|
461
|
+
return self._parsers[item]
|
|
462
|
+
|
|
463
|
+
def Parse(self):
|
|
464
|
+
with Stopwatch() as sw:
|
|
465
|
+
with self._logfile.open("r", encoding="utf-8") as f:
|
|
466
|
+
content = f.read()
|
|
467
|
+
|
|
468
|
+
activeParsers = list(self._parsers.values())
|
|
469
|
+
|
|
470
|
+
lines = content.splitlines()
|
|
471
|
+
for lineNumber, line in enumerate(l.rstrip() for l in lines):
|
|
472
|
+
prefix = line[:4]
|
|
473
|
+
if prefix == "INFO":
|
|
474
|
+
if (message := VivadoInfoMessage.Parse(line, lineNumber)) is None:
|
|
475
|
+
print(f"pattern not detected\n{line}")
|
|
476
|
+
continue
|
|
477
|
+
|
|
478
|
+
self._infoMessages.append(message)
|
|
479
|
+
elif prefix == "WARN":
|
|
480
|
+
if (message := VivadoWarningMessage.Parse(line, lineNumber)) is None:
|
|
481
|
+
print(f"pattern not detected\n{line}")
|
|
482
|
+
continue
|
|
483
|
+
|
|
484
|
+
self._warningMessages.append(message)
|
|
485
|
+
elif prefix == "CRIT":
|
|
486
|
+
if (message := VivadoCriticalWarningMessage.Parse(line, lineNumber)) is None:
|
|
487
|
+
print(f"pattern not detected\n{line}")
|
|
488
|
+
continue
|
|
489
|
+
|
|
490
|
+
self._criticalWarningMessages.append(message)
|
|
491
|
+
elif prefix == "ERRO":
|
|
492
|
+
if (message := VivadoErrorMessage.Parse(line, lineNumber)) is None:
|
|
493
|
+
print(f"pattern not detected\n{line}")
|
|
494
|
+
continue
|
|
495
|
+
|
|
496
|
+
self._errorMessages.append(message)
|
|
497
|
+
else:
|
|
498
|
+
if self._state is not None:
|
|
499
|
+
result = self._state(lineNumber, line)
|
|
500
|
+
if ProcessingState.Last in result:
|
|
501
|
+
obj: Section = self._state.__self__
|
|
502
|
+
activeParsers.remove(obj)
|
|
503
|
+
self._state = None
|
|
504
|
+
|
|
505
|
+
print(f" DONE: {obj.__class__.__name__}")
|
|
506
|
+
else:
|
|
507
|
+
if line.startswith("Start") or line.startswith("Starting"):
|
|
508
|
+
for parser in activeParsers: # type: Section
|
|
509
|
+
if line.startswith(parser._START):
|
|
510
|
+
print(f"BEGIN: {parser.__class__.__name__}")
|
|
511
|
+
self._state = parser.ParseLine
|
|
512
|
+
_ = self._state(lineNumber, line)
|
|
513
|
+
break
|
|
514
|
+
else:
|
|
515
|
+
print(f"Unknown section\n {line}")
|
|
516
|
+
|
|
517
|
+
continue
|
|
518
|
+
|
|
519
|
+
if message._toolID in self._messagesByID:
|
|
520
|
+
sub = self._messagesByID[message._toolID]
|
|
521
|
+
if message._messageKindID in sub:
|
|
522
|
+
sub[message._messageKindID].append(message)
|
|
523
|
+
else:
|
|
524
|
+
sub[message._messageKindID] = [message]
|
|
525
|
+
else:
|
|
526
|
+
self._toolIDs[message._toolID] = message._toolName
|
|
527
|
+
self._toolNames[message._toolName] = message._toolID
|
|
528
|
+
self._messagesByID[message._toolID] = {message._messageKindID: [message]}
|
|
529
|
+
|
|
530
|
+
self._duration = sw.Duration
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def main():
|
|
534
|
+
logfile = Path("tests/data/Stopwatch/toplevel.vds")
|
|
535
|
+
logfile = Path("tests/data/ADL-1000/toplevel.vds")
|
|
536
|
+
processor = Processor(logfile)
|
|
537
|
+
processor.Parse()
|
|
538
|
+
|
|
539
|
+
print()
|
|
540
|
+
print(f"Vivado version: {processor._parsers[Preamble]._toolVersion}")
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
#
|
|
544
|
+
# print("%" * 80)
|
|
545
|
+
# for toolID, messageGroups in processor._messagesByID.items():
|
|
546
|
+
# print(f"{toolID} ({len(messageGroups)}):")
|
|
547
|
+
# for messageID, messages in messageGroups.items():
|
|
548
|
+
# print(f" {messageID} ({len(messages)}):")
|
|
549
|
+
# for message in messages:
|
|
550
|
+
# print(f" {message}")
|
|
551
|
+
#
|
|
552
|
+
# print("%" * 80)
|
|
553
|
+
# for tool, toolID in processor._toolNames.items():
|
|
554
|
+
# messages = list(chain(*processor._messagesByID[toolID].values()))
|
|
555
|
+
# print(f"{tool} ({len(messages)}):")
|
|
556
|
+
# for message in messages:
|
|
557
|
+
# print(f" {message}")
|
|
558
|
+
|
|
559
|
+
# print(f"Cells Statistics")
|
|
560
|
+
# for cellName, cellCount in processor._parsers[WritingSynthesisReport]._cells.items():
|
|
561
|
+
# print(f" {cellName:16}: {cellCount}")
|
|
562
|
+
|
|
563
|
+
if __name__ == '__main__':
|
|
564
|
+
main()
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
# latches
|
|
568
|
+
# unused
|
|
569
|
+
# used but not set
|
|
570
|
+
# statemachine encodings
|
|
571
|
+
# resources
|
|
572
|
+
# * RTL
|
|
573
|
+
# * Mapped
|