cocotb / cocotb

cocotb, a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python
https://www.cocotb.org
BSD 3-Clause "New" or "Revised" License
1.76k stars 505 forks source link

GPI: simulator callback re-entrancy #2178

Open marlonjames opened 3 years ago

marlonjames commented 3 years ago

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 and exit(1).

Possible:

This is intended to be a general discussion. Should/can the GPI handle this a generic way?

ktbarrett commented 3 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).

marlonjames commented 3 years ago

VHDL 2008 LRM

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 enabled vhpiCbValueChange callbacks associated with S 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 with mode value vhpiDepositPropagate or vhpiForcePropagate 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 of vhpiDepositPropagate 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 the vhpi_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 argument vhpiFinish, the tool does not perform any vhpiCbEndOfSimulation callbacks.

Does any simulator follow this? This precludes the current approach in #1793, but it's not a shall...

Others

21.3.7.10 vhpiCbSigInterrupt

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.

14.7.5.3 Simulation Cycle

This details the execution algorithm for the simulator model and clearly defines when most callbacks should occur.

marlonjames commented 3 years ago

SystemVerilog 2012/2017 LRM

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.

vpiNoDelay

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.

Others

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.

ktbarrett commented 3 years ago

@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?