I wrote a previous post about clock and Timer triggers in cocotb. Considering that all triggers yield to core scheduler, I thought to do another trigger (Posedge) and the trampoline.
Class hierarchy Link to heading
Starting with FallingEdge
where it takes the signal handle, In this example, dut.clk
is passed to FallingEdge
await FallingEdge(dut.clk)
FallingEdge
sets the edge type for generic _EdgeBase
class FallingEdge(_EdgeBase):
"""Fires on the falling edge of *signal*, on a transition from ``1`` to ``0``."""
__slots__ = ()
_edge_type = 2
_EdgeBase
defines the prime
that registers callback
with the simulator.
class _EdgeBase(GPITrigger, metaclass=_ParameterizedSingletonAndABC):
def __init__(self, signal):
super().__init__()
self.signal = signal
def prime(self, callback):
if self.cbhdl is None:
self.cbhdl = simulator.register_value_change_callback(
self.signal._handle, callback, type(self)._edge_type, self
)
super().prime(callback)
GPITrigger
is almost empty base class which extends Trigger
class GPITrigger(Trigger):
__slots__ = ("cbhdl",)
def __init__(self):
Trigger.__init__(self)
self.cbhdl = None
Trigger
defines the __await__
that yields self to the scheduler.
class Trigger(Awaitable):
def __init__(self):
self.primed = False
def prime(self, callback):
self.primed = True
def __await__(self):
# hand the trigger back to the scheduler trampoline
return (yield self)
prime and trampoline Link to heading
To explain the trampoline, We will have to dig into the scheduler. _schedule
method is called on the trigger <NullTrigger for Start <Test dff_simple_test> at 0x7f8d48bc2cc0>
. Note that result
is returned to _advance
.
956 result = coroutine._advance(send_outcome)
957
973 if not coroutine.done():
979 try:
980 result = self._trigger_from_any(result)
981 except TypeError as exc:
984 result = NullTrigger(outcome=outcomes.Error(exc))
985
986 self._resume_coro_upon(coroutine, result)
And _resume_coro_upon
calls prime
612 def _resume_coro_upon(self, coro, trigger):
...
...
634 try:
635 print("trigger: Before prime", trigger)
636 trigger.prime(self._react)
The next point of interest is where _react
is called by the callback from simulator callback registered by the prime above.
361
362 def _react(self, trigger):
381 # start the event loop
382 self._is_reacting = True
383 try:
385 self._event_loop(trigger)
_react
calls _event_loop
389 def _event_loop(self, trigger):
480 for coro in self._scheduling:
...
...
488 self._schedule(coro, trigger=trigger)
_event_loop
calls _schedule
934 def _schedule(self, coroutine, trigger=None):
...
...
947 with self._task_context(coroutine):
948 if trigger is None:
949 send_outcome = outcomes.Value(None)
950 else:
951 send_outcome = trigger._outcome
952 if _debug:
953 self.log.debug(f"Scheduling with {send_outcome}")
954
955 coroutine._trigger = None
956 result = coroutine._advance(send_outcome)
and _advance
sends to outcome
which makes yield self
above returns and __await__
returns.
205 def _advance(self, outcome: outcomes.Outcome) -> typing.Any:
...
...
215 try:
216 self._started = True
218 return outcome.send(self._coro)