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,167 @@
|
|
|
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
|
+
from argparse import Namespace
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from sys import stdin
|
|
34
|
+
from typing import NoReturn
|
|
35
|
+
|
|
36
|
+
from pyTooling.Decorators import readonly
|
|
37
|
+
from pyTooling.MetaClasses import ExtendedType
|
|
38
|
+
from pyTooling.Common import count
|
|
39
|
+
from pyTooling.Attributes.ArgParse import CommandHandler
|
|
40
|
+
from pyTooling.Attributes.ArgParse.Flag import LongFlag
|
|
41
|
+
from pyTooling.Attributes.ArgParse.ValuedFlag import LongValuedFlag
|
|
42
|
+
from pyTooling.Stopwatch import Stopwatch
|
|
43
|
+
|
|
44
|
+
from pyEDAA.OutputFilter.Xilinx.Synthesis import Processor, Preamble, WritingSynthesisReport
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Proto(metaclass=ExtendedType, mixin=True):
|
|
48
|
+
@readonly
|
|
49
|
+
def Verbose(self) -> bool:
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
@readonly
|
|
53
|
+
def Width(self) -> int:
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def _PrintHeadline(self):
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def WriteDebug(self, a: str, appendLinebreak: bool = True):
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
def WriteVerbose(self, a: str, appendLinebreak: bool = True):
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
def WriteNormal(self, a: str, appendLinebreak: bool = True):
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
def WriteWarning(self, a: str, appendLinebreak: bool = True):
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
def WriteCritical(self, a: str, appendLinebreak: bool = True):
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def WriteError(self, a: str, appendLinebreak: bool = True):
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
def WriteFatal(self, a: str, appendLinebreak: bool = True, immediateExit: bool = True):
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def Exit(self, i: int = 0) -> NoReturn:
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class VivadoHandlers(Proto, metaclass=ExtendedType, mixin=True):
|
|
85
|
+
@CommandHandler("vivado-synth", help="Parse AMD/Xilinx Vivado Synthesis log files.", description="Parse AMD/Xilinx Vivado Synthesis log files.")
|
|
86
|
+
@LongValuedFlag("--file", dest="logfile", metaName='Synthesis Log', help="Synthesis log file (*.vds).")
|
|
87
|
+
@LongFlag("--info", dest="info", help="Print info messages.")
|
|
88
|
+
@LongFlag("--warning", dest="warning", help="Print warning messages.")
|
|
89
|
+
@LongFlag("--critical", dest="critical", help="Print critical warning messages.")
|
|
90
|
+
@LongFlag("--error", dest="error", help="Print error messages.")
|
|
91
|
+
@LongFlag("--influxdb", dest="influxdb", help="Write statistics as InfluxDB line protocol file (*.line).")
|
|
92
|
+
# @LongValuedFlag("--file", dest="logfile", metaName='Synthesis Log', help="Synthesis log file (*.vds).")
|
|
93
|
+
def HandleVivadoSynthesis(self, args: Namespace) -> None:
|
|
94
|
+
"""Handle program calls with command ``vivado-synth``."""
|
|
95
|
+
self._PrintHeadline()
|
|
96
|
+
|
|
97
|
+
returnCode = 0
|
|
98
|
+
if args.logfile is None:
|
|
99
|
+
self.WriteError(f"Option '--file=<VDS file>' is missing.")
|
|
100
|
+
returnCode = 3
|
|
101
|
+
|
|
102
|
+
logfile = Path(args.logfile)
|
|
103
|
+
if not logfile.exists():
|
|
104
|
+
self.WriteError(f"Vivado synthesis log file '{logfile}' doesn't exist.")
|
|
105
|
+
returnCode = 4
|
|
106
|
+
|
|
107
|
+
if returnCode != 0:
|
|
108
|
+
self.Exit(returnCode)
|
|
109
|
+
|
|
110
|
+
processor = Processor(logfile)
|
|
111
|
+
processor.Parse()
|
|
112
|
+
|
|
113
|
+
if args.info:
|
|
114
|
+
self.WriteNormal(f"INFO messages: {len(processor.InfoMessages)}")
|
|
115
|
+
for message in processor.InfoMessages:
|
|
116
|
+
self.WriteNormal(f" {message}")
|
|
117
|
+
if args.warning:
|
|
118
|
+
self.WriteNormal(f"WARNING messages: {len(processor.WarningMessages)}")
|
|
119
|
+
for message in processor.WarningMessages:
|
|
120
|
+
self.WriteNormal(f" {message}")
|
|
121
|
+
if args.critical:
|
|
122
|
+
self.WriteNormal(f"CRITICAL WARNING: messages {len(processor.CriticalWarningMessages)}")
|
|
123
|
+
for message in processor.CriticalWarningMessages:
|
|
124
|
+
self.WriteNormal(f" {message}")
|
|
125
|
+
if args.error:
|
|
126
|
+
self.WriteNormal(f"ERROR messages: {len(processor.ErrorMessages)}")
|
|
127
|
+
for message in processor.ErrorMessages:
|
|
128
|
+
self.WriteNormal(f" {message}")
|
|
129
|
+
|
|
130
|
+
if args.influxdb:
|
|
131
|
+
influxString = "vivado_synthesis_overview"
|
|
132
|
+
influxString += f",version={processor[Preamble].ToolVersion}"
|
|
133
|
+
influxString += f",branch=main"
|
|
134
|
+
influxString += f",design=Stopwatch"
|
|
135
|
+
influxString += " "
|
|
136
|
+
influxString += f"processing_duration={processor.Duration:.3f}"
|
|
137
|
+
influxString += f",synthesis_duration={processor[WritingSynthesisReport].Duration:.1f}"
|
|
138
|
+
influxString += f",info_count={len(processor.InfoMessages)}u"
|
|
139
|
+
influxString += f",warning_count={len(processor.WarningMessages)}u"
|
|
140
|
+
influxString += f",critical_count={len(processor.CriticalWarningMessages)}u"
|
|
141
|
+
influxString += f",error_count={len(processor.ErrorMessages)}u"
|
|
142
|
+
influxString += f",blackbox_count={len(processor[WritingSynthesisReport].Blackboxes)}u"
|
|
143
|
+
influxString += "\n"
|
|
144
|
+
influxString += "vivado_synthesis_cells"
|
|
145
|
+
influxString += f",version={processor[Preamble].ToolVersion}"
|
|
146
|
+
influxString += f",branch=main"
|
|
147
|
+
influxString += f",design=Stopwatch"
|
|
148
|
+
influxString += " "
|
|
149
|
+
influxString += ",".join(f"{cellName}={cellCount}" for cellName, cellCount in processor[WritingSynthesisReport].Cells.items() if not cellName.endswith("_bbox"))
|
|
150
|
+
|
|
151
|
+
self.WriteNormal(influxString)
|
|
152
|
+
|
|
153
|
+
self.WriteNormal("Summary:")
|
|
154
|
+
self.WriteNormal(f" Processing duration: {processor.Duration:.3f} s")
|
|
155
|
+
self.WriteNormal(f" Info: {len(processor.InfoMessages)} Warning: {len(processor.WarningMessages)} Critical Warning: {len(processor.CriticalWarningMessages)} Error: {len(processor.ErrorMessages)}")
|
|
156
|
+
|
|
157
|
+
self.ExitOnPreviousErrors()
|
|
158
|
+
|
|
159
|
+
@CommandHandler("vivado-impl", help="Parse AMD/Xilinx Vivado Implementation log files.", description="Parse AMD/Xilinx Vivado Implementation log files.")
|
|
160
|
+
@LongValuedFlag("--file", dest="logfile", metaName='Implementation Log', help="Implementation log file (*.vdi).")
|
|
161
|
+
@LongFlag("--info", dest="info", help="Print info messages.")
|
|
162
|
+
@LongFlag("--warning", dest="warning", help="Print warning messages.")
|
|
163
|
+
@LongFlag("--critical", dest="critical", help="Print critical warning messages.")
|
|
164
|
+
@LongFlag("--error", dest="error", help="Print error messages.")
|
|
165
|
+
def HandleVivadoImplementation(self, args: Namespace) -> None:
|
|
166
|
+
"""Handle program calls with command ``vivado-impl``."""
|
|
167
|
+
self._PrintHeadline()
|
|
@@ -0,0 +1,183 @@
|
|
|
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
|
+
"""
|
|
32
|
+
Tools to extract data from log files.
|
|
33
|
+
|
|
34
|
+
.. rubric:: Usage
|
|
35
|
+
|
|
36
|
+
.. code-block::
|
|
37
|
+
|
|
38
|
+
pyedaa-outputfilter vivado-synthesis --file=toplevel.vds \
|
|
39
|
+
--info --info-file=info.log \
|
|
40
|
+
--warning --warning-file=warning.log \
|
|
41
|
+
--critical --critical-file=critical.log \
|
|
42
|
+
--error --error-file=error.log \
|
|
43
|
+
--influxdb-file synthesis.line
|
|
44
|
+
"""
|
|
45
|
+
from typing import NoReturn, Optional as Nullable
|
|
46
|
+
|
|
47
|
+
from argparse import RawDescriptionHelpFormatter, Namespace
|
|
48
|
+
from textwrap import dedent
|
|
49
|
+
|
|
50
|
+
from pyTooling.Decorators import export
|
|
51
|
+
from pyTooling.Attributes.ArgParse import ArgParseHelperMixin, DefaultHandler, CommandHandler
|
|
52
|
+
from pyTooling.Attributes.ArgParse.Argument import StringArgument
|
|
53
|
+
from pyTooling.TerminalUI import TerminalApplication
|
|
54
|
+
|
|
55
|
+
from pyEDAA.OutputFilter import __version__, __copyright__, __license__
|
|
56
|
+
from pyEDAA.OutputFilter import OutputFilterException
|
|
57
|
+
from pyEDAA.OutputFilter.CLI.Vivado import VivadoHandlers
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# todo: merge into application
|
|
61
|
+
# todo: printheadline, printversion (see pyVersioning)
|
|
62
|
+
@export
|
|
63
|
+
class ProgramBase(TerminalApplication):
|
|
64
|
+
"""Base-class for all program classes."""
|
|
65
|
+
|
|
66
|
+
programTitle: str
|
|
67
|
+
|
|
68
|
+
def _PrintHeadline(self) -> None:
|
|
69
|
+
"""Print the program's headline."""
|
|
70
|
+
print("{line}".format(line="=" * 120))
|
|
71
|
+
print("{headline: ^120s}".format(headline=self.programTitle))
|
|
72
|
+
print("{line}".format(line="=" * 120))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@export
|
|
76
|
+
class Application(ProgramBase, VivadoHandlers, ArgParseHelperMixin):
|
|
77
|
+
"""Program class to implement the command line interface (CLI) using commands and options."""
|
|
78
|
+
|
|
79
|
+
programTitle = "pyEDAA.OutputFilter Service Program"
|
|
80
|
+
ISSUE_TRACKER_URL = "https://github.com/edaa-org/pyEDAA.OutputFilter/issues"
|
|
81
|
+
|
|
82
|
+
def __init__(self) -> None:
|
|
83
|
+
super().__init__()
|
|
84
|
+
|
|
85
|
+
# Call the constructor of the ArgParseMixin
|
|
86
|
+
ArgParseHelperMixin.__init__(
|
|
87
|
+
self,
|
|
88
|
+
prog="pyedaa-outputfilter",
|
|
89
|
+
description=dedent('''\
|
|
90
|
+
'pyEDAA.OutputFilter Service Program' to post-process log files from EDA tools.
|
|
91
|
+
'''),
|
|
92
|
+
# epilog=dedent("""\
|
|
93
|
+
# Currently the following inpu/output formats are supported:
|
|
94
|
+
# * JUnit XML (unit test reports - Java oriented format)
|
|
95
|
+
# * Cobertura XML (branch/statement coverage - Java oriented format)
|
|
96
|
+
# """),
|
|
97
|
+
formatter_class=RawDescriptionHelpFormatter,
|
|
98
|
+
add_help=False
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# @CommonSwitchArgumentAttribute("-q", "--quiet", dest="quiet", help="Reduce messages to a minimum.")
|
|
102
|
+
# @CommonSwitchArgumentAttribute("-v", "--verbose", dest="verbose", help="Print out detailed messages.")
|
|
103
|
+
# @CommonSwitchArgumentAttribute("-d", "--debug", dest="debug", help="Enable debug mode.")
|
|
104
|
+
def Run(self) -> None:
|
|
105
|
+
ArgParseHelperMixin.Run(self)
|
|
106
|
+
|
|
107
|
+
@DefaultHandler()
|
|
108
|
+
def HandleDefault(self, _: Namespace) -> None:
|
|
109
|
+
"""Handle program calls without any command."""
|
|
110
|
+
self._PrintHeadline()
|
|
111
|
+
self._PrintHelp()
|
|
112
|
+
|
|
113
|
+
@CommandHandler("help", help="Display help page(s) for the given command name.", description="Display help page(s) for the given command name.")
|
|
114
|
+
@StringArgument(dest="Command", metaName="Command", optional=True, help="Print help page(s) for a command.")
|
|
115
|
+
def HandleHelp(self, args: Namespace) -> None:
|
|
116
|
+
"""Handle program calls with command ``help``."""
|
|
117
|
+
self._PrintHeadline()
|
|
118
|
+
self._PrintHelp(args.Command)
|
|
119
|
+
|
|
120
|
+
@CommandHandler("version", help="Display version information.", description="Display version information.")
|
|
121
|
+
def HandleVersion(self, _: Namespace) -> None:
|
|
122
|
+
"""Handle program calls with command ``version``."""
|
|
123
|
+
self._PrintHeadline()
|
|
124
|
+
self._PrintVersion()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _PrintVersion(self) -> None:
|
|
128
|
+
"""Helper function to print the version information."""
|
|
129
|
+
print(dedent(f"""\
|
|
130
|
+
Copyright: {__copyright__}
|
|
131
|
+
License: {__license__}
|
|
132
|
+
Version: v{__version__}
|
|
133
|
+
""")
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _PrintHelp(self, command: Nullable[str] = None) -> None:
|
|
137
|
+
"""Helper function to print the command line parsers help page(s)."""
|
|
138
|
+
if command is None:
|
|
139
|
+
self.MainParser.print_help()
|
|
140
|
+
elif command == "help":
|
|
141
|
+
print("This is a recursion ...")
|
|
142
|
+
else:
|
|
143
|
+
try:
|
|
144
|
+
self.SubParsers[command].print_help()
|
|
145
|
+
except KeyError:
|
|
146
|
+
print(f"Command {command} is unknown.")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@export
|
|
150
|
+
def main() -> NoReturn:
|
|
151
|
+
"""
|
|
152
|
+
Entrypoint to start program execution.
|
|
153
|
+
|
|
154
|
+
This function should be called either from:
|
|
155
|
+
* ``if __name__ == "__main__":`` or
|
|
156
|
+
* ``console_scripts`` entry point configured via ``setuptools`` in ``setup.py``.
|
|
157
|
+
|
|
158
|
+
This function creates an instance of :class:`Program` in a ``try ... except`` environment. Any exception caught is
|
|
159
|
+
formatted and printed before the program returns with a non-zero exit code.
|
|
160
|
+
"""
|
|
161
|
+
from sys import argv
|
|
162
|
+
|
|
163
|
+
program = Application()
|
|
164
|
+
program.Configure(
|
|
165
|
+
verbose=("-v" in argv or "--verbose" in argv),
|
|
166
|
+
debug=("-d" in argv or "--debug" in argv),
|
|
167
|
+
quiet=("-q" in argv or "--quiet" in argv)
|
|
168
|
+
)
|
|
169
|
+
try:
|
|
170
|
+
program.Run()
|
|
171
|
+
except OutputFilterException as ex:
|
|
172
|
+
program.WriteLineToStdErr(f"{{RED}}[ERROR] {ex}{{NOCOLOR}}".format(**Application.Foreground))
|
|
173
|
+
if ex.__cause__ is not None:
|
|
174
|
+
program.WriteLineToStdErr(f"{{DARK_YELLOW}}Because of: {ex.__cause__}{{NOCOLOR}}".format(**Application.Foreground))
|
|
175
|
+
|
|
176
|
+
except NotImplementedError as ex:
|
|
177
|
+
program.PrintNotImplementedError(ex)
|
|
178
|
+
except Exception as ex:
|
|
179
|
+
program.PrintException(ex)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
main()
|