Analysis Example

The directory sample contains a working example of the energy analysis. The file run.py provides a complete example of ScEpTIC configuration:

import ScEpTIC
from ScEpTIC.config import ScEpTICConfig
from ScEpTIC.analysis.options import AnalysisResultFormat

from ScEpTIC.emulator.energy.buffer.capacitor import CapacitorModel
from ScEpTIC.emulator.energy.energy_harvester.charge_booster import ChargeBoosterEnergyHarvester
from ScEpTIC.emulator.energy.energy_source.time_series_energy_source import TimeSeriesEnergySource
from ScEpTIC.emulator.energy.mcu.msp430fr import MSP430FREnergyModel
from ScEpTIC.emulator.energy.mcu.options import MCUPowerState, ADCPowerState
from ScEpTIC.emulator.energy.mcu_peripheral.camera_0V7620 import Camera0V7620Model
from ScEpTIC.emulator.energy.mcu_peripheral.cc1101 import CC1101Model
from ScEpTIC.emulator.energy.options import ComponentVoltageSource
from ScEpTIC.emulator.energy.options import PowerOffCondition
from ScEpTIC.emulator.energy.power_state_event import PowerStateEvent
from ScEpTIC.emulator.energy.state_retention.internal_checkpoint import InternalNVMCheckpointEnergyModel
from ScEpTIC.emulator.energy.system_energy_model import SystemEnergyModel
from ScEpTIC.emulator.energy.timekeeper import TimekeeperModel
from ScEpTIC.emulator.energy.voltage_regulator.MAX20361 import MAX20361

# Configuration params
config = {
    'capacitance': '10u',
    'cap_v_max': 5.0,
    'mcu_v_on': 3.6,
    'mcu_model': 'msp430fr5969',
    'mcu_cache_hit': 0.85,
    'mcu_lpm': 'LPM4',
    'mcu_clock_frequency': '16MHz',
    'energy_source': 'rf_mementos',
    'load_resistance': '30K',
    'harvester_resistance': '1',
}

# RF Energy Source
energy_source = TimeSeriesEnergySource(config['energy_source'])
energy_source.set_load_resistance(config['load_resistance'])
energy_source.restart_on_trace_end = True

# Charge Booster Energy Harvester
charge_booster = MAX20361()
# Voltage 1.5% higher than MCU Von to ensure power on
charge_booster.set_output_voltage(config['mcu_v_on'] * 1.015)
energy_harvester = ChargeBoosterEnergyHarvester(charge_booster, config['harvester_resistance'])
energy_harvester.attach_energy_source(energy_source)

# Energy buffer
energy_buffer = CapacitorModel(config['capacitance'], config['cap_v_max'], None)
# Start with full energy buffer
energy_buffer.set_voltage(config['mcu_v_on'])

# MCU
mcu = MSP430FREnergyModel(config['mcu_model'], instruction_cache_hit_ratio=config['mcu_cache_hit'])
mcu.set_frequency(config['mcu_clock_frequency'])
mcu.set_target_lpm(config['mcu_lpm'])
mcu.set_v_on(config['mcu_v_on'])
mcu.set_mcu_state(MCUPowerState.ON)
mcu.set_adc_state(ADCPowerState.OFF)

# Timekeeper
timekeeper = TimekeeperModel()

# Peripherals
radio = CC1101Model()
camera = Camera0V7620Model()

# System model
system_model = SystemEnergyModel()
system_model.attach_energy_buffer(energy_buffer)
system_model.set_power_off_condition(PowerOffCondition.POWER_STATE_EVENT)
system_model.add_power_off_event(PowerStateEvent.MCU_OFF)
system_model.attach_energy_source_model(energy_source)
system_model.attach_energy_harvester(energy_harvester)
system_model.attach_mcu(mcu)
# Do not use a voltage regulator for the system
system_model.attach_voltage_regulator(None)

