cocotb 2.0.0rc2__cp39-cp39-macosx_11_0_arm64.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.
Potentially problematic release.
This version of cocotb might be problematic. Click here for more details.
- cocotb/_ANSI.py +65 -0
- cocotb/__init__.py +125 -0
- cocotb/_base_triggers.py +515 -0
- cocotb/_bridge.py +186 -0
- cocotb/_decorators.py +515 -0
- cocotb/_deprecation.py +36 -0
- cocotb/_exceptions.py +7 -0
- cocotb/_extended_awaitables.py +419 -0
- cocotb/_gpi_triggers.py +385 -0
- cocotb/_init.py +301 -0
- cocotb/_outcomes.py +54 -0
- cocotb/_profiling.py +46 -0
- cocotb/_py_compat.py +148 -0
- cocotb/_scheduler.py +448 -0
- cocotb/_test.py +248 -0
- cocotb/_test_factory.py +312 -0
- cocotb/_test_functions.py +42 -0
- cocotb/_typing.py +7 -0
- cocotb/_utils.py +274 -0
- cocotb/_version.py +4 -0
- cocotb/_xunit_reporter.py +66 -0
- cocotb/clock.py +419 -0
- cocotb/debug.py +24 -0
- cocotb/handle.py +1752 -0
- cocotb/libs/libcocotb.so +0 -0
- cocotb/libs/libcocotbfli_modelsim.so +0 -0
- cocotb/libs/libcocotbutils.so +0 -0
- cocotb/libs/libcocotbvhpi_aldec.so +0 -0
- cocotb/libs/libcocotbvhpi_ius.so +0 -0
- cocotb/libs/libcocotbvhpi_modelsim.so +0 -0
- cocotb/libs/libcocotbvhpi_nvc.so +0 -0
- cocotb/libs/libcocotbvpi_aldec.so +0 -0
- cocotb/libs/libcocotbvpi_dsim.so +0 -0
- cocotb/libs/libcocotbvpi_ghdl.so +0 -0
- cocotb/libs/libcocotbvpi_icarus.vpl +0 -0
- cocotb/libs/libcocotbvpi_ius.so +0 -0
- cocotb/libs/libcocotbvpi_modelsim.so +0 -0
- cocotb/libs/libcocotbvpi_vcs.so +0 -0
- cocotb/libs/libcocotbvpi_verilator.so +0 -0
- cocotb/libs/libembed.so +0 -0
- cocotb/libs/libgpi.so +0 -0
- cocotb/libs/libgpilog.so +0 -0
- cocotb/libs/libpygpilog.so +0 -0
- cocotb/logging.py +424 -0
- cocotb/py.typed +0 -0
- cocotb/queue.py +225 -0
- cocotb/regression.py +896 -0
- cocotb/result.py +38 -0
- cocotb/share/def/.gitignore +2 -0
- cocotb/share/def/README.md +4 -0
- cocotb/share/def/aldec.def +61 -0
- cocotb/share/def/ghdl.def +43 -0
- cocotb/share/def/icarus.def +43 -0
- cocotb/share/def/modelsim.def +138 -0
- cocotb/share/include/cocotb_utils.h +70 -0
- cocotb/share/include/embed.h +33 -0
- cocotb/share/include/exports.h +20 -0
- cocotb/share/include/gpi.h +459 -0
- cocotb/share/include/gpi_logging.h +291 -0
- cocotb/share/include/py_gpi_logging.h +33 -0
- cocotb/share/include/vhpi_user_ext.h +26 -0
- cocotb/share/include/vpi_user_ext.h +33 -0
- cocotb/share/lib/verilator/verilator.cpp +209 -0
- cocotb/simtime.py +230 -0
- cocotb/simulator.cpython-39-darwin.so +0 -0
- cocotb/simulator.pyi +107 -0
- cocotb/task.py +590 -0
- cocotb/triggers.py +67 -0
- cocotb/types/__init__.py +31 -0
- cocotb/types/_abstract_array.py +151 -0
- cocotb/types/_array.py +295 -0
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +333 -0
- cocotb/types/_logic_array.py +868 -0
- cocotb/types/_range.py +197 -0
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +110 -0
- cocotb-2.0.0rc2.dist-info/METADATA +60 -0
- cocotb-2.0.0rc2.dist-info/RECORD +115 -0
- cocotb-2.0.0rc2.dist-info/WHEEL +5 -0
- cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
- cocotb-2.0.0rc2.dist-info/licenses/LICENSE +29 -0
- cocotb-2.0.0rc2.dist-info/top_level.txt +23 -0
- cocotb_tools/__init__.py +0 -0
- cocotb_tools/_coverage.py +33 -0
- cocotb_tools/_vendor/__init__.py +3 -0
- cocotb_tools/_vendor/distutils_version.py +346 -0
- cocotb_tools/check_results.py +65 -0
- cocotb_tools/combine_results.py +152 -0
- cocotb_tools/config.py +241 -0
- cocotb_tools/ipython_support.py +99 -0
- cocotb_tools/makefiles/Makefile.deprecations +27 -0
- cocotb_tools/makefiles/Makefile.inc +198 -0
- cocotb_tools/makefiles/Makefile.sim +96 -0
- cocotb_tools/makefiles/simulators/Makefile.activehdl +72 -0
- cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
- cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
- cocotb_tools/makefiles/simulators/Makefile.ghdl +84 -0
- cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
- cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
- cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
- cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
- cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
- cocotb_tools/makefiles/simulators/Makefile.questa-compat +143 -0
- cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
- cocotb_tools/makefiles/simulators/Makefile.riviera +144 -0
- cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
- cocotb_tools/makefiles/simulators/Makefile.verilator +79 -0
- cocotb_tools/makefiles/simulators/Makefile.xcelium +104 -0
- cocotb_tools/py.typed +0 -0
- cocotb_tools/runner.py +1868 -0
- cocotb_tools/sim_versions.py +140 -0
- pygpi/__init__.py +0 -0
- pygpi/entry.py +42 -0
- pygpi/py.typed +0 -0
cocotb/libs/libcocotb.so
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
cocotb/libs/libembed.so
ADDED
|
Binary file
|
cocotb/libs/libgpi.so
ADDED
|
Binary file
|
cocotb/libs/libgpilog.so
ADDED
|
Binary file
|
|
Binary file
|
cocotb/logging.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Copyright (c) 2013, 2018 Potential Ventures Ltd
|
|
3
|
+
# Copyright (c) 2013 SolarFlare Communications Inc
|
|
4
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
5
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Everything related to logging
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import warnings
|
|
17
|
+
from functools import wraps
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Optional, Tuple, Union
|
|
20
|
+
|
|
21
|
+
import cocotb.simtime
|
|
22
|
+
from cocotb import simulator
|
|
23
|
+
from cocotb._ANSI import ANSI
|
|
24
|
+
from cocotb._deprecation import deprecated
|
|
25
|
+
from cocotb.simtime import get_sim_time
|
|
26
|
+
from cocotb.utils import get_time_from_sim_steps
|
|
27
|
+
|
|
28
|
+
__all__ = (
|
|
29
|
+
"ANSI",
|
|
30
|
+
"SimLog",
|
|
31
|
+
"SimLogFormatter",
|
|
32
|
+
"SimTimeContextFilter",
|
|
33
|
+
"default_config",
|
|
34
|
+
"strip_ansi",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
ANSI.__module__ = __name__
|
|
38
|
+
|
|
39
|
+
# Custom log level
|
|
40
|
+
logging.TRACE = 5 # type: ignore[attr-defined] # type checkers don't like adding module attributes after the fact
|
|
41
|
+
logging.addLevelName(5, "TRACE")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
strip_ansi: bool = False
|
|
45
|
+
"""Whether the default formatter should strip ANSI (color) escape codes from log messages.
|
|
46
|
+
|
|
47
|
+
Defaults to ``True`` if ``stdout`` is not a TTY and ``False`` otherwise;
|
|
48
|
+
but can be overridden with the :envvar:`NO_COLOR` or :envvar:`COCOTB_ANSI_OUTPUT` environment variable.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def default_config(
|
|
53
|
+
reduced_log_fmt: bool = True,
|
|
54
|
+
strip_ansi: Optional[bool] = None,
|
|
55
|
+
prefix_format: Optional[str] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Apply the default cocotb log formatting to the root logger.
|
|
58
|
+
|
|
59
|
+
This hooks up the logger to write to stdout, using :class:`SimLogFormatter` for formatting.
|
|
60
|
+
It also adds a :class:`SimTimeContextFilter` filter so that the
|
|
61
|
+
:attr:`~logging.LogRecord.created_sim_time` attribute on :class:`~logging.LogRecord`
|
|
62
|
+
is available to the formatter.
|
|
63
|
+
|
|
64
|
+
If desired, this logging configuration can be overwritten by calling
|
|
65
|
+
``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards),
|
|
66
|
+
or by manually resetting the root logger instance.
|
|
67
|
+
An example of this can be found in the section on :ref:`rotating-logger`.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
reduced_log_fmt:
|
|
71
|
+
If ``True``, use a reduced log format that does not include the
|
|
72
|
+
filename, line number, and function name in the log prefix.
|
|
73
|
+
|
|
74
|
+
.. versionadded:: 2.0
|
|
75
|
+
|
|
76
|
+
strip_ansi:
|
|
77
|
+
If ``True``, strip ANSI (color) escape codes in log messages.
|
|
78
|
+
If ``False``, do not strip ANSI escape codes in log messages.
|
|
79
|
+
If ``None``, use the value of :data:`strip_ansi`.
|
|
80
|
+
|
|
81
|
+
.. versionadded:: 2.0
|
|
82
|
+
|
|
83
|
+
prefix_format:
|
|
84
|
+
An f-string to build a prefix for each log message.
|
|
85
|
+
|
|
86
|
+
.. versionadded:: 2.0
|
|
87
|
+
|
|
88
|
+
.. versionadded:: 1.4
|
|
89
|
+
|
|
90
|
+
.. versionchanged:: 2.0
|
|
91
|
+
Now captures warnings and outputs them through the logging system using
|
|
92
|
+
:func:`logging.captureWarnings`.
|
|
93
|
+
"""
|
|
94
|
+
logging.basicConfig()
|
|
95
|
+
|
|
96
|
+
hdlr = logging.StreamHandler(sys.stdout)
|
|
97
|
+
hdlr.addFilter(SimTimeContextFilter())
|
|
98
|
+
hdlr.setFormatter(
|
|
99
|
+
SimLogFormatter(
|
|
100
|
+
reduced_log_fmt=reduced_log_fmt,
|
|
101
|
+
strip_ansi=strip_ansi,
|
|
102
|
+
prefix_format=prefix_format,
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
logging.getLogger().handlers = [hdlr] # overwrite default handlers
|
|
106
|
+
|
|
107
|
+
logging.getLogger("cocotb").setLevel(logging.INFO)
|
|
108
|
+
logging.getLogger("gpi").setLevel(logging.INFO)
|
|
109
|
+
|
|
110
|
+
logging.captureWarnings(True)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _init() -> None:
|
|
114
|
+
"""cocotb-specific logging setup.
|
|
115
|
+
|
|
116
|
+
- Decides whether ANSI escape code stripping is desired by checking
|
|
117
|
+
:envvar:`NO_COLOR` and :envvar:`COCOTB_ANSI_OUTPUT`.
|
|
118
|
+
- Initializes the GPI logger and sets up the GPI logging optimization.
|
|
119
|
+
- Sets the log level of the ``"cocotb"`` and ``"gpi"`` loggers based on
|
|
120
|
+
:envvar:`COCOTB_LOG_LEVEL` and :envvar:`GPI_LOG_LEVEL`, respectively.
|
|
121
|
+
"""
|
|
122
|
+
global strip_ansi
|
|
123
|
+
strip_ansi = not sys.stdout.isatty() # default to color for TTYs
|
|
124
|
+
if os.getenv("NO_COLOR", ""):
|
|
125
|
+
strip_ansi = True
|
|
126
|
+
ansi_output = os.getenv("COCOTB_ANSI_OUTPUT")
|
|
127
|
+
if ansi_output is not None:
|
|
128
|
+
strip_ansi = not int(ansi_output)
|
|
129
|
+
in_gui = os.getenv("GUI")
|
|
130
|
+
if in_gui is not None:
|
|
131
|
+
strip_ansi = bool(int(in_gui))
|
|
132
|
+
|
|
133
|
+
# Monkeypatch "gpi" logger with function that also sets a PyGPI-local logger level
|
|
134
|
+
# as an optimization.
|
|
135
|
+
gpi_logger = logging.getLogger("gpi")
|
|
136
|
+
old_setLevel = gpi_logger.setLevel
|
|
137
|
+
|
|
138
|
+
@wraps(old_setLevel)
|
|
139
|
+
def setLevel(level: Union[int, str]) -> None:
|
|
140
|
+
old_setLevel(level)
|
|
141
|
+
simulator.set_gpi_log_level(gpi_logger.getEffectiveLevel())
|
|
142
|
+
|
|
143
|
+
gpi_logger.setLevel = setLevel # type: ignore[method-assign]
|
|
144
|
+
|
|
145
|
+
# Initialize PyGPI logging
|
|
146
|
+
simulator.initialize_logger(_log_from_c, logging.getLogger)
|
|
147
|
+
|
|
148
|
+
# Set "cocotb" and "gpi" logger based on environment variables
|
|
149
|
+
def set_level(logger_name: str, envvar: str) -> None:
|
|
150
|
+
log_level = os.environ.get(envvar)
|
|
151
|
+
if log_level is None:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
log_level = log_level.upper()
|
|
155
|
+
|
|
156
|
+
logger = logging.getLogger(logger_name)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
logger.setLevel(log_level)
|
|
160
|
+
except ValueError:
|
|
161
|
+
valid_levels = ", ".join(
|
|
162
|
+
("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE", "NOTSET")
|
|
163
|
+
)
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"Invalid log level {log_level!r} passed through the "
|
|
166
|
+
f"{envvar} environment variable. Valid log "
|
|
167
|
+
f"levels: {valid_levels}"
|
|
168
|
+
) from None
|
|
169
|
+
|
|
170
|
+
set_level("gpi", "GPI_LOG_LEVEL")
|
|
171
|
+
set_level("cocotb", "COCOTB_LOG_LEVEL")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _configure(_: object) -> None:
|
|
175
|
+
"""Configure basic logging."""
|
|
176
|
+
reduced_log_fmt = True
|
|
177
|
+
try:
|
|
178
|
+
reduced_log_fmt = bool(int(os.environ.get("COCOTB_REDUCED_LOG_FMT", "1")))
|
|
179
|
+
except ValueError:
|
|
180
|
+
pass
|
|
181
|
+
prefix_format = os.environ.get("COCOTB_LOG_PREFIX", None)
|
|
182
|
+
default_config(reduced_log_fmt=reduced_log_fmt, prefix_format=prefix_format)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@deprecated('Use `logging.getLogger(f"{name}.0x{ident:x}")` instead')
|
|
186
|
+
def SimLog(name: str, ident: Union[int, None] = None) -> logging.Logger:
|
|
187
|
+
"""Like :func:`logging.getLogger`, but append a numeric identifier to the name.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
name: Logger name.
|
|
191
|
+
ident: Unique integer identifier.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
The Logger named ``{name}.0x{ident:x}``.
|
|
195
|
+
|
|
196
|
+
.. deprecated:: 2.0
|
|
197
|
+
|
|
198
|
+
Use ``logging.getLogger(f"{name}.0x{ident:x}")`` instead.
|
|
199
|
+
"""
|
|
200
|
+
if ident is not None:
|
|
201
|
+
name = f"{name}.0x{ident:x}"
|
|
202
|
+
return logging.getLogger(name)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class SimTimeContextFilter(logging.Filter):
|
|
206
|
+
"""
|
|
207
|
+
A filter to inject simulator times into the log records.
|
|
208
|
+
|
|
209
|
+
This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`
|
|
210
|
+
which adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
|
|
211
|
+
|
|
212
|
+
.. versionadded:: 1.4
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
216
|
+
try:
|
|
217
|
+
record.created_sim_time = get_sim_time()
|
|
218
|
+
except RecursionError:
|
|
219
|
+
# get_sim_time may try to log - if that happens, we can't
|
|
220
|
+
# attach a simulator time to this message.
|
|
221
|
+
record.created_sim_time = None
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class SimLogFormatter(logging.Formatter):
|
|
226
|
+
"""Log formatter to provide consistent log message handling.
|
|
227
|
+
|
|
228
|
+
This will only add simulator timestamps if the handler object this
|
|
229
|
+
formatter is attached to has a :class:`SimTimeContextFilter` filter
|
|
230
|
+
attached, which cocotb ensures by default.
|
|
231
|
+
|
|
232
|
+
See :func:`.default_config` for a description of the arguments.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
loglevel2colour = {
|
|
236
|
+
logging.TRACE: "", # type: ignore[attr-defined] # type checkers don't like adding module attributes after the fact
|
|
237
|
+
logging.DEBUG: "",
|
|
238
|
+
logging.INFO: "",
|
|
239
|
+
logging.WARNING: ANSI.YELLOW_FG,
|
|
240
|
+
logging.ERROR: ANSI.RED_FG,
|
|
241
|
+
logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
prefix_func_globals = {
|
|
245
|
+
"time": time,
|
|
246
|
+
"simtime": cocotb.simtime,
|
|
247
|
+
"ANSI": ANSI,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
def __init__(
|
|
251
|
+
self,
|
|
252
|
+
*,
|
|
253
|
+
reduced_log_fmt: bool = True,
|
|
254
|
+
strip_ansi: Union[bool, None] = None,
|
|
255
|
+
prefix_format: Optional[str] = None,
|
|
256
|
+
) -> None:
|
|
257
|
+
self._reduced_log_fmt = reduced_log_fmt
|
|
258
|
+
self._strip_ansi = strip_ansi
|
|
259
|
+
self._prefix_func = (
|
|
260
|
+
eval(
|
|
261
|
+
f"lambda record: f'''{prefix_format}'''",
|
|
262
|
+
type(self).prefix_func_globals,
|
|
263
|
+
)
|
|
264
|
+
if prefix_format is not None
|
|
265
|
+
else None
|
|
266
|
+
)
|
|
267
|
+
self._ansi_escape_pattern = re.compile(
|
|
268
|
+
r"""
|
|
269
|
+
(?: # either 7-bit C1, two bytes, ESC Fe (omitting CSI)
|
|
270
|
+
\x1B
|
|
271
|
+
[@-Z\\-_]
|
|
272
|
+
| # or a single 8-bit byte Fe (omitting CSI)
|
|
273
|
+
[\x80-\x9A\x9C-\x9F]
|
|
274
|
+
| # or CSI + control codes
|
|
275
|
+
(?: # 7-bit CSI, ESC [
|
|
276
|
+
\x1B\[
|
|
277
|
+
| # 8-bit CSI, 9B
|
|
278
|
+
\x9B
|
|
279
|
+
)
|
|
280
|
+
[0-?]* # Parameter bytes
|
|
281
|
+
[ -/]* # Intermediate bytes
|
|
282
|
+
[@-~] # Final byte
|
|
283
|
+
)
|
|
284
|
+
""",
|
|
285
|
+
re.VERBOSE,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def strip_ansi(self) -> bool:
|
|
289
|
+
return strip_ansi if self._strip_ansi is None else self._strip_ansi
|
|
290
|
+
|
|
291
|
+
# Justify and truncate
|
|
292
|
+
@staticmethod
|
|
293
|
+
def ljust(string: str, chars: int) -> str:
|
|
294
|
+
if len(string) > chars:
|
|
295
|
+
return ".." + string[(chars - 2) * -1 :]
|
|
296
|
+
return string.ljust(chars)
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def rjust(string: str, chars: int) -> str:
|
|
300
|
+
if len(string) > chars:
|
|
301
|
+
return ".." + string[(chars - 2) * -1 :]
|
|
302
|
+
return string.rjust(chars)
|
|
303
|
+
|
|
304
|
+
def formatPrefix(self, record: logging.LogRecord) -> Tuple[str, int]:
|
|
305
|
+
sim_time = getattr(record, "created_sim_time", None)
|
|
306
|
+
if sim_time is None:
|
|
307
|
+
sim_time_str = " -.--ns"
|
|
308
|
+
else:
|
|
309
|
+
time_ns = get_time_from_sim_steps(sim_time, "ns")
|
|
310
|
+
sim_time_str = f"{time_ns:6.2f}ns"
|
|
311
|
+
|
|
312
|
+
if self.strip_ansi():
|
|
313
|
+
highlight_start = ""
|
|
314
|
+
highlight_end = ""
|
|
315
|
+
else:
|
|
316
|
+
highlight_start = self.loglevel2colour.get(record.levelno, "")
|
|
317
|
+
highlight_end = ANSI.DEFAULT
|
|
318
|
+
|
|
319
|
+
if self._prefix_func is not None:
|
|
320
|
+
prefix = self._prefix_func(record)
|
|
321
|
+
stripped_prefix = self._ansi_escape_pattern.sub("", prefix)
|
|
322
|
+
prefix_len = len(stripped_prefix)
|
|
323
|
+
return prefix, prefix_len
|
|
324
|
+
else:
|
|
325
|
+
prefix = f"{sim_time_str:>11} {highlight_start}{record.levelname:<8}{highlight_end} {self.ljust(record.name, 34)} "
|
|
326
|
+
if not self._reduced_log_fmt:
|
|
327
|
+
prefix = f"{prefix}{self.rjust(Path(record.filename).name, 20)}:{record.lineno:<4} in {self.ljust(str(record.funcName), 31)} "
|
|
328
|
+
|
|
329
|
+
prefix_len = len(prefix) - len(highlight_start) - len(highlight_end)
|
|
330
|
+
return prefix, prefix_len
|
|
331
|
+
|
|
332
|
+
def formatMessage(self, record: logging.LogRecord) -> str:
|
|
333
|
+
msg = record.getMessage()
|
|
334
|
+
|
|
335
|
+
if self.strip_ansi():
|
|
336
|
+
highlight_start = ""
|
|
337
|
+
highlight_end = ""
|
|
338
|
+
msg = self._ansi_escape_pattern.sub("", msg)
|
|
339
|
+
else:
|
|
340
|
+
highlight_start = self.loglevel2colour.get(record.levelno, "")
|
|
341
|
+
highlight_end = ANSI.DEFAULT
|
|
342
|
+
|
|
343
|
+
msg = f"{highlight_start}{msg}{highlight_end}"
|
|
344
|
+
|
|
345
|
+
return msg
|
|
346
|
+
|
|
347
|
+
def formatExcInfo(self, record: logging.LogRecord) -> str:
|
|
348
|
+
msg = ""
|
|
349
|
+
|
|
350
|
+
# these lines are copied and updated from the built-in logger
|
|
351
|
+
if record.exc_info:
|
|
352
|
+
# Cache the traceback text to avoid converting it multiple times
|
|
353
|
+
# (it's constant anyway)
|
|
354
|
+
if not record.exc_text:
|
|
355
|
+
record.exc_text = self.formatException(record.exc_info)
|
|
356
|
+
if record.exc_text:
|
|
357
|
+
msg += record.exc_text
|
|
358
|
+
if record.stack_info:
|
|
359
|
+
if not msg.endswith("\n"):
|
|
360
|
+
msg += "\n"
|
|
361
|
+
msg += self.formatStack(record.stack_info)
|
|
362
|
+
|
|
363
|
+
return msg
|
|
364
|
+
|
|
365
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
366
|
+
prefix, prefix_len = self.formatPrefix(record)
|
|
367
|
+
msg = self.formatMessage(record)
|
|
368
|
+
exc_info = self.formatExcInfo(record)
|
|
369
|
+
|
|
370
|
+
lines = msg.splitlines()
|
|
371
|
+
lines.extend(exc_info.splitlines())
|
|
372
|
+
|
|
373
|
+
# add prefix to first line of message
|
|
374
|
+
lines[0] = prefix + lines[0]
|
|
375
|
+
|
|
376
|
+
# add padding to each line of message
|
|
377
|
+
pad = "\n" + " " * prefix_len
|
|
378
|
+
return pad.join(lines)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class SimColourLogFormatter(SimLogFormatter):
|
|
382
|
+
"""Log formatter similar to :class:`SimLogFormatter`, but with colored output by default.
|
|
383
|
+
|
|
384
|
+
.. deprecated:: 2.0
|
|
385
|
+
Use :class:`!SimLogFormatter` with ``strip_ansi=False`` instead.
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
def __init__(
|
|
389
|
+
self,
|
|
390
|
+
*,
|
|
391
|
+
reduced_log_fmt: bool = True,
|
|
392
|
+
strip_ansi: Union[bool, None] = False,
|
|
393
|
+
prefix_format: Optional[str] = None,
|
|
394
|
+
) -> None:
|
|
395
|
+
warnings.warn(
|
|
396
|
+
"SimColourLogFormatter is deprecated and will be removed in a future release. "
|
|
397
|
+
"Use SimLogFormatter with `strip_ansi=False` instead.",
|
|
398
|
+
DeprecationWarning,
|
|
399
|
+
stacklevel=2,
|
|
400
|
+
)
|
|
401
|
+
super().__init__(
|
|
402
|
+
reduced_log_fmt=reduced_log_fmt,
|
|
403
|
+
strip_ansi=strip_ansi,
|
|
404
|
+
prefix_format=prefix_format,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _log_from_c(
|
|
409
|
+
logger: logging.Logger,
|
|
410
|
+
level: int,
|
|
411
|
+
filename: str,
|
|
412
|
+
lineno: int,
|
|
413
|
+
msg: str,
|
|
414
|
+
function_name: str,
|
|
415
|
+
) -> None:
|
|
416
|
+
"""
|
|
417
|
+
This is for use from the C world, and allows us to insert C stack
|
|
418
|
+
information.
|
|
419
|
+
"""
|
|
420
|
+
if logger.isEnabledFor(level):
|
|
421
|
+
record = logger.makeRecord(
|
|
422
|
+
logger.name, level, filename, lineno, msg, (), None, function_name
|
|
423
|
+
)
|
|
424
|
+
logger.handle(record)
|
cocotb/py.typed
ADDED
|
File without changes
|
cocotb/queue.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
import asyncio.queues
|
|
5
|
+
import collections
|
|
6
|
+
import heapq
|
|
7
|
+
from abc import abstractmethod
|
|
8
|
+
from typing import (
|
|
9
|
+
Deque,
|
|
10
|
+
Generic,
|
|
11
|
+
List,
|
|
12
|
+
Tuple,
|
|
13
|
+
TypeVar,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
import cocotb
|
|
17
|
+
from cocotb._utils import pointer_str
|
|
18
|
+
from cocotb.task import Task
|
|
19
|
+
from cocotb.triggers import Event
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"AbstractQueue",
|
|
23
|
+
"LifoQueue",
|
|
24
|
+
"PriorityQueue",
|
|
25
|
+
"Queue",
|
|
26
|
+
"QueueEmpty",
|
|
27
|
+
"QueueFull",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class QueueFull(asyncio.queues.QueueFull):
|
|
32
|
+
"""Raised when the :meth:`Queue.put_nowait()` method is called on a full :class:`Queue`."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class QueueEmpty(asyncio.queues.QueueEmpty):
|
|
36
|
+
"""Raised when the :meth:`Queue.get_nowait()` method is called on an empty :class:`Queue`."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
T = TypeVar("T")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AbstractQueue(Generic[T]):
|
|
43
|
+
"""A queue, useful for coordinating producer and consumer coroutines.
|
|
44
|
+
|
|
45
|
+
If *maxsize* is less than or equal to 0, the queue size is infinite. If it
|
|
46
|
+
is an integer greater than 0, then :meth:`put` will block when the queue
|
|
47
|
+
reaches *maxsize*, until an item is removed by :meth:`get`.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, maxsize: int = 0) -> None:
|
|
51
|
+
self._maxsize: int = maxsize
|
|
52
|
+
self._getters: Deque[Tuple[Event, Task[object]]] = collections.deque()
|
|
53
|
+
self._putters: Deque[Tuple[Event, Task[object]]] = collections.deque()
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def _get(self) -> T:
|
|
57
|
+
"""Remove and return the next element from the queue."""
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def _put(self, item: T) -> None:
|
|
61
|
+
"""Place a new element on the queue."""
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def _size(self) -> int:
|
|
65
|
+
"""Return the number of elements in the queue."""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def _repr(self) -> str:
|
|
69
|
+
"""Return a string representation of the state of the queue."""
|
|
70
|
+
|
|
71
|
+
def _wakeup_next(self, waiters: Deque[Tuple[Event, Task[object]]]) -> None:
|
|
72
|
+
while waiters:
|
|
73
|
+
event, task = waiters.popleft()
|
|
74
|
+
if not task.done():
|
|
75
|
+
event.set()
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
def __repr__(self) -> str:
|
|
79
|
+
return f"<{type(self).__name__} {self._format()} at {pointer_str(self)}>"
|
|
80
|
+
|
|
81
|
+
def __str__(self) -> str:
|
|
82
|
+
return f"<{type(self).__name__} {self._format()}>"
|
|
83
|
+
|
|
84
|
+
def _format(self) -> str:
|
|
85
|
+
result = f"maxsize={self._maxsize!r}"
|
|
86
|
+
if getattr(self, "_queue", None):
|
|
87
|
+
result += f" _queue={self._repr()}"
|
|
88
|
+
if self._getters:
|
|
89
|
+
result += f" _getters[{len(self._getters)}]"
|
|
90
|
+
if self._putters:
|
|
91
|
+
result += f" _putters[{len(self._putters)}]"
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
def qsize(self) -> int:
|
|
95
|
+
"""Number of items in the queue."""
|
|
96
|
+
return self._size()
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def maxsize(self) -> int:
|
|
100
|
+
"""Number of items allowed in the queue."""
|
|
101
|
+
return self._maxsize
|
|
102
|
+
|
|
103
|
+
def empty(self) -> bool:
|
|
104
|
+
"""Return ``True`` if the queue is empty, ``False`` otherwise."""
|
|
105
|
+
return self._size() == 0
|
|
106
|
+
|
|
107
|
+
def full(self) -> bool:
|
|
108
|
+
"""Return ``True`` if there are :meth:`maxsize` items in the queue.
|
|
109
|
+
|
|
110
|
+
.. note::
|
|
111
|
+
If the Queue was initialized with ``maxsize=0`` (the default), then
|
|
112
|
+
:meth:`full` is never ``True``.
|
|
113
|
+
"""
|
|
114
|
+
if self._maxsize <= 0:
|
|
115
|
+
return False
|
|
116
|
+
else:
|
|
117
|
+
return self.qsize() >= self._maxsize
|
|
118
|
+
|
|
119
|
+
async def put(self, item: T) -> None:
|
|
120
|
+
"""Put an *item* into the queue.
|
|
121
|
+
|
|
122
|
+
If the queue is full, wait until a free
|
|
123
|
+
slot is available before adding the item.
|
|
124
|
+
"""
|
|
125
|
+
while self.full():
|
|
126
|
+
event = Event()
|
|
127
|
+
self._putters.append((event, cocotb.task.current_task()))
|
|
128
|
+
await event.wait()
|
|
129
|
+
self.put_nowait(item)
|
|
130
|
+
|
|
131
|
+
def put_nowait(self, item: T) -> None:
|
|
132
|
+
"""Put an *item* into the queue without blocking.
|
|
133
|
+
|
|
134
|
+
If no free slot is immediately available, raise :exc:`~cocotb.queue.QueueFull`.
|
|
135
|
+
"""
|
|
136
|
+
if self.full():
|
|
137
|
+
raise QueueFull()
|
|
138
|
+
self._put(item)
|
|
139
|
+
self._wakeup_next(self._getters)
|
|
140
|
+
|
|
141
|
+
async def get(self) -> T:
|
|
142
|
+
"""Remove and return an item from the queue.
|
|
143
|
+
|
|
144
|
+
If the queue is empty, wait until an item is available.
|
|
145
|
+
"""
|
|
146
|
+
while self.empty():
|
|
147
|
+
event = Event()
|
|
148
|
+
self._getters.append((event, cocotb.task.current_task()))
|
|
149
|
+
await event.wait()
|
|
150
|
+
return self.get_nowait()
|
|
151
|
+
|
|
152
|
+
def get_nowait(self) -> T:
|
|
153
|
+
"""Remove and return an item from the queue.
|
|
154
|
+
|
|
155
|
+
Return an item if one is immediately available, else raise
|
|
156
|
+
:exc:`~cocotb.queue.QueueEmpty`.
|
|
157
|
+
"""
|
|
158
|
+
if self.empty():
|
|
159
|
+
raise QueueEmpty()
|
|
160
|
+
item = self._get()
|
|
161
|
+
self._wakeup_next(self._putters)
|
|
162
|
+
return item
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Queue(AbstractQueue[T]):
|
|
166
|
+
"""A subclass of :class:`AbstractQueue`; retrieves oldest entries first (FIFO)."""
|
|
167
|
+
|
|
168
|
+
def __init__(self, maxsize: int = 0) -> None:
|
|
169
|
+
super().__init__(maxsize)
|
|
170
|
+
self._queue: Deque[T] = collections.deque()
|
|
171
|
+
|
|
172
|
+
def _put(self, item: T) -> None:
|
|
173
|
+
self._queue.append(item)
|
|
174
|
+
|
|
175
|
+
def _get(self) -> T:
|
|
176
|
+
return self._queue.popleft()
|
|
177
|
+
|
|
178
|
+
def _size(self) -> int:
|
|
179
|
+
return len(self._queue)
|
|
180
|
+
|
|
181
|
+
def _repr(self) -> str:
|
|
182
|
+
return repr(self._queue)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class PriorityQueue(AbstractQueue[T]):
|
|
186
|
+
r"""A subclass of :class:`AbstractQueue`; retrieves entries in priority order (smallest item first).
|
|
187
|
+
|
|
188
|
+
Entries are typically tuples of the form ``(priority number, data)``.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(self, maxsize: int = 0) -> None:
|
|
192
|
+
super().__init__(maxsize)
|
|
193
|
+
self._queue: List[T] = []
|
|
194
|
+
|
|
195
|
+
def _put(self, item: T) -> None:
|
|
196
|
+
heapq.heappush(self._queue, item)
|
|
197
|
+
|
|
198
|
+
def _get(self) -> T:
|
|
199
|
+
return heapq.heappop(self._queue)
|
|
200
|
+
|
|
201
|
+
def _size(self) -> int:
|
|
202
|
+
return len(self._queue)
|
|
203
|
+
|
|
204
|
+
def _repr(self) -> str:
|
|
205
|
+
return repr(self._queue)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class LifoQueue(AbstractQueue[T]):
|
|
209
|
+
"""A subclass of :class:`AbstractQueue`; retrieves most recently added entries first (LIFO)."""
|
|
210
|
+
|
|
211
|
+
def __init__(self, maxsize: int = 0) -> None:
|
|
212
|
+
super().__init__(maxsize)
|
|
213
|
+
self._queue: Deque[T] = collections.deque()
|
|
214
|
+
|
|
215
|
+
def _put(self, item: T) -> None:
|
|
216
|
+
self._queue.append(item)
|
|
217
|
+
|
|
218
|
+
def _get(self) -> T:
|
|
219
|
+
return self._queue.pop()
|
|
220
|
+
|
|
221
|
+
def _size(self) -> int:
|
|
222
|
+
return len(self._queue)
|
|
223
|
+
|
|
224
|
+
def _repr(self) -> str:
|
|
225
|
+
return repr(self._queue)
|