Solver Synchronization and Execution Semantics
Learn how the SystemC AMS solvers synchronize the TDF, LSF, and ELN models of computation with each other and the discrete-event kernel.
Solver Synchronization and Execution Semantics
Because SystemC AMS allows you to mix and match Timed Data Flow (TDF), Linear Signal Flow (LSF), Electrical Linear Networks (ELN), and standard SystemC discrete-event (DE) models, it relies on a sophisticated synchronization layer to ensure time remains consistent across all domains.
In this tutorial, we will explore how these different solvers interact, how time steps are propagated, and how data moves securely between the analog and digital worlds.
TDF as the Master Scheduler
In SystemC AMS, the TDF solver acts as the primary time-keeper for the continuous-time domains.
When you build an LSF or ELN model, those models form a system of continuous-time equations. However, a computer cannot simulate continuous time infinitely; it must discretize it. The LSF and ELN solvers derive their calculation timesteps directly from the TDF cluster they are connected to.
If you connect an ELN circuit to a TDF module that runs with a 1.0 us timestep, the ELN solver will simulate that circuit in chunks of 1.0 us to provide a synchronized output back to the TDF module.
Synchronizing with the SystemC DE Kernel
Ultimately, your AMS system will likely need to communicate with a standard SystemC discrete-event digital component (like a processor, an interrupt controller, or a TLM bus).
Synchronization between the AMS solvers and the SystemC DE kernel is done exclusively through specialized converter ports:
sca_tdf::sca_de::sca_in<T>: Reads a standardsc_core::sc_signal<T>from the DE kernel into the TDF domain.sca_tdf::sca_de::sca_out<T>: Writes a TDF sample to a standardsc_core::sc_signal<T>in the DE kernel.
The Synchronization Rules
According to the IEEE 1666.1 LRM:
- Reading from DE to TDF: When the TDF solver reads a value from a
sca_de::sca_inport during aprocessing()callback, it reads the value that was present on the SystemC signal at the first delta cycle of the current SystemC simulation time. The value is assumed to remain constant for the duration of the TDF timestep. - Writing from TDF to DE: When the TDF solver writes a sample to a
sca_de::sca_outport, the value is written to the SystemC signal at the exact corresponding SystemC time, triggering standard SystemC update phases and events (likevalue_changed_event()).
Complete Example: DE and TDF Synchronization
This complete, compilable example demonstrates a TDF module generating a mathematical waveform, connected via a converter port to a purely digital SystemC module (a simple threshold monitor) that wakes up only when the analog value crosses a boundary.
#include <systemc>
#include <systemc-ams.h>
// 1. TDF Domain: Analog Waveform Generator
SCA_TDF_MODULE(AnalogSensor) {
// A converter port: TDF driving a standard SystemC DE signal
sca_tdf::sca_de::sca_out<double> analog_out;
SCA_CTOR(AnalogSensor) {}
void set_attributes() {
set_timestep(1.0, sc_core::SC_MS); // 1 millisecond sampling
}
void processing() {
// Generate a slow 1 Hz sine wave
double t = get_time().to_seconds();
double voltage = 5.0 * std::sin(2.0 * M_PI * 1.0 * t);
// Write to the DE kernel. This schedules a SystemC event.
analog_out.write(voltage);
}
};
// 2. Digital Domain: Discrete-Event Monitor
SC_MODULE(DigitalMonitor) {
sc_core::sc_in<double> analog_in;
SC_CTOR(DigitalMonitor) {
SC_THREAD(monitor_thread);
// Wake up whenever the analog signal changes
sensitive << analog_in.value_changed_event();
dont_initialize();
}
void monitor_thread() {
bool threshold_exceeded = false;
while (true) {
double current_val = analog_in.read();
if (current_val > 4.5 && !threshold_exceeded) {
threshold_exceeded = true;
std::cout << "@ " << sc_core::sc_time_stamp()
<< " [DIGITAL ALARM]: Voltage exceeded 4.5V! (Value: "
<< current_val << "V)\n";
}
else if (current_val < 4.0 && threshold_exceeded) {
threshold_exceeded = false;
std::cout << "@ " << sc_core::sc_time_stamp()
<< " [DIGITAL CLEAR]: Voltage dropped below 4.0V.\n";
}
wait(); // Wait for the next value_changed_event
}
}
};
int sc_main(int argc, char* argv[]) {
// 3. The boundary signal: Standard SystemC sc_signal
sc_core::sc_signal<double> sig_analog_voltage("sig_analog_voltage");
// 4. Instantiate and bind
AnalogSensor sensor("sensor");
sensor.analog_out(sig_analog_voltage); // TDF writes to DE
DigitalMonitor monitor("monitor");
monitor.analog_in(sig_analog_voltage); // DE reads from DE
// Setup Tracing to observe the synchronization
sca_util::sca_trace_file* tf = sca_util::sca_create_vcd_trace_file("sync_wave");
sca_util::sca_trace(tf, sig_analog_voltage, "Sensor_Voltage");
// Run simulation for 2 seconds
std::cout << "Starting mixed-signal simulation...\n";
sc_core::sc_start(2.0, sc_core::SC_SEC);
sca_util::sca_close_vcd_trace_file(tf);
return 0;
}Event-Driven TDF Activation (Dynamic TDF)
In Dynamic TDF (introduced in SystemC AMS 2.0), a TDF module can actually suspend its static schedule and wait for a discrete-event trigger.
By using request_next_activation(port.default_event()) inside the processing() callback, a TDF module can wake up reactively when a digital signal changes. This saves immense computation power when the analog domain is otherwise idle, rather than forcing the TDF solver to calculate empty samples on a fixed timestep.
Comments and Corrections