Open marlonjames opened 4 years ago
This behavior on it's face seems wrong. Entering a VPI callback from another thread also seems wrong. The "correct" (but not necessarily realistic) solution is to have these issues resolved in the simulators and not have cocotb and every other VPI/VHPI/FLI user work around bad behavior with hacks. It is a cost to develop, test, document, and maintain for everyone affected. The whole point of a standardized interface is to avoid these issues. Before we go about discussing the development of hacks, we should investigate the LRM to see if this is legal or not, and investigate the ability to fix the problem behavior at the source.
Contrarily, I was talking to @leftink one day and he was of the opinion that the idea of a single unified VPI and VHPI implementation is a pipe dream; and if we can't beat 'em, join 'em. Every single simulator we support has quirks, some more serious than others. Instead of trying to force a universal VPI/VHPI implementation, we could move to independent simulator-specific GPI implementations. These implementations would support all simulator-supported procedural interfaces. This might be necessary for proper mixed language procedural support anyways, since there is no standard for it. Each simulator-specific implementation could handle mixed language interaction for it's particular simulator. The current situation of having per-simulator implementations of VPI/VHPI generated during build is an indication we are already sort of doing this. We are just co-locating the hacks in the same source; and are handling mixed language interaction in the GPI, assuming a particular behavior (which happens to work for Riviera and Questa).
Below are the details I could find for things that concern cocotb.
vhpiCbValueChange
14.7.3.4 Signal Update
Each time there is an event on a signal
S
, any registered and enabledvhpiCbValueChange
callbacks associated withS
are executed.21.3.2.2 vhpiCbValueChange
The trigger event for a
vhpiCbValueChange
callback is one of:
- [...] or a call by a VHPI program to the
vhpi_put_value
function to update the variable.- An event on a signal [...]
NOTE 4—An event on a signal may result from [...] a call by a VHPI program to the
vhpi_put_value
function withmode
valuevhpiDepositPropagate
orvhpiForcePropagate
to update the signal. [...]
But:
22.5.3 Updating an object of class signal
a call to the
vhpi_put_value
function to update the effective value of a signal with an update mode ofvhpiDepositPropagate
schedules an effective-value deposit for the signal, [...] The effect is to update the variable containing the current value of the signal during the next signal update phase of a simulation cycle.
So value changes shouldn't trigger callbacks until the appropriate part of the time step. Similar for vhpiForcePropagate
.
vhpi_control()
23.5 vhpi_control
If command is
vhpiFinish
, after control returns to the tool from the callback function from which thevhpi_control
function was invoked, the tool enters the termination phase (see 20.10)
That's pretty explicit.
This is interesting though:
NOTE—In response to a call to
vhpi_control
with the argumentvhpiFinish
, the tool does not perform anyvhpiCbEndOfSimulation
callbacks.
Does any simulator follow this? This precludes the current approach in #1793, but it's not a shall...
NOTE—The interrupt event may be an event that occurs asynchronously with respect to tool execution or an exception event.
This note implies it's an exception to normal behavior.
This details the execution algorithm for the simulator model and clearly defines when most callbacks should occur.
cbValueChange
38.36.1 Simulation event callbacks
- cbValueChange After value change on some variables, any expression, or terminal or after execution of an event statement. Specifically excluded are class objects, dynamic arrays, strings, queues, and associative arrays.
4.3 Event simulation
Every change in state of a net or variable in the system description being simulated is considered an update event.
Processes are sensitive to update events. When an update event is executed, all the processes that are sensitive to that event are considered for evaluation in an arbitrary order. The evaluation of a process is also an event, known as an evaluation event.
Evaluation events also include PLI callbacks, which are points in the execution model where PLI application routines can be called from the simulation kernel.
So the callback is an evaluation event caused by an update event.
38.34 vpi_put_value()
The flags argument shall be used to direct the routine to use one of the following delay modes:
- vpiInertialDelay All scheduled events on the object shall be removed before this event is scheduled.
- vpiTransportDelay All events on the object scheduled for times later than this event shall be removed (modified transport delay).
- vpiPureTransportDelay No events on the object shall be removed (transport delay).
- vpiNoDelay The object shall be set to the passed value with no delay. Argument time_p shall be ignored and can be set to
NULL
.- vpiForceFlag The object shall be forced to the passed value with no delay (same as the SystemVerilog procedural force). Argument time_p shall be ignored and can be set to
NULL
.If the flags argument also has the bit mask vpiReturnEvent, vpi_put_value() shall return a handle of type vpiSchedEvent to the newly scheduled event, provided there is some form of a delay and an event is scheduled. If the bit mask is not used, or if no delay is used, or if an event is not scheduled, the return value shall be
NULL
.
vpiInertialDelay puts the update on the end of the schedule queue at whatever time is specified (cocotb uses a delay of 0), so it gets updated after returning from the callback.
When doing a vpiNoDelay update, the value should change before returning from vpi_put_value() so that a subsequent vpi_get_value() returns the new value. My understanding is that it also doesn't create a scheduled event (even one with 0 delay that occurs in the same time step).
Some outstanding questions: If a VPI callback were to call vpi_put_value() with vpiNoDelay twice, changing the value then changing it back before returning, should that trigger a value change callback? Should it trigger it twice? I wonder what happens in simulators other than icarus. Does a vpiNoDelay update still count as an update event?
vpi_control()
38.4 vpi_control()
The VPI routine vpi_control() shall pass information from a user PLI application to a SystemVerilog software tool, such as a simulator. The following control constants are defined as part of the VPI standard:
- vpiFinish Causes the
$finish
built-in SystemVerilog system task to be executed upon return of the application routine. This operation shall be passed one additional integer argument, which is the same as the diagnostic message level argument passed to$finish
(see 20.2).
There you go, Questa is doing it wrong.
cbSignal
vpi_register_cb() can be used to set up a signal handler. To do this, set the reason field to
cbSignal
, and set the index field to one of the legal signals specified by the operating system. When this signal occurs, the simulator will trap the signal, proceed to a safe point (if possible), and then call the callback routine.
@garmin-mjames Thanks so much for doing the investigation! I will complain to Mentor about their behavior.
When doing a vpiNoDelay update, the value should change before returning from vpi_put_value() so that a subsequent vpi_get_value() returns the new value.
The update event can occur immediately, but that doesn't mean that reactive processes should be scheduled immediately.
Processes are sensitive to update events. The evaluation of a process is also an event, known as an evaluation event. Evaluation events also include PLI callbacks
So PLI callbacks should be treated the same as sensitive process statements in HDL. Are HDL processes run immediately when a vpi_put_value()
with vpiNoDelay
occurs?
There are multiple cases of simulators calling a registered VPI callback while cocotb is still inside a previous callback.
This causes
to_python()
to log an error andexit(1)
.cbValueChange
when usingForce
(also when usingvpiNoDelay
)cbEndOfSimulation
when callingvpi_control(vpiFinish, ...)
cbEndOfSimulation
on GHDL internal errorPossible:
This is intended to be a general discussion. Should/can the GPI handle this a generic way?