cocob provides a periodic clock class Clock
which depends on Timer
trigger. I will start with an examples of Clock
usage.
clock = Clock(dut.clk, 10, units="us")
cocotb.start_soon(clock.start())
Note That start_soon
is just starting cocotb coroutine after the current routines yields. Any let’s focus on Clock
In Clock
, The initialization __init__
sets some local vars (most importantly period and half_period)
118 BaseClock.__init__(self, signal)
126 self.period = get_sim_steps(period, units)
127 self.half_period = get_sim_steps(period / 2.0, units)
128 self.frequency = 1.0 / get_time_from_sim_steps(self.period, units="us")
130 self.signal = signal
Side note, get_sim_steps
calculates steps from real time passed to Clock
122 def get_sim_steps(
123 time: Union[Real, Decimal], units: str = "step", *, round_mode: str = "error"
124 ) -> int:
125 """Calculates the number of simulation time steps for a given amount of *time*.
start
creates a Timer
and keeps toggling when that timer triggers. See lines 157-160 below.
148 t = Timer(self.half_period)
149 if cycles is None:
150 it = itertools.count()
151 else:
152 it = range(cycles)
153
154 # branch outside for loop for performance (decision has to be taken only once)
155 if start_high:
156 for _ in it:
157 self.signal.value = 1
158 await t
159 self.signal.value = 0
160 await t
161 else:
162 for _ in it:
163 self.signal.value = 0
164 await t
165 self.signal.value = 1
166 await t
In Timer
, cbhdl
is set to register_timed_callback
with required time and callback.
167 class Timer(GPITrigger):
168 """Fire after the specified simulation time period has elapsed."""
169
...
...
270 def prime(self, callback):
271 """Register for a timed callback."""
272 if self.cbhdl is None:
273 self.cbhdl = simulator.register_timed_callback(
274 self.sim_steps, callback, self
275 )
276 if self.cbhdl is None:
277 raise TriggerException("Unable set up %s Trigger" % (str(self)))
278 GPITrigger.prime(self, callback)
register_timed_callback
is cpython implementation to register VPI callback that calls the routine passed from python. scheduler is the one calling the `prime() with callback coro.
523 gpi_sim_hdl sig_hdl = ((gpi_hdl_Object<gpi_sim_hdl> *)pSigHdl)->hdl;
524
525 // Extract the callback function
526 PyObject *function = PyTuple_GetItem(args, 1);
527 if (!PyCallable_Check(function)) {
528 PyErr_SetString(PyExc_TypeError,
529 "Attempt to register value change callback without "
530 "passing a callable callback!\n");
531 return NULL;
532 }
533 Py_INCREF(function);
534
535 PyObject *pedge = PyTuple_GetItem(args, 2);
536 int edge = (int)PyLong_AsLong(pedge);
537
538 // Remaining args for function
539 PyObject *fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference
540 if (fArgs == NULL) {
541 return NULL;
542 }
543
544 callback_data *cb_data = callback_data_new(function, fArgs, NULL);
545 if (cb_data == NULL) {
546 return NULL;
For completeness, GPITrigger
and Trigger
classes are below. The important part in Trigger
is __await__
as this is what get back when await is called. Note that Trigger is Awaitable
with __await__
return self
when coro tries to await the trigger
142 class GPITrigger(Trigger):
143 """Base Trigger class for GPI triggers.
...
...
150 def __init__(self):
151 Trigger.__init__(self)
65 class Trigger(Awaitable):
66 """Base class to derive from."""
...
...
121 @property
122 def _outcome(self):
123 """The result that `await this_trigger` produces in a coroutine.
124
125 The default is to produce the trigger itself, which is done for
126 ease of use with :class:`~cocotb.triggers.First`.
127 """
128 return outcomes.Value(self)
129
130 def __await__(self):
131 # hand the trigger back to the scheduler trampoline
132 return (yield self)
133