cocotb 1.9.2__cp38-cp38-win32.whl → 2.0.0b1__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.

Potentially problematic release.


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

Files changed (155) hide show
  1. cocotb/{ANSI.py → _ANSI.py} +5 -25
  2. cocotb/__init__.py +76 -332
  3. cocotb/_base_triggers.py +513 -0
  4. cocotb/_bridge.py +187 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +3 -3
  7. cocotb/_exceptions.py +7 -0
  8. cocotb/_extended_awaitables.py +419 -0
  9. cocotb/_gpi_triggers.py +382 -0
  10. cocotb/_init.py +295 -0
  11. cocotb/_outcomes.py +54 -0
  12. cocotb/_profiling.py +46 -0
  13. cocotb/_py_compat.py +100 -29
  14. cocotb/_scheduler.py +454 -0
  15. cocotb/_test.py +245 -0
  16. cocotb/_test_factory.py +309 -0
  17. cocotb/_test_functions.py +42 -0
  18. cocotb/_typing.py +7 -0
  19. cocotb/_utils.py +296 -0
  20. cocotb/_version.py +3 -7
  21. cocotb/_xunit_reporter.py +66 -0
  22. cocotb/clock.py +271 -108
  23. cocotb/handle.py +1342 -795
  24. cocotb/libs/cocotb.dll +0 -0
  25. cocotb/libs/cocotb.exp +0 -0
  26. cocotb/libs/cocotb.lib +0 -0
  27. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  28. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  29. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  30. cocotb/libs/cocotbutils.dll +0 -0
  31. cocotb/libs/cocotbutils.exp +0 -0
  32. cocotb/libs/cocotbutils.lib +0 -0
  33. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  34. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  35. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  36. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  37. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  38. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  39. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  40. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  41. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  42. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  43. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  44. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  45. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  46. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  47. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  48. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  49. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  50. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  51. cocotb/libs/embed.dll +0 -0
  52. cocotb/libs/embed.exp +0 -0
  53. cocotb/libs/embed.lib +0 -0
  54. cocotb/libs/gpi.dll +0 -0
  55. cocotb/libs/gpi.exp +0 -0
  56. cocotb/libs/gpi.lib +0 -0
  57. cocotb/libs/gpilog.dll +0 -0
  58. cocotb/libs/gpilog.exp +0 -0
  59. cocotb/libs/gpilog.lib +0 -0
  60. cocotb/libs/pygpilog.dll +0 -0
  61. cocotb/libs/pygpilog.exp +0 -0
  62. cocotb/libs/pygpilog.lib +0 -0
  63. cocotb/{log.py → logging.py} +105 -110
  64. cocotb/queue.py +103 -57
  65. cocotb/regression.py +667 -712
  66. cocotb/result.py +17 -188
  67. cocotb/share/def/aldec.exp +0 -0
  68. cocotb/share/def/aldec.lib +0 -0
  69. cocotb/share/def/ghdl.exp +0 -0
  70. cocotb/share/def/ghdl.lib +0 -0
  71. cocotb/share/def/icarus.exp +0 -0
  72. cocotb/share/def/icarus.lib +0 -0
  73. cocotb/share/def/modelsim.def +1 -0
  74. cocotb/share/def/modelsim.exp +0 -0
  75. cocotb/share/def/modelsim.lib +0 -0
  76. cocotb/share/include/cocotb_utils.h +6 -29
  77. cocotb/share/include/embed.h +5 -28
  78. cocotb/share/include/gpi.h +137 -92
  79. cocotb/share/include/gpi_logging.h +221 -142
  80. cocotb/share/include/py_gpi_logging.h +7 -4
  81. cocotb/share/include/vpi_user_ext.h +4 -26
  82. cocotb/share/lib/verilator/verilator.cpp +59 -54
  83. cocotb/simulator.cp38-win32.exp +0 -0
  84. cocotb/simulator.cp38-win32.lib +0 -0
  85. cocotb/simulator.cp38-win32.pyd +0 -0
  86. cocotb/simulator.pyi +107 -0
  87. cocotb/task.py +434 -212
  88. cocotb/triggers.py +55 -1092
  89. cocotb/types/__init__.py +25 -47
  90. cocotb/types/_abstract_array.py +151 -0
  91. cocotb/types/_array.py +264 -0
  92. cocotb/types/_logic.py +296 -0
  93. cocotb/types/_logic_array.py +834 -0
  94. cocotb/types/{range.py → _range.py} +36 -44
  95. cocotb/types/_resolve.py +76 -0
  96. cocotb/utils.py +119 -587
  97. cocotb-2.0.0b1.dist-info/METADATA +46 -0
  98. cocotb-2.0.0b1.dist-info/RECORD +143 -0
  99. {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info}/WHEEL +1 -1
  100. cocotb-2.0.0b1.dist-info/entry_points.txt +2 -0
  101. {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info}/top_level.txt +1 -0
  102. cocotb_tools/__init__.py +0 -0
  103. cocotb_tools/_coverage.py +33 -0
  104. cocotb_tools/_vendor/__init__.py +3 -0
  105. cocotb_tools/check_results.py +65 -0
  106. cocotb_tools/combine_results.py +152 -0
  107. cocotb_tools/config.py +241 -0
  108. {cocotb → cocotb_tools}/ipython_support.py +29 -22
  109. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  110. {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +82 -54
  111. {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +8 -33
  112. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
  113. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  114. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  115. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
  116. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  117. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  118. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  119. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  120. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  121. cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
  122. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  123. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
  124. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  125. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
  126. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
  127. cocotb_tools/py.typed +0 -0
  128. {cocotb → cocotb_tools}/runner.py +794 -361
  129. cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
  130. pygpi/entry.py +34 -17
  131. pygpi/py.typed +0 -0
  132. cocotb/binary.py +0 -858
  133. cocotb/config.py +0 -289
  134. cocotb/decorators.py +0 -332
  135. cocotb/memdebug.py +0 -35
  136. cocotb/outcomes.py +0 -56
  137. cocotb/scheduler.py +0 -1099
  138. cocotb/share/makefiles/Makefile.deprecations +0 -12
  139. cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
  140. cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
  141. cocotb/share/makefiles/simulators/Makefile.ius +0 -125
  142. cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
  143. cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
  144. cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
  145. cocotb/types/array.py +0 -309
  146. cocotb/types/logic.py +0 -292
  147. cocotb/types/logic_array.py +0 -298
  148. cocotb/wavedrom.py +0 -199
  149. cocotb/xunit_reporter.py +0 -80
  150. cocotb-1.9.2.dist-info/METADATA +0 -168
  151. cocotb-1.9.2.dist-info/RECORD +0 -121
  152. cocotb-1.9.2.dist-info/entry_points.txt +0 -2
  153. /cocotb/{_vendor/__init__.py → py.typed} +0 -0
  154. {cocotb-1.9.2.dist-info → cocotb-2.0.0b1.dist-info}/LICENSE +0 -0
  155. {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
@@ -0,0 +1,66 @@
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
+ import xml.etree.ElementTree as ET
8
+ from typing import Union
9
+ from xml.etree.ElementTree import Element, SubElement
10
+
11
+
12
+ class XUnitReporter:
13
+ last_testsuite: Element
14
+ last_testcase: Element
15
+
16
+ def __init__(self, filename: str = "results.xml") -> None:
17
+ self.results = Element("testsuites", name="results")
18
+ self.filename = filename
19
+
20
+ def add_testsuite(self, **kwargs: str) -> Element:
21
+ self.last_testsuite = SubElement(self.results, "testsuite", kwargs)
22
+ return self.last_testsuite
23
+
24
+ def add_testcase(
25
+ self, testsuite: Union[Element, None] = None, **kwargs: str
26
+ ) -> Element:
27
+ if testsuite is None:
28
+ testsuite = self.last_testsuite
29
+ self.last_testcase = SubElement(testsuite, "testcase", kwargs)
30
+ return self.last_testcase
31
+
32
+ def add_property(
33
+ self, testsuite: Union[Element, None] = None, **kwargs: str
34
+ ) -> Element:
35
+ if testsuite is None:
36
+ testsuite = self.last_testsuite
37
+ self.last_property = SubElement(testsuite, "property", kwargs)
38
+ return self.last_property
39
+
40
+ def add_failure(self, testcase: Union[Element, None] = None, **kwargs: str) -> None:
41
+ if testcase is None:
42
+ testcase = self.last_testcase
43
+ SubElement(testcase, "failure", kwargs)
44
+
45
+ def add_skipped(self, testcase: Union[Element, None] = None, **kwargs: str) -> None:
46
+ if testcase is None:
47
+ testcase = self.last_testcase
48
+ SubElement(testcase, "skipped", kwargs)
49
+
50
+ def indent(self, elem: Element, level: int = 0) -> None:
51
+ i = "\n" + level * " "
52
+ if len(elem):
53
+ if not elem.text or not elem.text.strip():
54
+ elem.text = i + " "
55
+ if not elem.tail or not elem.tail.strip():
56
+ elem.tail = i
57
+ for sub_elem in elem:
58
+ self.indent(sub_elem, level + 1)
59
+ if not sub_elem.tail or not sub_elem.tail.strip():
60
+ sub_elem.tail = i
61
+ elif level and (not elem.tail or not elem.tail.strip()):
62
+ elem.tail = i
63
+
64
+ def write(self) -> None:
65
+ self.indent(self.results)
66
+ ET.ElementTree(self.results).write(self.filename, encoding="UTF-8")
cocotb/clock.py CHANGED
@@ -1,89 +1,105 @@
1
+ # Copyright cocotb contributors
1
2
  # Copyright (c) 2013 Potential Ventures Ltd
2
3
  # Copyright (c) 2013 SolarFlare Communications Inc
3
- # All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- # * Redistributions of source code must retain the above copyright
8
- # notice, this list of conditions and the following disclaimer.
9
- # * Redistributions in binary form must reproduce the above copyright
10
- # notice, this list of conditions and the following disclaimer in the
11
- # documentation and/or other materials provided with the distribution.
12
- # * Neither the name of Potential Ventures Ltd,
13
- # SolarFlare Communications Inc nor the
14
- # names of its contributors may be used to endorse or promote products
15
- # derived from this software without specific prior written permission.
16
- #
17
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
- # DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
21
- # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
- # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4
+ # Licensed under the Revised BSD License, see LICENSE for details.
5
+ # SPDX-License-Identifier: BSD-3-Clause
27
6
 
28
7
  """A clock class."""
29
8
 
30
- import itertools
9
+ import logging
31
10
  import warnings
32
11
  from decimal import Decimal
33
- from numbers import Real
34
- from typing import Union
12
+ from fractions import Fraction
13
+ from logging import Logger
14
+ from typing import ClassVar, Type, Union
35
15
 
36
- from cocotb.log import SimLog
37
- from cocotb.triggers import Timer
38
- from cocotb.utils import get_sim_steps, get_time_from_sim_steps, lazy_property
16
+ import cocotb
17
+ from cocotb._py_compat import (
18
+ Literal,
19
+ TypeAlias,
20
+ cached_property,
21
+ )
22
+ from cocotb._typing import TimeUnit
23
+ from cocotb.handle import (
24
+ Deposit,
25
+ Force,
26
+ Immediate,
27
+ LogicObject,
28
+ _GPISetAction,
29
+ _trust_inertial,
30
+ )
31
+ from cocotb.simulator import clock_create
32
+ from cocotb.task import Task
33
+ from cocotb.triggers import (
34
+ ClockCycles,
35
+ Event,
36
+ FallingEdge,
37
+ RisingEdge,
38
+ Timer,
39
+ ValueChange,
40
+ )
41
+ from cocotb.utils import get_sim_steps, get_time_from_sim_steps
39
42
 
43
+ __all__ = ("Clock",)
40
44
 
41
- class BaseClock:
42
- """Base class to derive from."""
45
+ Impl: TypeAlias = Literal["gpi", "py"]
43
46
 
44
- def __init__(self, signal):
45
- self.signal = signal
46
47
 
47
- @lazy_property
48
- def log(self):
49
- return SimLog("cocotb.{}.{}".format(type(self).__qualname__, self.signal._name))
48
+ _valid_impls = ("gpi", "py")
50
49
 
51
50
 
52
- class Clock(BaseClock):
51
+ class Clock:
53
52
  r"""Simple 50:50 duty cycle clock driver.
54
53
 
55
- Instances of this class should call its :meth:`start` method
56
- and pass the coroutine object to one of the functions in :ref:`task-management`.
57
-
58
- This will create a clocking task that drives the signal at the
59
- desired period/frequency.
60
-
61
- Example:
62
-
63
54
  .. code-block:: python
64
55
 
65
- c = Clock(dut.clk, 10, 'ns')
66
- await cocotb.start(c.start())
56
+ c = Clock(dut.clk, 10, "ns")
57
+ c.start()
67
58
 
68
59
  Args:
69
60
  signal: The clock pin/signal to be driven.
70
- period (int): The clock period. Must convert to an even number of
71
- timesteps.
72
- units (str, optional): One of
73
- ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``.
74
- When *units* is ``'step'``,
61
+ period: The clock period.
62
+
63
+ .. note::
64
+ Must convert to an even number of timesteps.
65
+ unit:
66
+ One of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``.
67
+ When *unit* is ``'step'``,
75
68
  the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`).
76
69
 
70
+ .. versionchanged:: 2.0
71
+ Renamed from ``units``.
72
+
73
+ impl:
74
+ One of ``'auto'``, ``'gpi'``, ``'py'``.
75
+ Specify whether the clock is implemented with a :class:`~cocotb.simulator.GpiClock` (faster), or with a Python coroutine.
76
+ When ``'auto'`` is used (default), the fastest implementation that supports your environment and use case is picked.
77
+
78
+ .. versionadded:: 2.0
79
+
80
+ set_action:
81
+ One of :class:`.Immediate`, :class:`.Deposit`, or :class:`.Force`.
82
+ Specify the action to use when setting the clock signal value.
83
+ Defaults to the value of :attr:`default_set_action`.
84
+
85
+ .. versionadded:: 2.0
86
+
87
+ When *impl* is ``'auto'``, if :envvar:`COCOTB_TRUST_INERTIAL_WRITES` is defined,
88
+ the :class:`~cocotb.simulator.GpiClock` implementation will be used.
89
+ Otherwise, the Python coroutine implementation will be used.
90
+ See the environment variable's documentation for more information on the consequences
91
+ of using the simulator's inertial write mechanism.
92
+
77
93
  If you need more features like a phase shift and an asymmetric duty cycle,
78
- it is simple to create your own clock generator (that you then :func:`~cocotb.start`):
94
+ it is simple to create your own clock generator (that you then :func:`cocotb.start_soon`):
79
95
 
80
96
  .. code-block:: python
81
97
 
82
98
  async def custom_clock():
83
99
  # pre-construct triggers for performance
84
- high_time = Timer(high_delay, units="ns")
85
- low_time = Timer(low_delay, units="ns")
86
- await Timer(initial_delay, units="ns")
100
+ high_time = Timer(high_delay, unit="ns")
101
+ low_time = Timer(low_delay, unit="ns")
102
+ await Timer(initial_delay, unit="ns")
87
103
  while True:
88
104
  dut.clk.value = 1
89
105
  await high_time
@@ -100,75 +116,222 @@ class Clock(BaseClock):
100
116
  async def custom_clock():
101
117
  while True:
102
118
  dut.clk.value = 1
103
- await Timer(high_delay, units="ns")
119
+ await Timer(high_delay, unit="ns")
104
120
  dut.clk.value = 0
105
- await Timer(low_delay, units="ns")
121
+ await Timer(low_delay, unit="ns")
122
+
106
123
 
107
124
  high_delay = low_delay = 100
108
- await cocotb.start(custom_clock())
109
- await Timer(1000, units="ns")
125
+ cocotb.start_soon(custom_clock())
126
+ await Timer(1000, unit="ns")
110
127
  high_delay = low_delay = 10 # change the clock speed
111
- await Timer(1000, units="ns")
128
+ await Timer(1000, unit="ns")
129
+
130
+ .. versionadded:: 1.5
131
+ Support ``'step'`` as the *unit* argument to mean "simulator time step".
132
+
133
+ .. versionremoved:: 2.0
134
+ Passing ``None`` as the *unit* argument was removed, use ``'step'`` instead.
135
+
136
+ .. versionchanged:: 2.0
137
+ :meth:`start` now automatically calls :func:`cocotb.start_soon` and stores the Task
138
+ on the Clock object, so that it may later be :meth:`stop`\ ped.
139
+ """
140
+
141
+ _impl: Impl
112
142
 
113
- .. versionchanged:: 1.5
114
- Support ``'step'`` as the *units* argument to mean "simulator time step".
143
+ default_set_action: ClassVar[Union[Type[Immediate], Type[Deposit], Type[Force]]] = (
144
+ Deposit
145
+ )
146
+ """The default action used to set the clock signal value.
147
+ One of :class:`.Immediate`, :class:`.Deposit`, or :class:`.Force`.
115
148
 
116
- .. deprecated:: 1.5
117
- Using ``None`` as the *units* argument is deprecated, use ``'step'`` instead.
149
+ .. versionadded:: 2.0
118
150
  """
119
151
 
120
152
  def __init__(
121
- self, signal, period: Union[float, Real, Decimal], units: str = "step"
122
- ):
123
- BaseClock.__init__(self, signal)
124
- if units is None:
153
+ self,
154
+ signal: LogicObject,
155
+ period: Union[float, Fraction, Decimal],
156
+ unit: TimeUnit = "step",
157
+ impl: Union[Impl, None] = None,
158
+ *,
159
+ units: None = None,
160
+ set_action: Union[Type[Immediate], Type[Deposit], Type[Force], None] = None,
161
+ ) -> None:
162
+ self._signal = signal
163
+ self._period = period
164
+ if units is not None:
125
165
  warnings.warn(
126
- 'Using units=None is deprecated, use units="step" instead.',
166
+ "The 'units' argument has been renamed to 'unit'.",
127
167
  DeprecationWarning,
128
168
  stacklevel=2,
129
169
  )
130
- units = "step" # don't propagate deprecated value
131
- self.period = get_sim_steps(period, units)
132
- self.half_period = get_sim_steps(period / 2, units)
133
- self.frequency = 1 / get_time_from_sim_steps(self.period, units="us")
134
- self.hdl = None
135
- self.signal = signal
136
- self.coro = None
137
- self.mcoro = None
138
-
139
- async def start(self, cycles=None, start_high=True):
140
- r"""Clocking coroutine. Start driving your clock by :func:`cocotb.start`\ ing a
141
- call to this.
170
+ unit = units
171
+ self._unit: TimeUnit = unit
172
+ if set_action is None:
173
+ set_action = type(self).default_set_action
174
+ if set_action not in (Immediate, Deposit, Force):
175
+ raise TypeError(
176
+ "Invalid value for *set_action*. *set_action* must be one of Immediate, Deposit, or Force"
177
+ )
178
+ self._set_action = set_action
179
+
180
+ if impl is None:
181
+ self._impl = "gpi" if _trust_inertial else "py"
182
+ elif impl in _valid_impls:
183
+ self._impl = impl
184
+ else:
185
+ valid_impls_str = ", ".join([repr(i) for i in _valid_impls])
186
+ raise ValueError(
187
+ f"Invalid clock impl {impl!r}, must be one of: {valid_impls_str}"
188
+ )
189
+
190
+ self._task: Union[Task[None], None] = None
191
+
192
+ @property
193
+ def signal(self) -> LogicObject:
194
+ """The clock signal being driven."""
195
+ return self._signal
196
+
197
+ @property
198
+ def period(self) -> Union[float, Fraction, Decimal]:
199
+ """The clock period (unit-less)."""
200
+ return self._period
201
+
202
+ @property
203
+ def unit(self) -> TimeUnit:
204
+ """The unit of the clock period.
205
+
206
+ .. versionadded:: 2.0
207
+ """
208
+ return self._unit
209
+
210
+ @property
211
+ def impl(self) -> Impl:
212
+ """The concrete implementation of the clock used.
213
+
214
+ ``"gpi"`` if the clock is implemented in C in the GPI layer,
215
+ or ``"py"`` if the clock is implemented in Python using cocotb Tasks.
216
+
217
+ .. versionadded:: 2.0
218
+ """
219
+ return self._impl
220
+
221
+ @property
222
+ def set_action(self) -> Union[Type[Immediate], Type[Deposit], Type[Force]]:
223
+ """The value setting action used to set the clock signal value.
224
+
225
+ .. versionadded:: 2.0
226
+ """
227
+ return self._set_action
228
+
229
+ def start(self, start_high: bool = True) -> Task[None]:
230
+ r"""Start driving the clock signal.
231
+
232
+ You can later stop the clock by calling :meth:`stop`.
142
233
 
143
234
  Args:
144
- cycles (int, optional): Cycle the clock *cycles* number of times,
145
- or if ``None`` then cycle the clock forever.
146
- Note: ``0`` is not the same as ``None``, as ``0`` will cycle no times.
147
- start_high (bool, optional): Whether to start the clock with a ``1``
235
+ start_high: Whether to start the clock with a ``1``
148
236
  for the first half of the period.
149
237
  Default is ``True``.
150
238
 
151
239
  .. versionadded:: 1.3
240
+
241
+ Raises:
242
+ RuntimeError: If attempting to start a clock that has already been started.
243
+
244
+ Returns:
245
+ Object which can be passed to :func:`cocotb.start_soon` or ignored.
246
+
247
+ .. versionremoved:: 2.0
248
+ Removed ``cycles`` arguments for toggling for a finite amount of cycles.
249
+ Use :meth:`stop` to stop a clock from running.
250
+
251
+ .. versionchanged:: 2.0
252
+ Previously, this method returned a :term:`coroutine` which needed to be passed to :func:`cocotb.start_soon`.
253
+ Now the Clock object keeps track of its own driver Task, so this is no longer necessary.
254
+ Simply call ``clock.start()`` to start running the clock.
152
255
  """
153
- t = Timer(self.half_period)
154
- if cycles is None:
155
- it = itertools.count()
156
- else:
157
- it = range(cycles)
158
-
159
- # branch outside for loop for performance (decision has to be taken only once)
160
- if start_high:
161
- for _ in it:
162
- self.signal.value = 1
163
- await t
164
- self.signal.value = 0
165
- await t
256
+ if self._task is not None:
257
+ raise RuntimeError("Starting clock that has already been started.")
258
+
259
+ period = get_sim_steps(self._period, self._unit)
260
+ t_high = period // 2
261
+
262
+ if self._impl == "gpi":
263
+ clkobj = clock_create(self._signal._handle)
264
+ set_action = {
265
+ Deposit: _GPISetAction.DEPOSIT,
266
+ Immediate: _GPISetAction.NO_DELAY,
267
+ Force: _GPISetAction.FORCE,
268
+ }[self._set_action]
269
+ clkobj.start(period, t_high, start_high, set_action)
270
+
271
+ async def drive() -> None:
272
+ # The clock is meant to toggle forever, so awaiting this should
273
+ # never return by awaiting on Event that's never set.
274
+ e = Event()
275
+ try:
276
+ await e.wait()
277
+ finally:
278
+ clkobj.stop()
279
+
166
280
  else:
167
- for _ in it:
168
- self.signal.value = 0
169
- await t
170
- self.signal.value = 1
171
- await t
172
-
173
- def __str__(self):
174
- return type(self).__qualname__ + "(%3.1f MHz)" % self.frequency
281
+
282
+ async def drive() -> None:
283
+ timer_high = Timer(t_high)
284
+ timer_low = Timer(period - t_high)
285
+ if start_high:
286
+ self._signal.set(self._set_action(1))
287
+ await timer_high
288
+ while True:
289
+ self._signal.set(self._set_action(0))
290
+ await timer_low
291
+ self._signal.set(self._set_action(1))
292
+ await timer_high
293
+
294
+ self._task = cocotb.start_soon(drive())
295
+ return self._task
296
+
297
+ def stop(self) -> None:
298
+ """Stop driving the clock signal.
299
+
300
+ You can later start the clock again by calling :meth:`start`.
301
+
302
+ Raises:
303
+ RuntimeError: If attempting to stop a clock that has never been started.
304
+
305
+ .. versionadded:: 2.0
306
+ """
307
+ if self._task is None:
308
+ raise RuntimeError("Stopping a clock that was never started.")
309
+ self._task.cancel()
310
+ self._task = None
311
+
312
+ async def cycles(
313
+ self,
314
+ num_cycles: int,
315
+ edge_type: Union[
316
+ Type[RisingEdge], Type[FallingEdge], Type[ValueChange]
317
+ ] = RisingEdge,
318
+ ) -> None:
319
+ """Wait for a number of clock cycles."""
320
+ # TODO Improve implementation to use a Timer to skip most of the cycles
321
+ await ClockCycles(self._signal, num_cycles, edge_type)
322
+
323
+ def __repr__(self) -> str:
324
+ return self._repr
325
+
326
+ @cached_property
327
+ def _repr(self) -> str:
328
+ freq_mhz = 1 / get_time_from_sim_steps(
329
+ get_sim_steps(self._period, self._unit), "us"
330
+ )
331
+ return f"<{type(self).__qualname__}, {self._signal._path} @ {freq_mhz} MHz>"
332
+
333
+ @cached_property
334
+ def _log(self) -> Logger:
335
+ return logging.getLogger(
336
+ f"cocotb.{type(self).__qualname__}.{self._signal._name}"
337
+ )