A really nice feature of cocotb is force and release which map to RTL force and release depending on the language (and simulator). This is example how to use Force
and Release
.
from cocotb.handle import Force, Release, Deposit
from cocotb.binary import BinaryValue
value = "0"
sig = ....
sig.value = Force(BinaryValue(len(sig) * value)
sig.value = Release()
Force
and Release
are defines in handle.py
and defines _as_gpi_args_for
.
class _SetValueAction(_SetAction):
__slots__ = ("value",)
"""Base class representing the type of action used while write-accessing a handle with a value."""
def __init__(self, value):
self.value = value
class Force(_SetValueAction):
"""Action used to force a handle to a given value until a release is applied."""
def _as_gpi_args_for(self, hdl):
return self.value, 1 # GPI_FORCE
class Release(_SetAction):
"""Action used to stop the effects of a previously applied force/freeze action."""
def _as_gpi_args_for(self, hdl):
return 0, 2 # GPI_RELEASE
Ok, Let’s look what happens when testbench calls sig.value = Force(v)
. Starting with setter
function which calls _set_value
.
@value.setter
def value(self, value):
self._set_value(value, cocotb.scheduler._schedule_write)
For integer, IntegerObject
calls _check_for_set_action
class IntegerObject(ModifiableObject):
value, set_action = self._check_for_set_action(value)
def _set_value(self, value, call_sim):
...
call_sim(self, self._handle.set_signal_val_int, set_action, value)
And _check_for_set_action
calls _as_gpi_args_for
to get type of action and pass it down to simulator.
def _check_for_set_action(self, value):
if not isinstance(value, _SetAction):
return value, 0 # GPI_DEPOSIT
return value._as_gpi_args_for(self)
well, Several layers(gpi and stuff), there are several implementations of force(FLI/VHPI and VPI). This is snippet from the vpi set_signal_value
. vpiForceFlag
is passed to vpi_put_value
.
int VpiSignalObjHdl::set_signal_value(s_vpi_value value_s,
gpi_set_action_t action) {
PLI_INT32 vpi_put_flag = -1;
s_vpi_time vpi_time_s;
vpi_time_s.type = vpiSimTime;
vpi_time_s.high = 0;
vpi_time_s.low = 0;
switch (action) {
case GPI_DEPOSIT:
if (vpiStringVar ==
vpi_get(vpiType, GpiObjHdl::get_handle<vpiHandle>())) {
// assigning to a vpiStringVar only seems to work with
// vpiNoDelay
vpi_put_flag = vpiNoDelay;
} else {
// Use Inertial delay to schedule an event, thus behaving like a
// verilog testbench
vpi_put_flag = vpiInertialDelay;
}
break;
case GPI_FORCE:
vpi_put_flag = vpiForceFlag;
break;
case GPI_RELEASE:
// Best to pass its current value to the sim when releasing
vpi_get_value(GpiObjHdl::get_handle<vpiHandle>(), &value_s);
vpi_put_flag = vpiReleaseFlag;
break;
default:
assert(0);
}
if (vpi_put_flag == vpiNoDelay) {
vpi_put_value(GpiObjHdl::get_handle<vpiHandle>(), &value_s, NULL,
vpiNoDelay);
} else {
vpi_put_value(GpiObjHdl::get_handle<vpiHandle>(), &value_s, &vpi_time_s,
vpi_put_flag);
}