cocotb 2.0.0b1__cp310-cp310-win32.whl → 2.0.0rc2__cp310-cp310-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.

Potentially problematic release.


This version of cocotb might be problematic. Click here for more details.

Files changed (95) hide show
  1. cocotb/_ANSI.py +47 -54
  2. cocotb/__init__.py +12 -2
  3. cocotb/_base_triggers.py +11 -9
  4. cocotb/_bridge.py +8 -9
  5. cocotb/_extended_awaitables.py +1 -1
  6. cocotb/_gpi_triggers.py +4 -1
  7. cocotb/_init.py +17 -11
  8. cocotb/_py_compat.py +24 -10
  9. cocotb/_scheduler.py +25 -31
  10. cocotb/_test.py +6 -3
  11. cocotb/_test_factory.py +4 -1
  12. cocotb/_utils.py +1 -23
  13. cocotb/_version.py +1 -1
  14. cocotb/clock.py +98 -16
  15. cocotb/debug.py +24 -0
  16. cocotb/handle.py +62 -32
  17. cocotb/libs/cocotb.dll +0 -0
  18. cocotb/libs/cocotb.exp +0 -0
  19. cocotb/libs/cocotb.lib +0 -0
  20. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  21. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  22. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  23. cocotb/libs/cocotbutils.dll +0 -0
  24. cocotb/libs/cocotbutils.exp +0 -0
  25. cocotb/libs/cocotbutils.lib +0 -0
  26. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  27. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  28. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  29. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  30. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  31. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  32. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  33. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  34. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  35. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  36. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  37. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  38. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  39. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  40. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  41. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  42. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  43. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  44. cocotb/libs/embed.dll +0 -0
  45. cocotb/libs/embed.exp +0 -0
  46. cocotb/libs/embed.lib +0 -0
  47. cocotb/libs/gpi.dll +0 -0
  48. cocotb/libs/gpi.exp +0 -0
  49. cocotb/libs/gpi.lib +0 -0
  50. cocotb/libs/gpilog.dll +0 -0
  51. cocotb/libs/gpilog.exp +0 -0
  52. cocotb/libs/gpilog.lib +0 -0
  53. cocotb/libs/pygpilog.dll +0 -0
  54. cocotb/libs/pygpilog.exp +0 -0
  55. cocotb/libs/pygpilog.lib +0 -0
  56. cocotb/logging.py +243 -117
  57. cocotb/regression.py +43 -35
  58. cocotb/share/def/aldec.exp +0 -0
  59. cocotb/share/def/aldec.lib +0 -0
  60. cocotb/share/def/ghdl.exp +0 -0
  61. cocotb/share/def/ghdl.lib +0 -0
  62. cocotb/share/def/icarus.exp +0 -0
  63. cocotb/share/def/icarus.lib +0 -0
  64. cocotb/share/def/modelsim.exp +0 -0
  65. cocotb/share/def/modelsim.lib +0 -0
  66. cocotb/share/include/cocotb_utils.h +3 -3
  67. cocotb/share/include/embed.h +2 -2
  68. cocotb/share/include/gpi.h +258 -109
  69. cocotb/share/include/py_gpi_logging.h +3 -3
  70. cocotb/share/lib/verilator/verilator.cpp +23 -15
  71. cocotb/simtime.py +230 -0
  72. cocotb/simulator.cp310-win32.exp +0 -0
  73. cocotb/simulator.cp310-win32.lib +0 -0
  74. cocotb/simulator.cp310-win32.pyd +0 -0
  75. cocotb/task.py +54 -11
  76. cocotb/types/__init__.py +4 -1
  77. cocotb/types/_array.py +33 -2
  78. cocotb/types/_indexing.py +17 -0
  79. cocotb/types/_logic.py +96 -59
  80. cocotb/types/_logic_array.py +56 -22
  81. cocotb/types/_range.py +12 -5
  82. cocotb/utils.py +9 -129
  83. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/METADATA +1 -1
  84. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  85. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/licenses/LICENSE +1 -0
  86. cocotb_tools/config.py +5 -5
  87. cocotb_tools/makefiles/Makefile.inc +3 -9
  88. cocotb_tools/makefiles/Makefile.sim +9 -1
  89. cocotb_tools/makefiles/simulators/Makefile.vcs +1 -1
  90. cocotb_tools/runner.py +94 -59
  91. pygpi/entry.py +5 -6
  92. cocotb-2.0.0b1.dist-info/RECORD +0 -143
  93. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +0 -0
  94. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/entry_points.txt +0 -0
  95. {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
- DEFAULT_FG = _ESCAPE + "39m"
17
- DEFAULT_BG = _ESCAPE + "49m"
18
- DEFAULT = DEFAULT_BG + DEFAULT_FG
12
+ class ANSI(StrEnum):
13
+ """ANSI escape codes for coloring output.
19
14
 
20
- BLACK_FG = _ESCAPE + "30m"
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
- BLACK_BG = _ESCAPE + "40m"
30
- RED_BG = _ESCAPE + "41m"
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
- BRIGHT_BLACK_FG = _ESCAPE + "90m"
39
- BRIGHT_RED_FG = _ESCAPE + "91m"
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
- BRIGHT_BLACK_BG = _ESCAPE + "100m"
48
- BRIGHT_RED_BG = _ESCAPE + "101m"
49
- BRIGHT_GREEN_BG = _ESCAPE + "102m"
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
- COLOR_DEFAULT = DEFAULT
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
- COLOR_WARNING = YELLOW_FG
60
- COLOR_ERROR = RED_FG
61
- COLOR_CRITICAL = RED_BG + BLACK_FG
62
- COLOR_TEST = BLUE_FG
63
- COLOR_PASSED = GREEN_FG
64
- COLOR_SKIPPED = YELLOW_FG
65
- COLOR_FAILED = RED_FG
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
- COLOR_HILITE_SUMMARY = WHITE_FG + RED_BG
68
- COLOR_HILITE_HEXDIFF_DEFAULT = YELLOW_FG
69
- COLOR_HILITE_HEXDIFF_1 = CYAN_FG
70
- COLOR_HILITE_HEXDIFF_2 = RED_FG
71
- COLOR_HILITE_HEXDIFF_3 = MAGENTA_BG
72
- COLOR_HILITE_HEXDIFF_4 = CYAN_BG + BLACK_FG
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 :make:var:`COCOTB_PLUSARGS` for details.
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:`!.Event` to explicitly synchronize the two Tasks, like so:
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
- # Sadly the Python standard logging module is very slow so it's better not to
24
- # make any calls by testing a boolean flag first
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 _debug:
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 _debug:
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 _debug:
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 _debug:
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()}"
@@ -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 ClockCycle object.
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("COCOTB_COVERAGE_RCFILE")
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 COCOTB_COVERAGE_RCFILE."
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
- TypeAlias = ""
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) -> str:
123
- return ""
126
+ def kwargs(self) -> Any:
127
+ return Any
124
128
 
125
- def args(self) -> str:
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 _debug:
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.critical("No tasks waiting on trigger that fired: %s", trigger)
179
- trigger._log.info("I'm the culprit")
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 _debug:
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 _debug:
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 _debug:
207
+ if debug.debug:
219
208
  self.log.debug("Scheduling task %s", task)
220
209
  self._resume_task(task, exc)
221
- if _debug:
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 _debug:
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 _debug:
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
- self._schedule_task_internal(Task(wrapper()))
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 _debug:
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 _debug:
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 _debug:
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 _debug:
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 _debug:
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
- pdb.post_mortem(outcome.error.__traceback__)
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)