Timing Synchronization Between NGSPICE and VPI#
Overview#
This document explains the timing synchronization mechanism used in SpiceBind to coordinate simulation time between the digital HDL simulator (via VPI) and the analog SPICE simulator (NGSPICE). This mechanism ensures that both simulators advance in lock-step and can handle feedback between digital and analog domains.
Key Components#
1. Time Barrier (TimeBarrier.h
)#
The TimeBarrier
class is the core synchronization primitive that coordinates time progression between two simulation engines:
HDL_ENGINE_ID (0): Digital HDL simulator
SPICE_ENGINE_ID (1): NGSPICE analog simulator
The barrier ensures that neither simulator advances too far ahead of the other, maintaining synchronization.
2. Global State Variables#
g_time_barrier
: Global instance ofTimeBarrier<unsigned long long>
add_ngspice_timestep
: Flag indicating if NGSPICE needs a new timestep
Timing Synchronization Flow#
The timing synchronization follows a specific sequence when digital inputs to SPICE change:
Step 1: Digital Signal Change Detection#
Function: vpi_port_change_cb
(VpiCallbacks.cpp
)
auto vpi_port_change_cb(p_cb_data cb_data_p) -> PLI_INT32
Trigger:
cbValueChange
callback registered for every input port from VPI to SPICEPurpose: Detects when a digital signal changes that affects SPICE simulation
Key Actions:
Removes existing next time callbacks to prevent conflicts
Sets
add_ngspice_timestep = true
(only once per time step if multiple signals change)Registers immediate
cbAfterDelay
callback with delay = 0
Step 2: Immediate Timestep Callback#
Function: vpi_timestep_cb
(VpiCallbacks.cpp
)
auto vpi_timestep_cb(p_cb_data cb_data_p) -> PLI_INT32
Trigger:
cbAfterDelay
with delay = 0, registered byvpi_port_change_cb
Purpose: Adds a new timestep to NGSPICE and coordinates synchronization
Key Actions:
Add NGSPICE Timestep (if
add_ngspice_timestep == true
):g_time_barrier.update_no_wait(SPICE_ENGINE_ID, current_time); g_time_barrier.set_needs_redo(true);
Informs NGSPICE to add a timestep at the current VPI time
Sets redo flag to signal NGSPICE needs to recalculate
Update VPI Time:
g_time_barrier.update(HDL_ENGINE_ID, current_time + 1);
Updates HDL engine time and waits for NGSPICE synchronization
Update Digital Inputs:
g_interface->update_all_digital_inputs();
Applies new digital input values to SPICE simulation
Update Digital Outputs:
g_interface->set_digital_output();
Propagates analog results back to digital domain
Step 3: NGSPICE Synchronization#
Function: ng_sync
(NgSpiceCallbacks.cpp#L18
)
int ng_sync(double actual_time, double *delta_time, double old_delta_time,
int redostep, int identification_number, int location, void *user_data)
Trigger: Called by NGSPICE at each simulation step
Location: NgSpiceCallbacks.cpp:18
Purpose: Handles redo operations and synchronizes NGSPICE with VPI timing
Key Behaviors:
Redo Handling (when
location == 1
andneeds_redo() == true
):Calculates new delta time to backtrack to the VPI-requested time
Returns
1
to signal NGSPICE to redo the current stepResets
needs_redo
flag
End Step Processing (when
location == 0
):Updates next SPICE step time
Calls
analog_outputs_update()
to read analog results
Step 4: NGSPICE Data Source Callback#
Function: ng_srcdata
(NgSpiceCallbacks.cpp
)
int ng_srcdata(double *vp, double time, char *source, int id, void *udp)
Trigger: Called by NGSPICE to get input signal values
Purpose: Provides current digital input values to NGSPICE
Key Actions:
Updates SPICE engine time (if not in redo mode) for next event -> will create next
cbAfterDelay
Sets analog input values from digital signals via
g_interface->set_analog_input()
Step 5: Wait for Timestep Completion#
Function: vpi_timestep_cb
(VpiCallbacks.cpp
) (continued)
After initiating the NGSPICE timestep:
The VPI timestep callback waits for NGSPICE to complete the step
Uses
g_time_barrier.update()
which blocks until both engines are synchronizedOnce synchronized, schedules the next timestep callback
Time Barrier Synchronization Details#
The TimeBarrier
class provides several synchronization methods:
update(engine_id, current_time)
#
Updates time for one engine and waits for the other engine to catch up
Blocks calling thread until synchronization is achieved
update_no_wait(engine_id, current_time)
#
Updates time for one engine without waiting
Used to set SPICE timesteps without blocking VPI
set_needs_redo(bool)
/ needs_redo()
#
Signals when NGSPICE needs to redo a simulation step
Used when VPI changes require NGSPICE to backtrack in time
set_next_spice_step_time(time)
/ get_next_spice_step_time()
#
Manages the next scheduled NGSPICE timestep
Ensures VPI callbacks are scheduled at the correct times
Timing Diagram#
VPI Time : |----1----2----3----4----5--->
| ^ ^
| | |
SPICE Time : |----1----2----3----4----5--->
| ^ ^
| | |
Actions : Port | Wait for
Change | completion
|
Add timestep +
Signal redo
Key Design Principles#
Event-Driven: Changes in digital signals trigger synchronization
Lock-Step Execution: Neither simulator advances too far ahead
Redo Capability: NGSPICE can backtrack when VPI signals change