Chapter 8: SystemC AMS

AMS Modeling Style for Virtual Platforms

How to combine SystemC AMS with a VP without turning a software platform model into a slow analog simulation.

AMS Modeling Style for Virtual Platforms

SystemC AMS can enrich a Virtual Platform (VP), but it should not accidentally change the purpose of the VP. Virtual platforms are primarily designed for fast software execution, whereas analog simulations often require fine-grained time steps that slow down simulation significantly.

When AMS Belongs in a VP

According to the SystemC AMS standard (IEEE 1666.1), you should use AMS when software-visible behavior depends on analog or signal-processing effects:

  • ADC sample streams
  • Sensor thresholds
  • PLL lock approximation
  • Power or thermal trends
  • Filters
  • Motor-control feedback

Do not use AMS merely to make the model look more advanced.

Good VP Boundary

A practical mixed VP usually has:

  • A TLM register block for software programming.
  • An AMS cluster (TDF or LSF) for signal behavior.
  • Converter ports (e.g., sca_tdf::sca_in, sca_tdf::sca_out connected to sc_core::sc_signal) for control and status between the AMS and discrete-event worlds.
  • Interrupts or status registers for software-visible results.

The software should still see registers, memory, and interrupts, modeled via standard TLM-2.0 or Simple Bus abstraction.

Complete AMS to SystemC Boundary Example

Below is a fully compilable sc_main program demonstrating how to interface an AMS TDF (Timed Data Flow) model representing an ADC with a standard SystemC discrete-event module representing a simple Virtual Platform peripheral register block.

#include <systemc>
#include <systemc-ams>
 
// 1. AMS TDF Module (The Analog / Continuous Part)
SCA_TDF_MODULE(adc_sensor) {
    // Converter port: Continuous output to Discrete Event (DE) domain
    sca_tdf::sca_out<double> analog_out; 
    
    // Internal state
    double current_val;
 
    SCA_CTOR(adc_sensor) : analog_out("analog_out"), current_val(0.0) {}
 
    void set_attributes() override {
        // Set a coarse timestep so we don't slow down the VP unnecessarily
        set_timestep(1.0, sc_core::SC_MS);
    }
 
    void processing() override {
        // Simulate a slowly changing analog value (e.g., a temperature sensor)
        current_val += 0.5;
        if (current_val > 100.0) {
            current_val = 0.0;
        }
        // Write out the analog value to the converter port
        analog_out.write(current_val);
    }
};
 
// 2. VP Peripheral (The Discrete Event / Software Visible Part)
SC_MODULE(vp_adc_peripheral) {
    // Input from the AMS domain
    sc_core::sc_in<double> analog_in; 
    
    // Interrupt output to the CPU
    sc_core::sc_out<bool> irq_out;
 
    // A simple threshold register programmable by software
    double threshold_reg;
 
    SC_CTOR(vp_adc_peripheral) : analog_in("analog_in"), irq_out("irq_out"), threshold_reg(50.0) {
        SC_METHOD(monitor_threshold);
        sensitive << analog_in; // Trigger whenever the AMS converter writes a new value
        dont_initialize();
    }
 
    void monitor_threshold() {
        double current = analog_in.read();
        
        // If the analog value crosses the software-programmed threshold, trigger an interrupt
        if (current >= threshold_reg) {
            irq_out.write(true);
            std::cout << "@ " << sc_core::sc_time_stamp() 
                      << " VP ADC: Threshold crossed! Value: " << current 
                      << ", raising IRQ." << std::endl;
        } else {
            irq_out.write(false);
        }
    }
};
 
// 3. Top-Level Integration
int sc_main(int argc, char* argv[]) {
    // Signals
    sc_core::sc_signal<double> analog_sig("analog_sig");
    sc_core::sc_signal<bool> irq_sig("irq_sig");
 
    // Instantiation
    adc_sensor ams_block("ams_block");
    vp_adc_peripheral vp_block("vp_block");
 
    // Binding
    ams_block.analog_out(analog_sig);
    vp_block.analog_in(analog_sig);
    vp_block.irq_out(irq_sig);
 
    // Run simulation
    std::cout << "Starting mixed AMS/VP simulation..." << std::endl;
    sc_core::sc_start(200.0, sc_core::SC_MS);
    std::cout << "Simulation finished." << std::endl;
 
    return 0;
}

Performance Rule

Keep AMS timesteps as coarse as the use case allows. A VP that needs to boot firmware should not spend most of its time solving unnecessary high-resolution analog detail. In the example above, a 1.0 ms timestep was used to minimize context switches between the AMS solver and the SystemC kernel.

Documentation Rule

For each AMS block in a VP, document:

  • Model of computation (e.g., TDF, LSF, ELN)
  • Timestep and rates
  • Delays
  • Boundary ports (Converter type)
  • Numerical approximations
  • Software-visible effects (IRQs, specific register behaviors)

This lets users understand what is accurate, approximate, and intentionally ignored.

Comments and Corrections