cocotb 2.0.0b1__cp311-cp311-macosx_10_9_x86_64.whl → 2.0.0rc2__cp311-cp311-macosx_10_9_x86_64.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 +47 -54
- cocotb/__init__.py +12 -2
- cocotb/_base_triggers.py +11 -9
- cocotb/_bridge.py +8 -9
- cocotb/_extended_awaitables.py +1 -1
- cocotb/_gpi_triggers.py +4 -1
- cocotb/_init.py +17 -11
- cocotb/_py_compat.py +24 -10
- cocotb/_scheduler.py +25 -31
- cocotb/_test.py +6 -3
- cocotb/_test_factory.py +4 -1
- cocotb/_utils.py +1 -23
- cocotb/_version.py +1 -1
- cocotb/clock.py +98 -16
- cocotb/debug.py +24 -0
- cocotb/handle.py +62 -32
- cocotb/libs/libcocotbfli_modelsim.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/libgpi.so +0 -0
- cocotb/libs/libgpilog.so +0 -0
- cocotb/logging.py +243 -117
- cocotb/regression.py +43 -35
- cocotb/share/include/cocotb_utils.h +3 -3
- cocotb/share/include/embed.h +2 -2
- cocotb/share/include/gpi.h +258 -109
- cocotb/share/include/py_gpi_logging.h +3 -3
- cocotb/share/lib/verilator/verilator.cpp +23 -15
- cocotb/simtime.py +230 -0
- cocotb/simulator.cpython-311-darwin.so +0 -0
- cocotb/task.py +54 -11
- cocotb/types/__init__.py +4 -1
- cocotb/types/_array.py +33 -2
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +96 -59
- cocotb/types/_logic_array.py +56 -22
- cocotb/types/_range.py +12 -5
- cocotb/utils.py +9 -129
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/METADATA +1 -1
- cocotb-2.0.0rc2.dist-info/RECORD +115 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/licenses/LICENSE +1 -0
- cocotb_tools/config.py +5 -5
- cocotb_tools/makefiles/Makefile.inc +3 -9
- cocotb_tools/makefiles/Makefile.sim +9 -1
- cocotb_tools/makefiles/simulators/Makefile.vcs +1 -1
- cocotb_tools/runner.py +94 -59
- pygpi/entry.py +5 -6
- cocotb-2.0.0b1.dist-info/RECORD +0 -112
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +0 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/entry_points.txt +0 -0
- {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +0 -0
cocotb/_ANSI.py
CHANGED
|
@@ -4,69 +4,62 @@
|
|
|
4
4
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
5
5
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
Some constants for doing ANSI stuff.
|
|
9
|
-
"""
|
|
7
|
+
from cocotb._py_compat import StrEnum
|
|
10
8
|
|
|
11
|
-
# flake8: noqa (skip this file for flake8: pypi.python.org/pypi/flake8)
|
|
12
9
|
_ESCAPE = "\033["
|
|
13
10
|
|
|
14
|
-
# see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
DEFAULT = DEFAULT_BG + DEFAULT_FG
|
|
12
|
+
class ANSI(StrEnum):
|
|
13
|
+
"""ANSI escape codes for coloring output.
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
RED_FG = _ESCAPE + "31m"
|
|
22
|
-
GREEN_FG = _ESCAPE + "32m"
|
|
23
|
-
YELLOW_FG = _ESCAPE + "33m"
|
|
24
|
-
BLUE_FG = _ESCAPE + "34m"
|
|
25
|
-
MAGENTA_FG = _ESCAPE + "35m"
|
|
26
|
-
CYAN_FG = _ESCAPE + "36m"
|
|
27
|
-
WHITE_FG = _ESCAPE + "37m"
|
|
15
|
+
The color names supported are ``[BRIGHT_]{BLACK|RED|GREEN|YELLOW|BLUE|MAGENTA|CYAN|WHITE}{_FG|_BG}``.
|
|
28
16
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
GREEN_BG = _ESCAPE + "42m"
|
|
32
|
-
YELLOW_BG = _ESCAPE + "43m"
|
|
33
|
-
BLUE_BG = _ESCAPE + "44m"
|
|
34
|
-
MAGENTA_BG = _ESCAPE + "45m"
|
|
35
|
-
CYAN_BG = _ESCAPE + "46m"
|
|
36
|
-
WHITE_BG = _ESCAPE + "47m"
|
|
17
|
+
Variables that end in ``_FG`` will color the character or symbol ("foreground")
|
|
18
|
+
and variables that end in ``_BG`` will color the background.
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
BRIGHT_GREEN_FG = _ESCAPE + "92m"
|
|
41
|
-
BRIGHT_YELLOW_FG = _ESCAPE + "93m"
|
|
42
|
-
BRIGHT_BLUE_FG = _ESCAPE + "94m"
|
|
43
|
-
BRIGHT_MAGENTA_FG = _ESCAPE + "95m"
|
|
44
|
-
BRIGHT_CYAN_FG = _ESCAPE + "96m"
|
|
45
|
-
BRIGHT_WHITE_FG = _ESCAPE + "97m"
|
|
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.
|
|
46
22
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
BRIGHT_YELLOW_BG = _ESCAPE + "103m"
|
|
51
|
-
BRIGHT_BLUE_BG = _ESCAPE + "104m"
|
|
52
|
-
BRIGHT_MAGENTA_BG = _ESCAPE + "105m"
|
|
53
|
-
BRIGHT_CYAN_BG = _ESCAPE + "106m"
|
|
54
|
-
BRIGHT_WHITE_BG = _ESCAPE + "107m"
|
|
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
|
+
"""
|
|
55
26
|
|
|
27
|
+
DEFAULT_FG = _ESCAPE + "39m"
|
|
28
|
+
DEFAULT_BG = _ESCAPE + "49m"
|
|
29
|
+
DEFAULT = DEFAULT_BG + DEFAULT_FG
|
|
56
30
|
|
|
57
|
-
|
|
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"
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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"
|
|
66
48
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Copyright (c) 2013 SolarFlare Communications Inc
|
|
4
4
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
5
5
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
6
|
+
import sys
|
|
6
7
|
from typing import TYPE_CHECKING, Dict, List, Union
|
|
7
8
|
|
|
8
9
|
from cocotb._decorators import (
|
|
@@ -50,7 +51,7 @@ parametrize.__module__ = __name__
|
|
|
50
51
|
pass_test.__module__ = __name__
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
__version__ = _version
|
|
54
|
+
__version__: str = _version
|
|
54
55
|
"""The version of cocotb."""
|
|
55
56
|
|
|
56
57
|
|
|
@@ -76,7 +77,7 @@ argv: List[str]
|
|
|
76
77
|
plusargs: Dict[str, Union[bool, str]]
|
|
77
78
|
"""A dictionary of "plusargs" handed to the simulation.
|
|
78
79
|
|
|
79
|
-
See :
|
|
80
|
+
See :envvar:`COCOTB_PLUSARGS` for details.
|
|
80
81
|
"""
|
|
81
82
|
|
|
82
83
|
packages: "SimpleNamespace"
|
|
@@ -113,3 +114,12 @@ and in parameters to :class:`.TestFactory`\ s.
|
|
|
113
114
|
|
|
114
115
|
is_simulation: bool = False
|
|
115
116
|
"""``True`` if cocotb was loaded in a simulation."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if sys.version_info < (3, 8):
|
|
120
|
+
import warnings
|
|
121
|
+
|
|
122
|
+
warnings.warn(
|
|
123
|
+
"Support for Python versions < 3.8 will be dropped in version 2.1",
|
|
124
|
+
FutureWarning,
|
|
125
|
+
)
|
cocotb/_base_triggers.py
CHANGED
|
@@ -442,17 +442,18 @@ class NullTrigger(Trigger):
|
|
|
442
442
|
**Do not** do this:
|
|
443
443
|
|
|
444
444
|
.. code-block:: python
|
|
445
|
+
:class: removed
|
|
445
446
|
|
|
446
447
|
transaction_data = None
|
|
447
448
|
|
|
448
449
|
|
|
449
|
-
def monitor():
|
|
450
|
+
def monitor(dut):
|
|
450
451
|
while dut.valid.value != 1 and dut.ready.value != 1:
|
|
451
452
|
await RisingEdge(dut.clk)
|
|
452
453
|
transaction_data = dut.data.value
|
|
453
454
|
|
|
454
455
|
|
|
455
|
-
def use_transaction():
|
|
456
|
+
def use_transaction(dut):
|
|
456
457
|
while True:
|
|
457
458
|
await RisingEdge(dut.clk)
|
|
458
459
|
# We need the NullTrigger here because both Tasks react to RisingEdge,
|
|
@@ -463,33 +464,34 @@ class NullTrigger(Trigger):
|
|
|
463
464
|
process(transaction_data)
|
|
464
465
|
|
|
465
466
|
|
|
466
|
-
use_task = cocotb.start_soon(use_transaction())
|
|
467
|
-
monitor_task = cocotb.start_soon(monitor())
|
|
467
|
+
use_task = cocotb.start_soon(use_transaction(cocotb.top))
|
|
468
|
+
monitor_task = cocotb.start_soon(monitor(cocotb.top))
|
|
468
469
|
|
|
469
|
-
Instead use an :class
|
|
470
|
+
Instead use an :class:`!Event` to explicitly synchronize the two Tasks, like so:
|
|
470
471
|
|
|
471
472
|
.. code-block:: python
|
|
473
|
+
:class: new
|
|
472
474
|
|
|
473
475
|
transaction_data = None
|
|
474
476
|
transaction_event = Event()
|
|
475
477
|
|
|
476
478
|
|
|
477
|
-
def monitor():
|
|
479
|
+
def monitor(dut):
|
|
478
480
|
while dut.valid.value != 1 and dut.ready.value != 1:
|
|
479
481
|
await RisingEdge(dut.clk)
|
|
480
482
|
transaction_data = dut.data.value
|
|
481
483
|
transaction_event.set()
|
|
482
484
|
|
|
483
485
|
|
|
484
|
-
def use_transaction():
|
|
486
|
+
def use_transaction(dut):
|
|
485
487
|
# Now we don't need the NullTrigger.
|
|
486
488
|
# This Task will wake up *strictly* after `monitor_task` sets the transaction.
|
|
487
489
|
await transaction_event.wait()
|
|
488
490
|
process(transaction_data)
|
|
489
491
|
|
|
490
492
|
|
|
491
|
-
use_task = cocotb.start_soon(use_transaction())
|
|
492
|
-
monitor_task = cocotb.start_soon(monitor())
|
|
493
|
+
use_task = cocotb.start_soon(use_transaction(cocotb.top))
|
|
494
|
+
monitor_task = cocotb.start_soon(monitor(cocotb.top))
|
|
493
495
|
|
|
494
496
|
.. versionremoved:: 2.0
|
|
495
497
|
The *outcome* parameter was removed. There is no alternative.
|
cocotb/_bridge.py
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
4
|
import functools
|
|
5
5
|
import logging
|
|
6
|
-
import os
|
|
7
6
|
import threading
|
|
8
7
|
from enum import IntEnum
|
|
9
8
|
from typing import (
|
|
9
|
+
TYPE_CHECKING,
|
|
10
10
|
Callable,
|
|
11
11
|
Coroutine,
|
|
12
12
|
Generic,
|
|
@@ -15,14 +15,13 @@ from typing import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
import cocotb
|
|
18
|
+
from cocotb import debug
|
|
18
19
|
from cocotb._base_triggers import Event, Trigger
|
|
19
20
|
from cocotb._exceptions import InternalError
|
|
20
|
-
from cocotb._outcomes import Outcome
|
|
21
21
|
from cocotb._py_compat import ParamSpec
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
_debug = "COCOTB_SCHEDULER_DEBUG" in os.environ
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from cocotb._outcomes import Outcome
|
|
26
25
|
|
|
27
26
|
P = ParamSpec("P")
|
|
28
27
|
|
|
@@ -133,7 +132,7 @@ class external_waiter(Generic[Result]):
|
|
|
133
132
|
|
|
134
133
|
def _propagate_state(self, new_state: external_state) -> None:
|
|
135
134
|
with self.cond:
|
|
136
|
-
if
|
|
135
|
+
if debug.debug:
|
|
137
136
|
self._log.debug(
|
|
138
137
|
f"Changing state from {self.state} -> {new_state} from {threading.current_thread()}"
|
|
139
138
|
)
|
|
@@ -141,7 +140,7 @@ class external_waiter(Generic[Result]):
|
|
|
141
140
|
self.cond.notify()
|
|
142
141
|
|
|
143
142
|
def thread_done(self) -> None:
|
|
144
|
-
if
|
|
143
|
+
if debug.debug:
|
|
145
144
|
self._log.debug(f"Thread finished from {threading.current_thread()}")
|
|
146
145
|
self._propagate_state(external_state.EXITED)
|
|
147
146
|
|
|
@@ -160,7 +159,7 @@ class external_waiter(Generic[Result]):
|
|
|
160
159
|
self._propagate_state(external_state.RUNNING)
|
|
161
160
|
|
|
162
161
|
def thread_wait(self) -> external_state:
|
|
163
|
-
if
|
|
162
|
+
if debug.debug:
|
|
164
163
|
self._log.debug(
|
|
165
164
|
f"Waiting for the condition lock {threading.current_thread()}"
|
|
166
165
|
)
|
|
@@ -169,7 +168,7 @@ class external_waiter(Generic[Result]):
|
|
|
169
168
|
while self.state == external_state.RUNNING:
|
|
170
169
|
self.cond.wait()
|
|
171
170
|
|
|
172
|
-
if
|
|
171
|
+
if debug.debug:
|
|
173
172
|
if self.state == external_state.EXITED:
|
|
174
173
|
self._log.debug(
|
|
175
174
|
f"Thread {self.thread} has exited from {threading.current_thread()}"
|
cocotb/_extended_awaitables.py
CHANGED
|
@@ -206,7 +206,7 @@ class First(_AggregateWaitable[object]):
|
|
|
206
206
|
class ClockCycles(Waitable["ClockCycles"]):
|
|
207
207
|
r"""Finishes after *num_cycles* transitions of *signal*.
|
|
208
208
|
|
|
209
|
-
:keyword:`await`\ ing this Trigger returns the
|
|
209
|
+
:keyword:`await`\ ing this Trigger returns the :class:`!ClockCycles` object.
|
|
210
210
|
|
|
211
211
|
Args:
|
|
212
212
|
signal: The signal to monitor.
|
cocotb/_gpi_triggers.py
CHANGED
|
@@ -10,6 +10,7 @@ import warnings
|
|
|
10
10
|
from decimal import Decimal
|
|
11
11
|
from fractions import Fraction
|
|
12
12
|
from typing import (
|
|
13
|
+
TYPE_CHECKING,
|
|
13
14
|
Any,
|
|
14
15
|
Callable,
|
|
15
16
|
ClassVar,
|
|
@@ -24,11 +25,13 @@ import cocotb.handle
|
|
|
24
25
|
from cocotb import simulator
|
|
25
26
|
from cocotb._base_triggers import Trigger
|
|
26
27
|
from cocotb._deprecation import deprecated
|
|
27
|
-
from cocotb._py_compat import Self
|
|
28
28
|
from cocotb._typing import RoundMode, TimeUnit
|
|
29
29
|
from cocotb._utils import pointer_str, singleton
|
|
30
30
|
from cocotb.utils import get_sim_steps, get_time_from_sim_steps
|
|
31
31
|
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from cocotb._py_compat import Self
|
|
34
|
+
|
|
32
35
|
|
|
33
36
|
class GPITrigger(Trigger):
|
|
34
37
|
"""A trigger for a simulation event."""
|
cocotb/_init.py
CHANGED
|
@@ -17,6 +17,8 @@ from typing import Callable, List, cast
|
|
|
17
17
|
import cocotb
|
|
18
18
|
import cocotb._profiling
|
|
19
19
|
import cocotb.handle
|
|
20
|
+
import cocotb.logging
|
|
21
|
+
import cocotb.simtime
|
|
20
22
|
import cocotb.simulator
|
|
21
23
|
from cocotb._scheduler import Scheduler
|
|
22
24
|
from cocotb.regression import RegressionManager, RegressionMode
|
|
@@ -62,6 +64,7 @@ def init_package_from_simulation(argv: List[str]) -> None:
|
|
|
62
64
|
# Add it back because users expect to be able to import files in their test directory.
|
|
63
65
|
sys.path.insert(0, "")
|
|
64
66
|
|
|
67
|
+
cocotb.logging._init()
|
|
65
68
|
_setup_logging()
|
|
66
69
|
|
|
67
70
|
# From https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners
|
|
@@ -84,6 +87,8 @@ def init_package_from_simulation(argv: List[str]) -> None:
|
|
|
84
87
|
_setup_root_handle()
|
|
85
88
|
_start_user_coverage()
|
|
86
89
|
|
|
90
|
+
cocotb.simtime._init()
|
|
91
|
+
|
|
87
92
|
log.info(
|
|
88
93
|
"Initialized cocotb v%s from %s",
|
|
89
94
|
cocotb.__version__,
|
|
@@ -171,19 +176,11 @@ def _start_user_coverage() -> None:
|
|
|
171
176
|
"Coverage collection requested but coverage module not available. Install it using `pip install coverage`."
|
|
172
177
|
) from None
|
|
173
178
|
else:
|
|
174
|
-
config_filepath = os.getenv("
|
|
175
|
-
if config_filepath is None:
|
|
176
|
-
config_filepath = os.getenv("COVERAGE_RCFILE")
|
|
177
|
-
if config_filepath is not None:
|
|
178
|
-
warnings.warn(
|
|
179
|
-
"COVERAGE_RCFILE is deprecated in favor of COCOTB_COVERAGE_RCFILE",
|
|
180
|
-
DeprecationWarning,
|
|
181
|
-
stacklevel=2,
|
|
182
|
-
)
|
|
179
|
+
config_filepath = os.getenv("COVERAGE_RCFILE")
|
|
183
180
|
if config_filepath is None:
|
|
184
181
|
# Exclude cocotb itself from coverage collection.
|
|
185
182
|
log.info(
|
|
186
|
-
"Collecting coverage of user code. No coverage config file supplied via
|
|
183
|
+
"Collecting coverage of user code. No coverage config file supplied via COVERAGE_RCFILE."
|
|
187
184
|
)
|
|
188
185
|
cocotb_package_dir = Path(__file__).parent.absolute()
|
|
189
186
|
user_coverage = coverage.coverage(
|
|
@@ -213,10 +210,14 @@ def _setup_random_seed() -> None:
|
|
|
213
210
|
warnings.warn(
|
|
214
211
|
"RANDOM_SEED is deprecated in favor of COCOTB_RANDOM_SEED",
|
|
215
212
|
DeprecationWarning,
|
|
216
|
-
stacklevel=2,
|
|
217
213
|
)
|
|
218
214
|
if seed_envvar is None:
|
|
219
215
|
if "ntb_random_seed" in cocotb.plusargs:
|
|
216
|
+
warnings.warn(
|
|
217
|
+
"Passing +ntb_random_seed will not be used to seed Python's random number generator in the future. "
|
|
218
|
+
"Ensure you also set `COCOTB_RANDOM_SEED`.",
|
|
219
|
+
FutureWarning,
|
|
220
|
+
)
|
|
220
221
|
plusarg_seed = cocotb.plusargs["ntb_random_seed"]
|
|
221
222
|
if not isinstance(plusarg_seed, str):
|
|
222
223
|
raise TypeError("ntb_random_seed plusarg is not a valid seed value.")
|
|
@@ -225,6 +226,11 @@ def _setup_random_seed() -> None:
|
|
|
225
226
|
raise TypeError("ntb_random_seed plusargs is not a valid seed value.")
|
|
226
227
|
cocotb.RANDOM_SEED = seed
|
|
227
228
|
elif "seed" in cocotb.plusargs:
|
|
229
|
+
warnings.warn(
|
|
230
|
+
"Passing +seed will not be used to seed Python's random number generator in the future. "
|
|
231
|
+
"Ensure you also set `COCOTB_RANDOM_SEED`.",
|
|
232
|
+
FutureWarning,
|
|
233
|
+
)
|
|
228
234
|
plusarg_seed = cocotb.plusargs["seed"]
|
|
229
235
|
if not isinstance(plusarg_seed, str):
|
|
230
236
|
raise TypeError("seed plusarg is not a valid seed value.")
|
cocotb/_py_compat.py
CHANGED
|
@@ -20,6 +20,7 @@ __all__ = (
|
|
|
20
20
|
"ParamSpec",
|
|
21
21
|
"Protocol",
|
|
22
22
|
"Self",
|
|
23
|
+
"StrEnum",
|
|
23
24
|
"TypeAlias",
|
|
24
25
|
"cached_property",
|
|
25
26
|
"insertion_ordered_dict",
|
|
@@ -98,14 +99,15 @@ else:
|
|
|
98
99
|
return res
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
class FakeGetItemType:
|
|
102
|
-
def __class_getitem__(self, a: object) -> str:
|
|
103
|
-
return ""
|
|
104
|
-
|
|
105
|
-
|
|
106
102
|
if sys.version_info >= (3, 8):
|
|
107
103
|
from typing import Final, Literal, Protocol
|
|
108
104
|
else:
|
|
105
|
+
from typing import Any
|
|
106
|
+
|
|
107
|
+
class FakeGetItemType:
|
|
108
|
+
def __class_getitem__(self, a: object) -> Any:
|
|
109
|
+
return Any
|
|
110
|
+
|
|
109
111
|
Final = FakeGetItemType
|
|
110
112
|
Literal = FakeGetItemType
|
|
111
113
|
Protocol = ABC
|
|
@@ -114,16 +116,18 @@ else:
|
|
|
114
116
|
if sys.version_info >= (3, 10):
|
|
115
117
|
from typing import ParamSpec, TypeAlias
|
|
116
118
|
else:
|
|
117
|
-
|
|
119
|
+
from typing import Any
|
|
120
|
+
|
|
121
|
+
TypeAlias = Any
|
|
118
122
|
|
|
119
123
|
class FakeParamSpecType:
|
|
120
124
|
def __init__(self, name: str) -> None: ...
|
|
121
125
|
|
|
122
|
-
def kwargs(self) ->
|
|
123
|
-
return
|
|
126
|
+
def kwargs(self) -> Any:
|
|
127
|
+
return Any
|
|
124
128
|
|
|
125
|
-
def args(self) ->
|
|
126
|
-
return
|
|
129
|
+
def args(self) -> Any:
|
|
130
|
+
return Any
|
|
127
131
|
|
|
128
132
|
ParamSpec = FakeParamSpecType
|
|
129
133
|
|
|
@@ -132,3 +136,13 @@ if sys.version_info >= (3, 11):
|
|
|
132
136
|
from typing import Self
|
|
133
137
|
else:
|
|
134
138
|
Self = ""
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if sys.version_info >= (3, 11):
|
|
142
|
+
from enum import StrEnum
|
|
143
|
+
else:
|
|
144
|
+
from enum import Enum
|
|
145
|
+
|
|
146
|
+
class StrEnum(str, Enum):
|
|
147
|
+
def __str__(self) -> str:
|
|
148
|
+
return self.value
|
cocotb/_scheduler.py
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
|
|
3
1
|
# Copyright cocotb contributors
|
|
4
2
|
# Copyright (c) 2013, 2018 Potential Ventures Ltd
|
|
5
3
|
# Copyright (c) 2013 SolarFlare Communications Inc
|
|
6
4
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
7
5
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
8
6
|
|
|
9
|
-
"""Task scheduler.
|
|
10
|
-
|
|
11
|
-
FIXME: We have a problem here. If a task schedules a read-only but we
|
|
12
|
-
also have pending writes we have to schedule the ReadWrite callback before
|
|
13
|
-
the ReadOnly (and this is invalid, at least in Modelsim).
|
|
14
|
-
"""
|
|
7
|
+
"""Task scheduler."""
|
|
15
8
|
|
|
16
9
|
import logging
|
|
17
|
-
import os
|
|
18
10
|
import threading
|
|
19
11
|
from bdb import BdbQuit
|
|
20
12
|
from collections import OrderedDict
|
|
@@ -23,6 +15,7 @@ from typing import Any, Callable, Coroutine, Dict, List, TypeVar, Union
|
|
|
23
15
|
import cocotb
|
|
24
16
|
import cocotb._gpi_triggers
|
|
25
17
|
import cocotb.handle
|
|
18
|
+
from cocotb import debug
|
|
26
19
|
from cocotb._base_triggers import Event, Trigger
|
|
27
20
|
from cocotb._bridge import external_state, external_waiter
|
|
28
21
|
from cocotb._exceptions import InternalError
|
|
@@ -36,11 +29,6 @@ from cocotb._profiling import profiling_context
|
|
|
36
29
|
from cocotb._py_compat import ParamSpec, insertion_ordered_dict
|
|
37
30
|
from cocotb.task import Task, _TaskState
|
|
38
31
|
|
|
39
|
-
# Sadly the Python standard logging module is very slow so it's better not to
|
|
40
|
-
# make any calls by testing a boolean flag first
|
|
41
|
-
_debug = "COCOTB_SCHEDULER_DEBUG" in os.environ
|
|
42
|
-
|
|
43
|
-
|
|
44
32
|
T = TypeVar("T")
|
|
45
33
|
|
|
46
34
|
P = ParamSpec("P")
|
|
@@ -120,8 +108,6 @@ class Scheduler:
|
|
|
120
108
|
|
|
121
109
|
def __init__(self) -> None:
|
|
122
110
|
self.log = logging.getLogger("cocotb.scheduler")
|
|
123
|
-
if _debug:
|
|
124
|
-
self.log.setLevel(logging.DEBUG)
|
|
125
111
|
|
|
126
112
|
# A dictionary of pending tasks for each trigger,
|
|
127
113
|
# indexed by trigger
|
|
@@ -164,7 +150,7 @@ class Scheduler:
|
|
|
164
150
|
|
|
165
151
|
Finds all Tasks waiting on the Trigger that fired and queues them.
|
|
166
152
|
"""
|
|
167
|
-
if
|
|
153
|
+
if debug.debug:
|
|
168
154
|
self.log.debug("Trigger fired: %s", trigger)
|
|
169
155
|
|
|
170
156
|
# find all tasks waiting on trigger that fired
|
|
@@ -175,16 +161,19 @@ class Scheduler:
|
|
|
175
161
|
# associated task waiting on that trigger, otherwise it would
|
|
176
162
|
# have been unprimed already
|
|
177
163
|
if isinstance(trigger, GPITrigger):
|
|
178
|
-
self.log.
|
|
179
|
-
|
|
164
|
+
self.log.warning(
|
|
165
|
+
"No tasks waiting on GPITrigger that fired: %s\n"
|
|
166
|
+
"This is due to an issue with the GPI or a simulator bug.",
|
|
167
|
+
trigger,
|
|
168
|
+
)
|
|
180
169
|
# For Python triggers this isn't actually an error - we might do
|
|
181
170
|
# event.set() without knowing whether any tasks are actually
|
|
182
171
|
# waiting on this event, for example
|
|
183
|
-
elif
|
|
172
|
+
elif debug.debug:
|
|
184
173
|
self.log.debug("No tasks waiting on trigger that fired: %s", trigger)
|
|
185
174
|
return
|
|
186
175
|
|
|
187
|
-
if
|
|
176
|
+
if debug.debug:
|
|
188
177
|
debugstr = "\n\t".join([str(task) for task in scheduling])
|
|
189
178
|
if len(scheduling) > 0:
|
|
190
179
|
debugstr = "\n\t" + debugstr
|
|
@@ -215,10 +204,10 @@ class Scheduler:
|
|
|
215
204
|
while self._scheduled_tasks:
|
|
216
205
|
task, exc = self._scheduled_tasks.popitem(last=False)
|
|
217
206
|
|
|
218
|
-
if
|
|
207
|
+
if debug.debug:
|
|
219
208
|
self.log.debug("Scheduling task %s", task)
|
|
220
209
|
self._resume_task(task, exc)
|
|
221
|
-
if
|
|
210
|
+
if debug.debug:
|
|
222
211
|
self.log.debug("Scheduled task %s", task)
|
|
223
212
|
|
|
224
213
|
# remove our reference to the objects at the end of each loop,
|
|
@@ -228,14 +217,14 @@ class Scheduler:
|
|
|
228
217
|
|
|
229
218
|
# Schedule may have queued up some events so we'll burn through those
|
|
230
219
|
while self._pending_events:
|
|
231
|
-
if
|
|
220
|
+
if debug.debug:
|
|
232
221
|
self.log.debug(
|
|
233
222
|
"Scheduling pending event %s", self._pending_events[0]
|
|
234
223
|
)
|
|
235
224
|
self._pending_events.pop(0).set()
|
|
236
225
|
|
|
237
226
|
# no more pending tasks
|
|
238
|
-
if
|
|
227
|
+
if debug.debug:
|
|
239
228
|
self.log.debug("All tasks scheduled, handing control back to simulator")
|
|
240
229
|
|
|
241
230
|
def _unschedule(self, task: Task[Any]) -> None:
|
|
@@ -289,6 +278,10 @@ class Scheduler:
|
|
|
289
278
|
|
|
290
279
|
It is an error to attempt to queue a task that has already been queued.
|
|
291
280
|
"""
|
|
281
|
+
if task.done():
|
|
282
|
+
raise RuntimeError(
|
|
283
|
+
f"{task} has finished executing and can not be scheduled again. Did you call start_soon() on a finished Task?"
|
|
284
|
+
)
|
|
292
285
|
if task in self._scheduled_tasks:
|
|
293
286
|
return
|
|
294
287
|
for tasks in self._trigger2tasks.values():
|
|
@@ -341,7 +334,8 @@ class Scheduler:
|
|
|
341
334
|
event.set()
|
|
342
335
|
|
|
343
336
|
event = threading.Event()
|
|
344
|
-
|
|
337
|
+
# must register this with test as there's no way to clean up with threading
|
|
338
|
+
self._schedule_task_internal(cocotb.start_soon(wrapper()))
|
|
345
339
|
# The scheduler thread blocks in `thread_wait`, and is woken when we
|
|
346
340
|
# call `thread_suspend` - so we need to make sure the task is
|
|
347
341
|
# queued before that.
|
|
@@ -367,7 +361,7 @@ class Scheduler:
|
|
|
367
361
|
|
|
368
362
|
def execute_external() -> None:
|
|
369
363
|
waiter._outcome = capture(func, *args, **kwargs)
|
|
370
|
-
if
|
|
364
|
+
if debug.debug:
|
|
371
365
|
self.log.debug(
|
|
372
366
|
"Execution of external routine done %s", threading.current_thread()
|
|
373
367
|
)
|
|
@@ -410,13 +404,13 @@ class Scheduler:
|
|
|
410
404
|
trigger = task._advance(exc)
|
|
411
405
|
|
|
412
406
|
if task.done():
|
|
413
|
-
if
|
|
407
|
+
if debug.debug:
|
|
414
408
|
self.log.debug("%s completed with %s", task, task._outcome)
|
|
415
409
|
assert trigger is None
|
|
416
410
|
self._unschedule(task)
|
|
417
411
|
|
|
418
412
|
if not task.done():
|
|
419
|
-
if
|
|
413
|
+
if debug.debug:
|
|
420
414
|
self.log.debug("%r yielded %s", task, trigger)
|
|
421
415
|
if not isinstance(trigger, Trigger):
|
|
422
416
|
e = TypeError(
|
|
@@ -434,14 +428,14 @@ class Scheduler:
|
|
|
434
428
|
if self._main_thread is threading.current_thread():
|
|
435
429
|
for ext in self._pending_threads:
|
|
436
430
|
ext.thread_start()
|
|
437
|
-
if
|
|
431
|
+
if debug.debug:
|
|
438
432
|
self.log.debug(
|
|
439
433
|
"Blocking from %s on %s",
|
|
440
434
|
threading.current_thread(),
|
|
441
435
|
ext.thread,
|
|
442
436
|
)
|
|
443
437
|
state = ext.thread_wait()
|
|
444
|
-
if
|
|
438
|
+
if debug.debug:
|
|
445
439
|
self.log.debug(
|
|
446
440
|
"Back from wait on self %s with newstate %s",
|
|
447
441
|
threading.current_thread(),
|
cocotb/_test.py
CHANGED
|
@@ -14,12 +14,12 @@ from typing import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
import cocotb
|
|
17
|
+
from cocotb._base_triggers import NullTrigger, Trigger
|
|
17
18
|
from cocotb._deprecation import deprecated
|
|
18
19
|
from cocotb._exceptions import InternalError
|
|
19
20
|
from cocotb._outcomes import Error, Outcome, Value
|
|
20
21
|
from cocotb._test_functions import TestSuccess
|
|
21
22
|
from cocotb.task import ResultType, Task
|
|
22
|
-
from cocotb.triggers import NullTrigger, Trigger
|
|
23
23
|
|
|
24
24
|
_pdb_on_exception = "COCOTB_PDB_ON_EXCEPTION" in os.environ
|
|
25
25
|
|
|
@@ -89,7 +89,10 @@ class RunningTest:
|
|
|
89
89
|
|
|
90
90
|
# Break into pdb on test end before all Tasks are killed.
|
|
91
91
|
if _pdb_on_exception and isinstance(outcome, Error):
|
|
92
|
-
|
|
92
|
+
try:
|
|
93
|
+
pdb.post_mortem(outcome.error.__traceback__)
|
|
94
|
+
except BaseException:
|
|
95
|
+
pdb.set_trace()
|
|
93
96
|
|
|
94
97
|
# Set outcome and cancel Tasks.
|
|
95
98
|
self._outcome = outcome
|
|
@@ -99,8 +102,8 @@ class RunningTest:
|
|
|
99
102
|
self._test_complete_cb()
|
|
100
103
|
|
|
101
104
|
def add_task(self, task: Task[Any]) -> None:
|
|
102
|
-
task._add_done_callback(self._task_done_callback)
|
|
103
105
|
self.tasks.append(task)
|
|
106
|
+
task._add_done_callback(self._task_done_callback)
|
|
104
107
|
|
|
105
108
|
def _task_done_callback(self, task: Task[Any]) -> None:
|
|
106
109
|
self.tasks.remove(task)
|