TDF Modules, Ports, and Signals
Mastering Timed Data Flow (TDF) modules, cluster synchronization, and port rates.
AMS TDF: Modules, Ports, and Signals
The Timed Data Flow (TDF) model relies on a statically computed schedule. To achieve fast execution, the AMS solver groups connected TDF modules into clusters. Before simulation begins, the solver analyzes the cluster's topology, calculates the execution order, and assigns exact time steps to every module based on their port rates and explicit set_timestep declarations.
TDF Ports and Rates
In TDF, communication happens at discrete time steps. A module's processing method consumes a specific number of samples from its inputs and produces a specific number of samples on its outputs. This is called the Rate.
By default, the rate is 1 (one sample per processing() call). However, multi-rate systems (like decimators or interpolators) can modify these rates.
Complete Multi-Rate TDF Example
This example demonstrates a multi-rate TDF cluster. A fast sensor generates data at a high frequency, and a slow decimator averages 4 samples at a time before outputting them.
#include <systemc>
#include <systemc-ams.h>
// 1. Fast Sensor (Produces 1 sample per 10 us)
SCA_TDF_MODULE(FastSensor) {
sca_tdf::sca_out<double> out;
double val;
SCA_CTOR(FastSensor) : val(0.0) {}
void set_attributes() {
// Explicitly set the timestep for the entire cluster driver
set_timestep(10, sc_core::SC_US);
}
void processing() {
val += 1.0;
out.write(val);
std::cout << "@" << sc_core::sc_time_stamp() << " [Sensor] Produced: " << val << std::endl;
}
};
// 2. Decimator (Reads 4 samples, Outputs 1 sample)
SCA_TDF_MODULE(Decimator) {
sca_tdf::sca_in<double> in;
sca_tdf::sca_out<double> out;
SCA_CTOR(Decimator) {}
void set_attributes() {
// Set the input port rate to 4.
// This module will only execute once 4 samples are available!
in.set_rate(4);
out.set_rate(1); // Default
}
void processing() {
double sum = 0.0;
// Read the 4 accumulated samples
for (unsigned int i = 0; i < in.get_rate(); ++i) {
sum += in.read(i); // Read at index i
}
double avg = sum / in.get_rate();
out.write(avg);
std::cout << "@" << sc_core::sc_time_stamp() << " [Decimator] Output Avg: " << avg << std::endl;
}
};
// 3. Slow Consumer (Consumes 1 sample per 40 us)
SCA_TDF_MODULE(SlowConsumer) {
sca_tdf::sca_in<double> in;
SCA_CTOR(SlowConsumer) {}
void set_attributes() {
// Inherits timestep and rate (40 us timestep assigned by the solver)
}
void processing() {
std::cout << "@" << sc_core::sc_time_stamp() << " [Consumer] Received: " << in.read() << std::endl;
}
};
int sc_main(int argc, char* argv[]) {
sca_tdf::sca_signal<double> wire_fast("wire_fast");
sca_tdf::sca_signal<double> wire_slow("wire_slow");
FastSensor sensor("sensor");
Decimator decimator("decimator");
SlowConsumer consumer("consumer");
sensor.out(wire_fast);
decimator.in(wire_fast);
decimator.out(wire_slow);
consumer.in(wire_slow);
std::cout << "Starting Multi-Rate TDF Simulation..." << std::endl;
sc_core::sc_start(100, sc_core::SC_US);
return 0;
}The TDF LRM Processing Rules
set_attributes(): Used exclusively to define topological parameters before elaboration finishes:set_timestep(),set_rate(), andset_delay().initialize(): Called once after elaboration but before the firstprocessing()call. Used to initialize internal states.processing(): Called by the AMS scheduler. The method must not block. Callingwait()inside a TDFprocessing()method is a severe LRM violation and will crash the AMS linear solver, as it breaks the statically computed mathematical schedule.
Comments and Corrections