cocotb 2.0.1__cp38-cp38-win32.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.
- cocotb/_ANSI.py +65 -0
- cocotb/__init__.py +127 -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 +150 -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 +103 -0
- cocotb/clock.py +419 -0
- cocotb/debug.py +24 -0
- cocotb/handle.py +1752 -0
- cocotb/libs/cocotb.dll +0 -0
- cocotb/libs/cocotb.exp +0 -0
- cocotb/libs/cocotb.lib +0 -0
- cocotb/libs/cocotbfli_modelsim.dll +0 -0
- cocotb/libs/cocotbfli_modelsim.exp +0 -0
- cocotb/libs/cocotbfli_modelsim.lib +0 -0
- cocotb/libs/cocotbutils.dll +0 -0
- cocotb/libs/cocotbutils.exp +0 -0
- cocotb/libs/cocotbutils.lib +0 -0
- cocotb/libs/cocotbvhpi_aldec.dll +0 -0
- cocotb/libs/cocotbvhpi_aldec.exp +0 -0
- cocotb/libs/cocotbvhpi_aldec.lib +0 -0
- cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
- cocotb/libs/cocotbvhpi_nvc.dll +0 -0
- cocotb/libs/cocotbvhpi_nvc.exp +0 -0
- cocotb/libs/cocotbvhpi_nvc.lib +0 -0
- cocotb/libs/cocotbvpi_aldec.dll +0 -0
- cocotb/libs/cocotbvpi_aldec.exp +0 -0
- cocotb/libs/cocotbvpi_aldec.lib +0 -0
- cocotb/libs/cocotbvpi_ghdl.dll +0 -0
- cocotb/libs/cocotbvpi_ghdl.exp +0 -0
- cocotb/libs/cocotbvpi_ghdl.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.exp +0 -0
- cocotb/libs/cocotbvpi_icarus.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.vpl +0 -0
- cocotb/libs/cocotbvpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvpi_modelsim.lib +0 -0
- cocotb/libs/embed.dll +0 -0
- cocotb/libs/embed.exp +0 -0
- cocotb/libs/embed.lib +0 -0
- cocotb/libs/gpi.dll +0 -0
- cocotb/libs/gpi.exp +0 -0
- cocotb/libs/gpi.lib +0 -0
- cocotb/libs/gpilog.dll +0 -0
- cocotb/libs/gpilog.exp +0 -0
- cocotb/libs/gpilog.lib +0 -0
- cocotb/libs/pygpilog.dll +0 -0
- cocotb/libs/pygpilog.exp +0 -0
- cocotb/libs/pygpilog.lib +0 -0
- cocotb/logging.py +417 -0
- cocotb/py.typed +0 -0
- cocotb/queue.py +235 -0
- cocotb/regression.py +900 -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/aldec.exp +0 -0
- cocotb/share/def/aldec.lib +0 -0
- cocotb/share/def/ghdl.def +43 -0
- cocotb/share/def/ghdl.exp +0 -0
- cocotb/share/def/ghdl.lib +0 -0
- cocotb/share/def/icarus.def +43 -0
- cocotb/share/def/icarus.exp +0 -0
- cocotb/share/def/icarus.lib +0 -0
- cocotb/share/def/modelsim.def +138 -0
- cocotb/share/def/modelsim.exp +0 -0
- cocotb/share/def/modelsim.lib +0 -0
- cocotb/share/def/nvcvhpi.def +18 -0
- cocotb/share/def/nvcvhpi.exp +0 -0
- cocotb/share/def/nvcvhpi.lib +0 -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 +238 -0
- cocotb/simulator.cp38-win32.exp +0 -0
- cocotb/simulator.cp38-win32.lib +0 -0
- cocotb/simulator.cp38-win32.pyd +0 -0
- cocotb/simulator.cp38-win32.pyd.2.config +8 -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 +297 -0
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +333 -0
- cocotb/types/_logic_array.py +884 -0
- cocotb/types/_range.py +197 -0
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +110 -0
- cocotb-2.0.1.dist-info/LICENSE +29 -0
- cocotb-2.0.1.dist-info/METADATA +44 -0
- cocotb-2.0.1.dist-info/RECORD +152 -0
- cocotb-2.0.1.dist-info/WHEEL +5 -0
- cocotb-2.0.1.dist-info/entry_points.txt +2 -0
- cocotb-2.0.1.dist-info/top_level.txt +18 -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 +242 -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 +2001 -0
- cocotb_tools/sim_versions.py +140 -0
- pygpi/__init__.py +0 -0
- pygpi/entry.py +42 -0
- pygpi/py.typed +0 -0
cocotb/_ANSI.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
from cocotb._py_compat import StrEnum
|
|
8
|
+
|
|
9
|
+
_ESCAPE = "\033["
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ANSI(StrEnum):
|
|
13
|
+
"""ANSI escape codes for coloring output.
|
|
14
|
+
|
|
15
|
+
The color names supported are ``[BRIGHT_]{BLACK|RED|GREEN|YELLOW|BLUE|MAGENTA|CYAN|WHITE}{_FG|_BG}``.
|
|
16
|
+
|
|
17
|
+
Variables that end in ``_FG`` will color the character or symbol ("foreground")
|
|
18
|
+
and variables that end in ``_BG`` will color the background.
|
|
19
|
+
|
|
20
|
+
Foreground and background colors can be combined together with a ``+``.
|
|
21
|
+
Setting a new foreground color will override the previous foreground, likewise with background colors.
|
|
22
|
+
|
|
23
|
+
Use ``DEFAULT_FG`` and ``DEFAULT_BG`` to reset the coloring to the default colors for the foreground and background, respectively.
|
|
24
|
+
Or use ``DEFAULT`` to reset both.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
DEFAULT_FG = _ESCAPE + "39m"
|
|
28
|
+
DEFAULT_BG = _ESCAPE + "49m"
|
|
29
|
+
DEFAULT = DEFAULT_BG + DEFAULT_FG
|
|
30
|
+
|
|
31
|
+
BLACK_FG = _ESCAPE + "30m"
|
|
32
|
+
RED_FG = _ESCAPE + "31m"
|
|
33
|
+
GREEN_FG = _ESCAPE + "32m"
|
|
34
|
+
YELLOW_FG = _ESCAPE + "33m"
|
|
35
|
+
BLUE_FG = _ESCAPE + "34m"
|
|
36
|
+
MAGENTA_FG = _ESCAPE + "35m"
|
|
37
|
+
CYAN_FG = _ESCAPE + "36m"
|
|
38
|
+
WHITE_FG = _ESCAPE + "37m"
|
|
39
|
+
|
|
40
|
+
BLACK_BG = _ESCAPE + "40m"
|
|
41
|
+
RED_BG = _ESCAPE + "41m"
|
|
42
|
+
GREEN_BG = _ESCAPE + "42m"
|
|
43
|
+
YELLOW_BG = _ESCAPE + "43m"
|
|
44
|
+
BLUE_BG = _ESCAPE + "44m"
|
|
45
|
+
MAGENTA_BG = _ESCAPE + "45m"
|
|
46
|
+
CYAN_BG = _ESCAPE + "46m"
|
|
47
|
+
WHITE_BG = _ESCAPE + "47m"
|
|
48
|
+
|
|
49
|
+
BRIGHT_BLACK_FG = _ESCAPE + "90m"
|
|
50
|
+
BRIGHT_RED_FG = _ESCAPE + "91m"
|
|
51
|
+
BRIGHT_GREEN_FG = _ESCAPE + "92m"
|
|
52
|
+
BRIGHT_YELLOW_FG = _ESCAPE + "93m"
|
|
53
|
+
BRIGHT_BLUE_FG = _ESCAPE + "94m"
|
|
54
|
+
BRIGHT_MAGENTA_FG = _ESCAPE + "95m"
|
|
55
|
+
BRIGHT_CYAN_FG = _ESCAPE + "96m"
|
|
56
|
+
BRIGHT_WHITE_FG = _ESCAPE + "97m"
|
|
57
|
+
|
|
58
|
+
BRIGHT_BLACK_BG = _ESCAPE + "100m"
|
|
59
|
+
BRIGHT_RED_BG = _ESCAPE + "101m"
|
|
60
|
+
BRIGHT_GREEN_BG = _ESCAPE + "102m"
|
|
61
|
+
BRIGHT_YELLOW_BG = _ESCAPE + "103m"
|
|
62
|
+
BRIGHT_BLUE_BG = _ESCAPE + "104m"
|
|
63
|
+
BRIGHT_MAGENTA_BG = _ESCAPE + "105m"
|
|
64
|
+
BRIGHT_CYAN_BG = _ESCAPE + "106m"
|
|
65
|
+
BRIGHT_WHITE_BG = _ESCAPE + "107m"
|
cocotb/__init__.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Copyright (c) 2013 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
|
+
import sys
|
|
7
|
+
from typing import TYPE_CHECKING, Dict, List, Union
|
|
8
|
+
|
|
9
|
+
from cocotb._decorators import (
|
|
10
|
+
parametrize,
|
|
11
|
+
test,
|
|
12
|
+
)
|
|
13
|
+
from cocotb._test import create_task, start, start_soon
|
|
14
|
+
from cocotb._test_functions import pass_test
|
|
15
|
+
|
|
16
|
+
from ._version import __version__ as _version
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from logging import Logger
|
|
20
|
+
from types import SimpleNamespace
|
|
21
|
+
|
|
22
|
+
from cocotb._scheduler import Scheduler
|
|
23
|
+
from cocotb.handle import SimHandleBase
|
|
24
|
+
from cocotb.regression import RegressionManager
|
|
25
|
+
|
|
26
|
+
__all__ = (
|
|
27
|
+
"RANDOM_SEED",
|
|
28
|
+
"SIM_NAME",
|
|
29
|
+
"SIM_VERSION",
|
|
30
|
+
"__version__",
|
|
31
|
+
"argv",
|
|
32
|
+
"create_task",
|
|
33
|
+
"is_simulation",
|
|
34
|
+
"log",
|
|
35
|
+
"packages",
|
|
36
|
+
"parametrize",
|
|
37
|
+
"pass_test",
|
|
38
|
+
"plusargs",
|
|
39
|
+
"start",
|
|
40
|
+
"start_soon",
|
|
41
|
+
"test",
|
|
42
|
+
"top",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Set __module__ on re-exports
|
|
46
|
+
test.__module__ = __name__
|
|
47
|
+
start_soon.__module__ = __name__
|
|
48
|
+
start.__module__ = __name__
|
|
49
|
+
create_task.__module__ = __name__
|
|
50
|
+
parametrize.__module__ = __name__
|
|
51
|
+
pass_test.__module__ = __name__
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__version__: str = _version
|
|
55
|
+
"""The version of cocotb."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
log: "Logger"
|
|
59
|
+
"""An easily accessible :class:`~logging.Logger` for the user.
|
|
60
|
+
|
|
61
|
+
This logger defaults to the :data:`logging.INFO` log level.
|
|
62
|
+
|
|
63
|
+
.. versionchanged:: 2.0
|
|
64
|
+
This was previously the ``"cocotb"`` Logger.
|
|
65
|
+
It is now a Logger under the ``"test"`` namespace.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
_scheduler_inst: "Scheduler"
|
|
69
|
+
"""The global scheduler instance."""
|
|
70
|
+
|
|
71
|
+
_regression_manager: "RegressionManager"
|
|
72
|
+
"""The global regression manager instance."""
|
|
73
|
+
|
|
74
|
+
argv: List[str]
|
|
75
|
+
"""The argument list as seen by the simulator."""
|
|
76
|
+
|
|
77
|
+
plusargs: Dict[str, Union[bool, str]]
|
|
78
|
+
"""A dictionary of "plusargs" handed to the simulation.
|
|
79
|
+
|
|
80
|
+
See :envvar:`COCOTB_PLUSARGS` for details.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
packages: "SimpleNamespace"
|
|
84
|
+
"""A :class:`python:types.SimpleNamespace` of package handles.
|
|
85
|
+
|
|
86
|
+
This will be populated with handles at test time if packages can be discovered
|
|
87
|
+
via the GPI.
|
|
88
|
+
|
|
89
|
+
.. versionadded:: 2.0
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
SIM_NAME: str
|
|
93
|
+
"""The product information of the running simulator."""
|
|
94
|
+
|
|
95
|
+
SIM_VERSION: str
|
|
96
|
+
"""The version of the running simulator."""
|
|
97
|
+
|
|
98
|
+
RANDOM_SEED: int
|
|
99
|
+
"""The last value used to seed the global PRNG.
|
|
100
|
+
|
|
101
|
+
During test collection, this is set to the value provided by :envvar:`COCOTB_RANDOM_SEED`,
|
|
102
|
+
if given, or a random value based on the state of the operating system.
|
|
103
|
+
|
|
104
|
+
During test run, this is set to a new value computed by combining the value used during test collection,
|
|
105
|
+
with the full test name (e.g. ``my_test_module.my_test``).
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
top: "SimHandleBase"
|
|
109
|
+
r"""
|
|
110
|
+
A handle to the :envvar:`COCOTB_TOPLEVEL` entity/module.
|
|
111
|
+
|
|
112
|
+
This is equivalent to the :term:`DUT` parameter given to cocotb tests, so it can be used wherever that variable can be used.
|
|
113
|
+
It is particularly useful for extracting information about the :term:`DUT` in module-level class and function definitions;
|
|
114
|
+
and in parameters to :class:`.TestFactory`\ s.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
is_simulation: bool = False
|
|
118
|
+
"""``True`` if cocotb was loaded in a simulation."""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if sys.version_info < (3, 9):
|
|
122
|
+
import warnings
|
|
123
|
+
|
|
124
|
+
warnings.warn(
|
|
125
|
+
"Support for Python versions < 3.9 will be dropped in version 2.1",
|
|
126
|
+
FutureWarning,
|
|
127
|
+
)
|
cocotb/_base_triggers.py
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Copyright (c) 2013 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
|
+
"""A collection of triggers which a testbench can :keyword:`await`."""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import warnings
|
|
11
|
+
from typing import (
|
|
12
|
+
AsyncContextManager,
|
|
13
|
+
Callable,
|
|
14
|
+
Generator,
|
|
15
|
+
List,
|
|
16
|
+
Optional,
|
|
17
|
+
Union,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from cocotb._deprecation import deprecated
|
|
21
|
+
from cocotb._py_compat import Self, cached_property
|
|
22
|
+
from cocotb._utils import pointer_str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Trigger:
|
|
26
|
+
"""A future event that a Task can wait upon."""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
self._primed = False
|
|
30
|
+
|
|
31
|
+
@cached_property
|
|
32
|
+
def _log(self) -> logging.Logger:
|
|
33
|
+
return logging.getLogger(f"cocotb.{type(self).__qualname__}.0x{id(self):x}")
|
|
34
|
+
|
|
35
|
+
def _prime(self, callback: Callable[["Self"], None]) -> None:
|
|
36
|
+
"""Set a callback to be invoked when the trigger fires.
|
|
37
|
+
|
|
38
|
+
The callback will be invoked with a single argument, `self`.
|
|
39
|
+
|
|
40
|
+
Sub-classes must override this, but should end by calling the base class
|
|
41
|
+
method.
|
|
42
|
+
|
|
43
|
+
.. warning::
|
|
44
|
+
Do not call this directly within a :term:`task`. It is intended to be used
|
|
45
|
+
only by the scheduler.
|
|
46
|
+
"""
|
|
47
|
+
# Set _primed so the trigger can test if it's already been primed and behave appropriately.
|
|
48
|
+
self._primed = True
|
|
49
|
+
|
|
50
|
+
def _unprime(self) -> None:
|
|
51
|
+
"""Remove the callback, and perform cleanup if necessary.
|
|
52
|
+
|
|
53
|
+
After being un-primed, a Trigger may be re-primed again in the future.
|
|
54
|
+
Calling `_unprime` multiple times is allowed, subsequent calls should be
|
|
55
|
+
a no-op.
|
|
56
|
+
|
|
57
|
+
Sub-classes may override this, but should end by calling the base class
|
|
58
|
+
method.
|
|
59
|
+
|
|
60
|
+
.. warning::
|
|
61
|
+
Do not call this directly within a :term:`task`. It is intended to be used
|
|
62
|
+
only by the scheduler.
|
|
63
|
+
"""
|
|
64
|
+
self._cleanup()
|
|
65
|
+
|
|
66
|
+
def _cleanup(self) -> None:
|
|
67
|
+
# Clear _primed so this Trigger can be re-primed.
|
|
68
|
+
self._primed = False
|
|
69
|
+
|
|
70
|
+
def __await__(self) -> Generator["Self", None, "Self"]:
|
|
71
|
+
yield self
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class _Event(Trigger):
|
|
76
|
+
"""Unique instance used by the Event object.
|
|
77
|
+
|
|
78
|
+
One created for each attempt to wait on the event so that the scheduler
|
|
79
|
+
can maintain a unique mapping of triggers to tasks.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
_callback: Callable[["_Event"], None]
|
|
83
|
+
|
|
84
|
+
def __init__(self, parent: "Event") -> None:
|
|
85
|
+
super().__init__()
|
|
86
|
+
self._parent = parent
|
|
87
|
+
|
|
88
|
+
def _prime(self, callback: Callable[["_Event"], None]) -> None:
|
|
89
|
+
if self._primed:
|
|
90
|
+
return
|
|
91
|
+
if self._parent.is_set():
|
|
92
|
+
# If the event is already set, we need to call the callback
|
|
93
|
+
# immediately, so we don't need to wait for the scheduler.
|
|
94
|
+
callback(self)
|
|
95
|
+
return
|
|
96
|
+
self._callback = callback
|
|
97
|
+
return super()._prime(callback)
|
|
98
|
+
|
|
99
|
+
def _unprime(self) -> None:
|
|
100
|
+
if not self._primed:
|
|
101
|
+
return
|
|
102
|
+
return super()._unprime()
|
|
103
|
+
|
|
104
|
+
def _set(self) -> None:
|
|
105
|
+
if self._primed:
|
|
106
|
+
self._callback(self)
|
|
107
|
+
|
|
108
|
+
def __repr__(self) -> str:
|
|
109
|
+
return f"<{self._parent!r}.wait() at {pointer_str(self)}>"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Event:
|
|
113
|
+
r"""A way to signal an event across :class:`~cocotb.task.Task`\ s.
|
|
114
|
+
|
|
115
|
+
:keyword:`await`\ ing the result of :meth:`wait()` will block the :keyword:`await`\ ing :class:`~cocotb.task.Task`
|
|
116
|
+
until :meth:`set` is called.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
name: Name for the Event.
|
|
120
|
+
|
|
121
|
+
Usage:
|
|
122
|
+
.. code-block:: python
|
|
123
|
+
|
|
124
|
+
e = Event()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def task1():
|
|
128
|
+
await e.wait()
|
|
129
|
+
print("resuming!")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
cocotb.start_soon(task1())
|
|
133
|
+
# do stuff
|
|
134
|
+
e.set()
|
|
135
|
+
await NullTrigger() # allows task1 to execute
|
|
136
|
+
# resuming!
|
|
137
|
+
|
|
138
|
+
.. versionremoved:: 2.0
|
|
139
|
+
|
|
140
|
+
Removed the undocumented *data* attribute and argument to :meth:`set`,
|
|
141
|
+
and the *name* attribute and argument to the constructor.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(self, name: Optional[str] = None) -> None:
|
|
145
|
+
self._event: _Event = _Event(self)
|
|
146
|
+
self._name: Union[str, None] = None
|
|
147
|
+
if name is not None:
|
|
148
|
+
warnings.warn(
|
|
149
|
+
"The 'name' argument will be removed in a future release.",
|
|
150
|
+
DeprecationWarning,
|
|
151
|
+
stacklevel=2,
|
|
152
|
+
)
|
|
153
|
+
self.name = name
|
|
154
|
+
self._fired: bool = False
|
|
155
|
+
self._data: object = None
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
@deprecated("The 'name' field will be removed in a future release.")
|
|
159
|
+
def name(self) -> Union[str, None]:
|
|
160
|
+
"""Name of the Event.
|
|
161
|
+
|
|
162
|
+
.. deprecated:: 2.0
|
|
163
|
+
The *name* field will be removed in a future release.
|
|
164
|
+
"""
|
|
165
|
+
return self._name
|
|
166
|
+
|
|
167
|
+
@name.setter
|
|
168
|
+
@deprecated("The 'name' field will be removed in a future release.")
|
|
169
|
+
def name(self, new_name: Union[str, None]) -> None:
|
|
170
|
+
self._name = new_name
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
@deprecated("The data field will be removed in a future release.")
|
|
174
|
+
def data(self) -> object:
|
|
175
|
+
"""The data associated with the Event.
|
|
176
|
+
|
|
177
|
+
.. deprecated:: 2.0
|
|
178
|
+
The data field will be removed in a future release.
|
|
179
|
+
Use a separate variable to store the data instead.
|
|
180
|
+
"""
|
|
181
|
+
return self._data
|
|
182
|
+
|
|
183
|
+
@data.setter
|
|
184
|
+
@deprecated("The data field will be removed in a future release.")
|
|
185
|
+
def data(self, new_data: object) -> None:
|
|
186
|
+
self._data = new_data
|
|
187
|
+
|
|
188
|
+
def set(self, data: Optional[object] = None) -> None:
|
|
189
|
+
"""Set the Event and unblock all Tasks blocked on this Event."""
|
|
190
|
+
self._fired = True
|
|
191
|
+
if data is not None:
|
|
192
|
+
warnings.warn(
|
|
193
|
+
"The data field will be removed in a future release.",
|
|
194
|
+
DeprecationWarning,
|
|
195
|
+
stacklevel=2,
|
|
196
|
+
)
|
|
197
|
+
self._data = data
|
|
198
|
+
self._event._set()
|
|
199
|
+
|
|
200
|
+
def wait(self) -> Trigger:
|
|
201
|
+
"""Block the current Task until the Event is set.
|
|
202
|
+
|
|
203
|
+
If the event has already been set, the trigger will fire immediately.
|
|
204
|
+
|
|
205
|
+
To set the Event call :meth:`set`.
|
|
206
|
+
To reset the Event (and enable the use of :meth:`wait` again),
|
|
207
|
+
call :meth:`clear`.
|
|
208
|
+
"""
|
|
209
|
+
return self._event
|
|
210
|
+
|
|
211
|
+
def clear(self) -> None:
|
|
212
|
+
"""Clear this event that has been set.
|
|
213
|
+
|
|
214
|
+
Subsequent calls to :meth:`~cocotb.triggers.Event.wait` will block until
|
|
215
|
+
:meth:`~cocotb.triggers.Event.set` is called again.
|
|
216
|
+
"""
|
|
217
|
+
self._fired = False
|
|
218
|
+
|
|
219
|
+
def is_set(self) -> bool:
|
|
220
|
+
"""Return ``True`` if event has been set."""
|
|
221
|
+
return self._fired
|
|
222
|
+
|
|
223
|
+
def __repr__(self) -> str:
|
|
224
|
+
if self._name is None:
|
|
225
|
+
fmt = "<{0} at {2}>"
|
|
226
|
+
else:
|
|
227
|
+
fmt = "<{0} for {1} at {2}>"
|
|
228
|
+
return fmt.format(type(self).__qualname__, self._name, pointer_str(self))
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class _InternalEvent(Trigger):
|
|
232
|
+
"""Event used internally for triggers that need cross-:class:`~cocotb.task.Task` synchronization.
|
|
233
|
+
|
|
234
|
+
This Event can only be waited on once, by a single :class:`~cocotb.task.Task`.
|
|
235
|
+
|
|
236
|
+
Provides transparent :func`repr` pass-through to the :class:`Trigger` using this event,
|
|
237
|
+
providing a better debugging experience.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
def __init__(self, parent: object) -> None:
|
|
241
|
+
super().__init__()
|
|
242
|
+
self._parent = parent
|
|
243
|
+
self._callback: Optional[Callable[[_InternalEvent], None]] = None
|
|
244
|
+
self.fired: bool = False
|
|
245
|
+
|
|
246
|
+
def _prime(self, callback: Callable[["_InternalEvent"], None]) -> None:
|
|
247
|
+
if self._primed:
|
|
248
|
+
raise RuntimeError("This Trigger may only be awaited once")
|
|
249
|
+
self._callback = callback
|
|
250
|
+
super()._prime(callback)
|
|
251
|
+
if self.fired:
|
|
252
|
+
self._callback(self)
|
|
253
|
+
|
|
254
|
+
def _cleanup(self) -> None:
|
|
255
|
+
# Don't clear _primed so a second call to _prime() fails.
|
|
256
|
+
pass
|
|
257
|
+
|
|
258
|
+
def set(self) -> None:
|
|
259
|
+
"""Wake up coroutine blocked on this event."""
|
|
260
|
+
self.fired = True
|
|
261
|
+
|
|
262
|
+
if self._callback is not None:
|
|
263
|
+
self._callback(self)
|
|
264
|
+
|
|
265
|
+
def is_set(self) -> bool:
|
|
266
|
+
"""Return true if event has been set."""
|
|
267
|
+
return self.fired
|
|
268
|
+
|
|
269
|
+
def __await__(
|
|
270
|
+
self,
|
|
271
|
+
) -> Generator["Self", None, "Self"]:
|
|
272
|
+
if self._primed:
|
|
273
|
+
raise RuntimeError("Only one Task may await this Trigger")
|
|
274
|
+
yield self
|
|
275
|
+
return self
|
|
276
|
+
|
|
277
|
+
def __repr__(self) -> str:
|
|
278
|
+
return repr(self._parent)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class _Lock(Trigger):
|
|
282
|
+
"""Unique instance used by the Lock object.
|
|
283
|
+
|
|
284
|
+
One created for each attempt to acquire the Lock so that the scheduler
|
|
285
|
+
can maintain a unique mapping of triggers to tasks.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def __init__(self, parent: "Lock") -> None:
|
|
289
|
+
super().__init__()
|
|
290
|
+
self._parent = parent
|
|
291
|
+
|
|
292
|
+
def _prime(self, callback: Callable[["Self"], None]) -> None:
|
|
293
|
+
if self._primed:
|
|
294
|
+
raise RuntimeError(
|
|
295
|
+
"Lock.acquire() result can only be used by one task at a time"
|
|
296
|
+
)
|
|
297
|
+
self._callback = callback
|
|
298
|
+
self._parent._prime_lock(self)
|
|
299
|
+
return super()._prime(callback)
|
|
300
|
+
|
|
301
|
+
def _unprime(self) -> None:
|
|
302
|
+
if not self._primed:
|
|
303
|
+
return
|
|
304
|
+
self._parent._unprime_lock(self)
|
|
305
|
+
return super()._unprime()
|
|
306
|
+
|
|
307
|
+
def __repr__(self) -> str:
|
|
308
|
+
return f"<{self._parent!r}.acquire() at {pointer_str(self)}>"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class Lock(AsyncContextManager[None]):
|
|
312
|
+
"""A mutual exclusion lock.
|
|
313
|
+
|
|
314
|
+
Guarantees fair scheduling.
|
|
315
|
+
Lock acquisition is given in order of attempted lock acquisition.
|
|
316
|
+
|
|
317
|
+
Usage:
|
|
318
|
+
By directly calling :meth:`acquire` and :meth:`release`.
|
|
319
|
+
|
|
320
|
+
.. code-block:: python
|
|
321
|
+
|
|
322
|
+
lock = Lock()
|
|
323
|
+
...
|
|
324
|
+
await lock.acquire()
|
|
325
|
+
try:
|
|
326
|
+
# do some stuff
|
|
327
|
+
...
|
|
328
|
+
finally:
|
|
329
|
+
lock.release()
|
|
330
|
+
|
|
331
|
+
Or...
|
|
332
|
+
|
|
333
|
+
.. code-block:: python
|
|
334
|
+
|
|
335
|
+
async with Lock():
|
|
336
|
+
# do some stuff
|
|
337
|
+
...
|
|
338
|
+
|
|
339
|
+
.. versionchanged:: 1.4
|
|
340
|
+
|
|
341
|
+
The lock can be used as an asynchronous context manager in an
|
|
342
|
+
:keyword:`async with` statement
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def __init__(self, name: Optional[str] = None) -> None:
|
|
346
|
+
self._pending_primed: List[_Lock] = []
|
|
347
|
+
self._name: Union[str, None] = None
|
|
348
|
+
if name is not None:
|
|
349
|
+
warnings.warn(
|
|
350
|
+
"The 'name' argument will be removed in a future release.",
|
|
351
|
+
DeprecationWarning,
|
|
352
|
+
stacklevel=2,
|
|
353
|
+
)
|
|
354
|
+
self._name = name
|
|
355
|
+
self._locked: bool = False
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
@deprecated("The 'name' field will be removed in a future release.")
|
|
359
|
+
def name(self) -> Union[str, None]:
|
|
360
|
+
"""Name of the Lock.
|
|
361
|
+
|
|
362
|
+
.. deprecated:: 2.0
|
|
363
|
+
The *name* field will be removed in a future release.
|
|
364
|
+
"""
|
|
365
|
+
return self._name
|
|
366
|
+
|
|
367
|
+
@name.setter
|
|
368
|
+
@deprecated("The 'name' field will be removed in a future release.")
|
|
369
|
+
def name(self, new_name: Union[str, None]) -> None:
|
|
370
|
+
self._name = new_name
|
|
371
|
+
|
|
372
|
+
def locked(self) -> bool:
|
|
373
|
+
"""Return ``True`` if the lock has been acquired.
|
|
374
|
+
|
|
375
|
+
.. versionchanged:: 2.0
|
|
376
|
+
This is now a method to match :meth:`asyncio.Lock.locked`, rather than an attribute.
|
|
377
|
+
"""
|
|
378
|
+
return self._locked
|
|
379
|
+
|
|
380
|
+
def _acquire_and_fire(self, lock: _Lock) -> None:
|
|
381
|
+
self._locked = True
|
|
382
|
+
lock._callback(lock)
|
|
383
|
+
|
|
384
|
+
def _prime_lock(self, lock: _Lock) -> None:
|
|
385
|
+
if not self._locked:
|
|
386
|
+
self._acquire_and_fire(lock)
|
|
387
|
+
else:
|
|
388
|
+
self._pending_primed.append(lock)
|
|
389
|
+
|
|
390
|
+
def _unprime_lock(self, lock: _Lock) -> None:
|
|
391
|
+
if lock in self._pending_primed:
|
|
392
|
+
self._pending_primed.remove(lock)
|
|
393
|
+
|
|
394
|
+
def acquire(self) -> Trigger:
|
|
395
|
+
"""Produce a trigger which fires when the lock is acquired."""
|
|
396
|
+
trig = _Lock(self)
|
|
397
|
+
return trig
|
|
398
|
+
|
|
399
|
+
def release(self) -> None:
|
|
400
|
+
"""Release the lock."""
|
|
401
|
+
if not self._locked:
|
|
402
|
+
raise RuntimeError(f"Attempt to release an unacquired Lock {self!s}")
|
|
403
|
+
|
|
404
|
+
self._locked = False
|
|
405
|
+
|
|
406
|
+
# nobody waiting for this lock
|
|
407
|
+
if not self._pending_primed:
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
lock = self._pending_primed.pop(0)
|
|
411
|
+
self._acquire_and_fire(lock)
|
|
412
|
+
|
|
413
|
+
def __repr__(self) -> str:
|
|
414
|
+
if self._name is None:
|
|
415
|
+
fmt = "<{0} [{2} waiting] at {3}>"
|
|
416
|
+
else:
|
|
417
|
+
fmt = "<{0} for {1} [{2} waiting] at {3}>"
|
|
418
|
+
return fmt.format(
|
|
419
|
+
type(self).__qualname__,
|
|
420
|
+
self._name,
|
|
421
|
+
len(self._pending_primed),
|
|
422
|
+
pointer_str(self),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
async def __aenter__(self) -> None:
|
|
426
|
+
await self.acquire()
|
|
427
|
+
|
|
428
|
+
async def __aexit__(self, *args: object) -> None:
|
|
429
|
+
self.release()
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
class NullTrigger(Trigger):
|
|
433
|
+
"""Trigger that fires immediately.
|
|
434
|
+
|
|
435
|
+
Mostly useful when building or using higher-order functions which need to take or return Triggers.
|
|
436
|
+
|
|
437
|
+
The scheduling order of the Task awaiting this Trigger with respect to any other Task is not deterministic
|
|
438
|
+
and should generally not be relied upon.
|
|
439
|
+
Instead of using this Trigger to push the Task until "after" another Task has run,
|
|
440
|
+
use other synchronization techniques, such as using an :class:`.Event`.
|
|
441
|
+
|
|
442
|
+
**Do not** do this:
|
|
443
|
+
|
|
444
|
+
.. code-block:: python
|
|
445
|
+
:class: removed
|
|
446
|
+
|
|
447
|
+
transaction_data = None
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def monitor(dut):
|
|
451
|
+
while dut.valid.value != 1 and dut.ready.value != 1:
|
|
452
|
+
await RisingEdge(dut.clk)
|
|
453
|
+
transaction_data = dut.data.value
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def use_transaction(dut):
|
|
457
|
+
while True:
|
|
458
|
+
await RisingEdge(dut.clk)
|
|
459
|
+
# We need the NullTrigger here because both Tasks react to RisingEdge,
|
|
460
|
+
# but there's no guarantee about which Task is run first,
|
|
461
|
+
# so we need to force this one to run "later" using NullTrigger.
|
|
462
|
+
await NullTrigger()
|
|
463
|
+
if transaction_data is not None:
|
|
464
|
+
process(transaction_data)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
use_task = cocotb.start_soon(use_transaction(cocotb.top))
|
|
468
|
+
monitor_task = cocotb.start_soon(monitor(cocotb.top))
|
|
469
|
+
|
|
470
|
+
Instead use an :class:`!Event` to explicitly synchronize the two Tasks, like so:
|
|
471
|
+
|
|
472
|
+
.. code-block:: python
|
|
473
|
+
:class: new
|
|
474
|
+
|
|
475
|
+
transaction_data = None
|
|
476
|
+
transaction_event = Event()
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def monitor(dut):
|
|
480
|
+
while dut.valid.value != 1 and dut.ready.value != 1:
|
|
481
|
+
await RisingEdge(dut.clk)
|
|
482
|
+
transaction_data = dut.data.value
|
|
483
|
+
transaction_event.set()
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def use_transaction(dut):
|
|
487
|
+
# Now we don't need the NullTrigger.
|
|
488
|
+
# This Task will wake up *strictly* after `monitor_task` sets the transaction.
|
|
489
|
+
await transaction_event.wait()
|
|
490
|
+
process(transaction_data)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
use_task = cocotb.start_soon(use_transaction(cocotb.top))
|
|
494
|
+
monitor_task = cocotb.start_soon(monitor(cocotb.top))
|
|
495
|
+
|
|
496
|
+
.. versionremoved:: 2.0
|
|
497
|
+
The *outcome* parameter was removed. There is no alternative.
|
|
498
|
+
"""
|
|
499
|
+
|
|
500
|
+
def __init__(self, name: Optional[str] = None) -> None:
|
|
501
|
+
super().__init__()
|
|
502
|
+
self.name = name
|
|
503
|
+
|
|
504
|
+
def _prime(self, callback: Callable[["Self"], None]) -> None:
|
|
505
|
+
if self._primed:
|
|
506
|
+
return
|
|
507
|
+
callback(self)
|
|
508
|
+
return super()._prime(callback)
|
|
509
|
+
|
|
510
|
+
def __repr__(self) -> str:
|
|
511
|
+
if self.name is None:
|
|
512
|
+
fmt = "<{0} at {2}>"
|
|
513
|
+
else:
|
|
514
|
+
fmt = "<{0} for {1} at {2}>"
|
|
515
|
+
return fmt.format(type(self).__qualname__, self.name, pointer_str(self))
|