2026-07-10 10:38:57 +02:00

180 lines
6.3 KiB
Python

import math
from ScEpTIC import ConfigurationException
from ScEpTIC.emulator.energy import energy_utils
from ScEpTIC.emulator.energy.mcu import MCUEnergyModel
from ScEpTIC.emulator.energy.mcu.options import MCUClockCycleAction
from ScEpTIC.emulator.energy.mcu.datasheets.msp430g2553_utils.lookup_table import resistance_lookup_table
from ScEpTIC.emulator.energy.mcu.datasheets.msp430g2553_utils.voltage_lookup_hash import voltage_lookup_hash
class MSP430GEnergyModel(MCUEnergyModel):
"""
MCU energy model for MSP430-G series from Texas Instruments
"""
def _get_MCU_frequency(self, frequency_data):
"""
:param frequency_data: MCU datasheet information of a specific clock frequency
:return: clock frequency
"""
return energy_utils.str_to_float(frequency_data["frequency"])
def _get_MCU_nominal_v(self, frequency_data):
"""
:param frequency_data: MCU datasheet information of a specific clock frequency
:return: the nominal voltage at which the current draws of the MCU were measured
"""
return energy_utils.str_to_float(frequency_data['V'])
def _get_MCU_min_v(self, frequency_data):
"""
:param frequency_data: MCU datasheet information of a specific clock frequency
:return: the minimum operating voltage of the MCU at a given clock frequency
"""
return energy_utils.str_to_float(frequency_data['V_min'])
def _calculate_MCU_equivalent_r(self, frequency_name, frequency_data):
"""
:param frequency_name: operating frequency name
:param frequency_data: MCU datasheet information of a specific clock frequency
:return: the equivalent resistance of the MCU in the various operating conditions.
"""
data = {}
nominal_v = self._get_MCU_nominal_v(frequency_data)
current_draws = self._calculate_MCU_I(frequency_data)
for operating_mode, current_draw in current_draws.items():
data[operating_mode] = energy_utils.equivalent_resistance(nominal_v, current_draw)
return data
def _calculate_MCU_I(self, frequency_data):
"""
Calculates the current draw of the MCU in the various operating conditions.
:param frequency_data: MCU datasheet information of a specific clock frequency
"""
i = energy_utils.str_to_float(frequency_data['I'])
# the datasheet does not provide sufficient information to differentiate clock cycles accessing SRAM from clock cycles that doesn't.
data = {
MCUClockCycleAction.NO_MEMORY_ACCESS: i,
MCUClockCycleAction.I2C_ACCESS: i,
MCUClockCycleAction.SPI_ACCESS: i,
MCUClockCycleAction.LPM_ENTER: i,
MCUClockCycleAction.LPM_EXIT: i,
MCUClockCycleAction.VOLATILE_MEMORY_ACCESS: i,
}
return data
def _calculate_ADC_equivalent_r(self, adc_data):
"""
:param adc_data: ADC datasheet information
:return: the equivalent resistance of the ADC
"""
nominal_v = self._get_ADC_nominal_v(adc_data)
current_draw = self._get_ADC_I(adc_data)
return energy_utils.equivalent_resistance(nominal_v, current_draw)
def _calculate_MCU_LPM_R(self, lpm_data):
"""
:param lpm_data: LPM datasheet information
:return: the equivalent resistance of the MCU in LPM
"""
v_lpm = energy_utils.str_to_float(lpm_data['V'])
i_lpm = energy_utils.str_to_float(lpm_data['I'])
return energy_utils.equivalent_resistance(v_lpm, i_lpm)
def _get_MCU_LPM_V_min(self, lpm_data):
"""
:param lpm_data: LPM datasheet information
:return: the minimum voltage required in LPM
"""
return energy_utils.str_to_float(lpm_data['V_min'])
def _get_MCU_LPM_t_wakeup(self, lpm_data):
"""
:param lpm_data: LPM datasheet information
:return: the wakeup time from LPM to active mode
"""
return energy_utils.str_to_float(lpm_data['t_wakeup'])
def _get_ADC_nominal_v(self, adc_data):
"""
:param adc_data: ADC datasheet information
:return: the nominal voltage at which the current draw of the ADC was measured
"""
return energy_utils.str_to_float(adc_data['V'])
def _get_ADC_min_v(self, adc_data):
"""
:param adc_data: ADC datasheet information
:return: the minimum operating voltage of the ADC
"""
return energy_utils.str_to_float(adc_data['V_min'])
def _calculate_ADC_wait_cycles(self, adc_data, frequency):
"""
Calculates the number of cycles to activate the ADC, wait for its operativity, retrieve data, and turn it off
:param adc_data: ADC datasheet information
:param frequency: MCU frequency
:return: the wait cycles
"""
# Time to start the ADC
t_off_on = energy_utils.str_to_float(adc_data['T_off_on'])
# Time to sample data
t_sample = energy_utils.str_to_float(adc_data['T_sampling'])
n_off_on = math.ceil(t_off_on * frequency)
n_sample = math.ceil(t_sample * frequency)
return int(n_off_on + n_sample)
def _get_ADC_instructions(self, adc_data):
"""
:param adc_data: ADC datasheet information
:return: the instructions executed to turn on the ADC, retrieve data, and turn it off
"""
# Instructions to init the ADC
n_init = energy_utils.str_to_int(adc_data['N_init'])
# Instructions to transfer data
n_transfer = energy_utils.str_to_int(adc_data['N_transfer_ops'])
# Instructions to turn off the ADC
n_off = energy_utils.str_to_int(adc_data['N_off'])
return {"on": n_init, "transfer": n_transfer, "off": n_off}
def _get_ADC_I(self, adc_data):
"""
:param adc_data: ADC datasheet information
:return: the current draw of the ADC
"""
if self.ADC_I_TO_CONSIDER == 'min':
return energy_utils.str_to_float(adc_data['I_min'])
elif self.ADC_I_TO_CONSIDER == 'max':
return energy_utils.str_to_float(adc_data['I_max'])
elif self.ADC_I_TO_CONSIDER == 'avg':
I_min = energy_utils.str_to_float(adc_data['I_min'])
I_max = energy_utils.str_to_float(adc_data['I_max'])
return (I_min + I_max) / 2.0