Skip to content

Commit 0ec79e0

Browse files
committed
Add timing synchronization documentation
1 parent 1732efe commit 0ec79e0

2 files changed

Lines changed: 174 additions & 5 deletions

File tree

docs/source/index.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ spi_adc
4040
% - making connections
4141
% - no instruction or technical description
4242

43-
% ```{toctree}
44-
% :caption: Explanation
45-
% :hidden: true
46-
% :maxdepth: 2
47-
% ```
43+
```{toctree}
44+
:caption: Explanation
45+
:hidden: true
46+
:maxdepth: 2
47+
48+
timing_synchronization
49+
```
4850

4951
% How-To Guides - guides that take the reader through the steps required to solve a common problem
5052
% (Example: recipe; problem-oriented)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Timing Synchronization Between NGSPICE and VPI
2+
3+
## Overview
4+
5+
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.
6+
7+
## Key Components
8+
9+
### 1. Time Barrier ([`TimeBarrier.h`](../../cpp/TimeBarrier.h))
10+
11+
The `TimeBarrier` class is the core synchronization primitive that coordinates time progression between two simulation engines:
12+
- **HDL_ENGINE_ID (0)**: Digital HDL simulator
13+
- **SPICE_ENGINE_ID (1)**: NGSPICE analog simulator
14+
15+
The barrier ensures that neither simulator advances too far ahead of the other, maintaining synchronization.
16+
17+
### 2. Global State Variables
18+
19+
- `g_time_barrier`: Global instance of `TimeBarrier<unsigned long long>`
20+
- `add_ngspice_timestep`: Flag indicating if NGSPICE needs a new timestep
21+
22+
## Timing Synchronization Flow
23+
24+
The timing synchronization follows a specific sequence when digital inputs to SPICE change:
25+
26+
### Step 1: Digital Signal Change Detection
27+
28+
**Function**: [`vpi_port_change_cb`](../../cpp/VpiCallbacks.cpp)
29+
30+
```cpp
31+
auto vpi_port_change_cb(p_cb_data cb_data_p) -> PLI_INT32
32+
```
33+
34+
- **Trigger**: `cbValueChange` callback registered for every input port from VPI to SPICE
35+
- **Purpose**: Detects when a digital signal changes that affects SPICE simulation
36+
- **Key Actions**:
37+
- Removes existing next time callbacks to prevent conflicts
38+
- Sets `add_ngspice_timestep = true` (only once per time step if multiple signals change)
39+
- Registers immediate `cbAfterDelay` callback with delay = 0
40+
41+
### Step 2: Immediate Timestep Callback
42+
43+
**Function**: [`vpi_timestep_cb`](../../cpp/VpiCallbacks.cpp)
44+
45+
```cpp
46+
auto vpi_timestep_cb(p_cb_data cb_data_p) -> PLI_INT32
47+
```
48+
49+
- **Trigger**: `cbAfterDelay` with delay = 0, registered by `vpi_port_change_cb`
50+
- **Purpose**: Adds a new timestep to NGSPICE and coordinates synchronization
51+
52+
**Key Actions**:
53+
1. **Add NGSPICE Timestep** (if `add_ngspice_timestep == true`):
54+
```cpp
55+
g_time_barrier.update_no_wait(SPICE_ENGINE_ID, current_time);
56+
g_time_barrier.set_needs_redo(true);
57+
```
58+
- Informs NGSPICE to add a timestep at the current VPI time
59+
- Sets redo flag to signal NGSPICE needs to recalculate
60+
61+
2. **Update VPI Time**:
62+
```cpp
63+
g_time_barrier.update(HDL_ENGINE_ID, current_time + 1);
64+
```
65+
- Updates HDL engine time and waits for NGSPICE synchronization
66+
67+
3. **Update Digital Inputs**:
68+
```cpp
69+
g_interface->update_all_digital_inputs();
70+
```
71+
- Applies new digital input values to SPICE simulation
72+
73+
4. **Update Digital Outputs**:
74+
```cpp
75+
g_interface->set_digital_output();
76+
```
77+
- Propagates analog results back to digital domain
78+
79+
### Step 3: NGSPICE Synchronization
80+
81+
**Function**: [`ng_sync`](../../cpp/NgSpiceCallbacks.cpp#L18)
82+
83+
```cpp
84+
int ng_sync(double actual_time, double *delta_time, double old_delta_time,
85+
int redostep, int identification_number, int location, void *user_data)
86+
```
87+
88+
- **Trigger**: Called by NGSPICE at each simulation step
89+
- **Location**: NgSpiceCallbacks.cpp:18
90+
- **Purpose**: Handles redo operations and synchronizes NGSPICE with VPI timing
91+
92+
**Key Behaviors**:
93+
94+
1. **Redo Handling** (when `location == 1` and `needs_redo() == true`):
95+
- Calculates new delta time to backtrack to the VPI-requested time
96+
- Returns `1` to signal NGSPICE to redo the current step
97+
- Resets `needs_redo` flag
98+
99+
2. **End Step Processing** (when `location == 0`):
100+
- Updates next SPICE step time
101+
- Calls `analog_outputs_update()` to read analog results
102+
103+
### Step 4: NGSPICE Data Source Callback
104+
105+
**Function**: [`ng_srcdata`](../../cpp/NgSpiceCallbacks.cpp)
106+
107+
```cpp
108+
int ng_srcdata(double *vp, double time, char *source, int id, void *udp)
109+
```
110+
111+
- **Trigger**: Called by NGSPICE to get input signal values
112+
- **Purpose**: Provides current digital input values to NGSPICE
113+
114+
**Key Actions**:
115+
- Updates SPICE engine time (if not in redo mode) for next event -> will create next `cbAfterDelay` `
116+
- Sets analog input values from digital signals via `g_interface->set_analog_input()`
117+
118+
### Step 5: Wait for Timestep Completion
119+
120+
**Function**: [`vpi_timestep_cb`](../../cpp/VpiCallbacks.cpp) (continued)
121+
122+
After initiating the NGSPICE timestep:
123+
- The VPI timestep callback waits for NGSPICE to complete the step
124+
- Uses `g_time_barrier.update()` which blocks until both engines are synchronized
125+
- Once synchronized, schedules the next timestep callback
126+
127+
## Time Barrier Synchronization Details
128+
129+
The `TimeBarrier` class provides several synchronization methods:
130+
131+
### `update(engine_id, current_time)`
132+
- Updates time for one engine and **waits** for the other engine to catch up
133+
- Blocks calling thread until synchronization is achieved
134+
135+
### `update_no_wait(engine_id, current_time)`
136+
- Updates time for one engine **without waiting**
137+
- Used to set SPICE timesteps without blocking VPI
138+
139+
### `set_needs_redo(bool)` / `needs_redo()`
140+
- Signals when NGSPICE needs to redo a simulation step
141+
- Used when VPI changes require NGSPICE to backtrack in time
142+
143+
### `set_next_spice_step_time(time)` / `get_next_spice_step_time()`
144+
- Manages the next scheduled NGSPICE timestep
145+
- Ensures VPI callbacks are scheduled at the correct times
146+
147+
## Timing Diagram
148+
149+
```
150+
VPI Time : |----1----2----3----4----5--->
151+
| ^ ^
152+
| | |
153+
SPICE Time : |----1----2----3----4----5--->
154+
| ^ ^
155+
| | |
156+
Actions : Port | Wait for
157+
Change | completion
158+
|
159+
Add timestep +
160+
Signal redo
161+
```
162+
163+
## Key Design Principles
164+
165+
1. **Event-Driven**: Changes in digital signals trigger synchronization
166+
2. **Lock-Step Execution**: Neither simulator advances too far ahead
167+
3. **Redo Capability**: NGSPICE can backtrack when VPI signals change

0 commit comments

Comments
 (0)