system_model.attach_timekeeper(timekeeper)
system_model.attach_component('cc1101', radio, ComponentVoltageSource.ENERGY_BUFFER)
system_model.attach_component('0V7620', camera, ComponentVoltageSource.ENERGY_BUFFER)

# State retention model
state_retention = InternalNVMCheckpointEnergyModel()
system_model.attach_state_retention_model(state_retention)

config = ScEpTICConfig()

# Volatile Memory
config.memory.volatile.set_config('enabled', True)
config.memory.volatile.set_config('contains_stack', True)
config.memory.volatile.set_config('contains_heap', True)
config.memory.volatile.set_config('contains_gst', True)

# Non-volatile memory
config.memory.non_volatile.set_config('enabled', True)
config.memory.non_volatile.set_config('contains_stack', False)
config.memory.non_volatile.set_config('contains_heap', False)
config.memory.non_volatile.set_config('contains_gst', True)

config.memory.set_config("gst_default_memory", "volatile")

# State-retention
config.state_retention.set_config('type', 'checkpoint')
config.state_retention.set_config('state_save_strategy', 'static_placement')
config.state_retention.set_config('restore_stack', True)
config.state_retention.set_config('restore_heap', True)
config.state_retention.set_config('restore_volatile_gst', True)
config.state_retention.set_config('restore_non_volatile_gst', False)
config.state_retention.set_config('restore_register_file', True)

# Memory check
config.memory.set_config("check_memory_size", False)

# Program
config.program.set_config('file', "source.ll")

# Results output
config.result_output.set_config("save_directory", "analysis_results/")
config.result_output.set_config("test_name", "sceptic_example")
config.result_output.set_config("dir_append_datetime", False)

config.analysis.add_config("enabled_analysis", "energy")
config.analysis.energy.set_config("system_model", system_model)
config.analysis.set_config('save_results', True)
config.analysis.add_config('results_formats', AnalysisResultFormat.TEXT)

config.custom_metrics.add_custom_metric(0, 'APP_START', True, True, True)
config.custom_metrics.add_custom_metric(1, 'APP_END', True, True, True)

vm = ScEpTIC.init(config)
vm.execute_analysis()

The code we are going to execute is the following:

int i;
// NVM var
int res[100] __attribute__((section(".DATA,.NVM")));

void main() {
    // Limit to 20s of simulated time
    while(sceptic_get_simulation_time() < 20) {
        // Metric to track starts
        sceptic_increment_custom_metric(0);
        // Simulate sensor read
        sceptic_camera_read_data(2);
        checkpoint();

        // Simulate workload
        for(int i = 0; i < 100; i++) {
            res[i] = rand() * rand() * rand() * 1000;
        }
        checkpoint();

        // Send data
        sceptic_cc1101_transmit(64, 2);
        checkpoint();

        // Metric to track finishes
        sceptic_increment_custom_metric(1);
    }
}

The corresponding LLVM IR is available in source.ll.

To run the analysis, you need to execute:

pypy3 run.py

Once the analysis terminates, ScEpTIC writes the analysis results inside a new directory, called analysis_results. The results of our analysis are located inside analysis_results/sceptic_example:

.
├── analysis
│   ├── energy
│   │   ├── energy_analysis.txt
│   │   └── termination_reason.json
│   └── energy_custom_metrics.txt
├── code
│   ├── main.txt
│   ├── rand.txt
│   ├── sceptic_camera_read_data.txt
│   ├── sceptic_cc1101_transmit.txt
│   └── sceptic_get_simulation_time.txt
└── states
    └── energy.txt

Inside analysis/energy/ there are two files: energy_analysis.txt contains system timing and energy consumption metrics, whereas termination_reason.json contains information on the simulation termination reason. Instead, analysis/energy_custom_metrics.txt contains the value of the custom metrics we incremented using sceptic_increment_custom_metrics. We can check that the outer loop started and terminated its execution 18 times.

The code/ directory contains the ScEpTIC AST of every function used in source.c. Finally, states/energy.txt provides a dump of the register file, main memory, and system state.