import math from ScEpTIC.emulator.energy.mcu import EnergyCalculator class MSP430FREnergyCalculator(EnergyCalculator): """ Calculates the energy parameters for MSP430-FRxxxx class MCUs """ def __init__(self, datasheet, n_registers, adc_datasheet): self.datasheet = datasheet self.n_registers = n_registers self.adc_datasheet = adc_datasheet self.voltage = self._str_to_float(self.datasheet["voltage"]) self.frequency = self._str_to_float(self.datasheet["frequency"]) self.nvm_extra_cycles = self._str_to_int(self.datasheet["n_waits"]) self._calculate_energy_parameters() self._calculate_adc_parameters() def n_min_function(self, n_writes): """ Function to calculate the minimum number of read instructions required to create a volatile copy of a memory location. Necessary for virtual_memory transformation :param n_writes: number of writes required for creating a volatile copy :return: number of minimum reads """ volatile_access = self.energy_volatile_memory_access + self.energy_clock_cycle non_volatile_access = self.energy_non_volatile_memory_access + self.energy_clock_cycle numerator = volatile_access * float(n_writes) denominator = non_volatile_access * float(1 + self.nvm_extra_cycles) - volatile_access value = float(numerator) / float(denominator) return math.floor(value) def _calculate_energy_parameters(self): """ Calculates the energy consumption of: - clock cycle - volatile memory access (does not account for energy consumption of clock cycle) - non-volatile memory access (does not account for energy consumption of clock cycle) """ I_am_ram = self._str_to_float(self.datasheet["I_am_ram"]) I_am_fram = self._str_to_float(self.datasheet["I_am_fram"]) I_am_fram_uni = self._str_to_float(self.datasheet["I_am_fram_uni"]) I_am_fram_hit = self._str_to_float(self.datasheet["I_am_fram_hit"]) I_am_fram_miss = 1 - I_am_fram_hit # I_am_ram and I_am_fram_uni account for 2 accesses (instruction and data) per clock cycle # Identify energy consumption of single SRAM/FRAM access to volatile/non-volatile memory I_ram = I_am_ram / 2.0 self.energy_volatile_memory_access = self._I_to_e(I_ram, self.voltage, self.frequency) I_fram = I_am_fram_uni / 2.0 self.energy_non_volatile_memory_access = self._I_to_e(I_fram, self.voltage, self.frequency) # Identify cost of clock cycle # I_am_fram accounts for a non-volatile access and a volatile access (+ clock cycle overhead) # To identify clock cycle current consumption we need to subtract I_ram (fram access) from I_am_fram # as program is in FRAM, but data can be in SRAM/FRAM I_cycle = I_am_fram - I_ram # Need to account for cache hit ratio -> when does not hit, we need to account for current consumption of the extra n_waits cycles # Hit=0.75 -> 75% times we use only 1 cycle, 25% times we use 1 cycle + n_waits I_cycle = I_cycle * (I_am_fram_hit * 1 + I_am_fram_miss * (1 + self.nvm_extra_cycles)) self.energy_clock_cycle = self._I_to_e(I_cycle, self.voltage, self.frequency) # % increase of a non-volatile access w.r.t. a volatile memory access energy_volatile = self.energy_clock_cycle + self.energy_volatile_memory_access energy_non_volatile = self.energy_clock_cycle + self.energy_non_volatile_memory_access self.non_volatile_increase = float(energy_non_volatile / energy_volatile) - 1.0 def _calculate_adc_parameters(self): # ADC init N_init = self._str_to_int(self.adc_datasheet['N_init']) # Wait for init -> N clock cycles = Time * Frequency T_off_on = self._str_to_float(self.adc_datasheet['T_off_on']) N_off_on = math.ceil(float(T_off_on) * float(self.frequency)) # Wait for sample ready -> N clock cycles = Time * Frequency T_sampling = self._str_to_float(self.adc_datasheet['T_sampling']) N_sampling = math.ceil(float(T_sampling) * float(self.frequency)) # Get sample N_transfer_ops = self._str_to_int(self.adc_datasheet['N_transfer_ops']) # Set ADC to off N_off = self._str_to_int(self.adc_datasheet['N_off']) # cycles = sum of all N parameters self.adc_active_cycles = N_init + N_off_on + N_sampling + N_transfer_ops + N_off # instructions executed self.adc_instructions = N_init + N_transfer_ops + N_off I_min = self._str_to_float(self.adc_datasheet['I_min']) I_max = self._str_to_float(self.adc_datasheet['I_max']) I_avg = (I_min + I_max) / 2.0 I_to_consider = self.adc_datasheet['I_to_consider'] if I_to_consider == 'min': I_target = I_min elif I_to_consider == 'agv': I_target = I_avg else: I_target = I_max # extra energy consumption per clock cycle while the ADC is on self.energy_clock_cycle_adc = self._I_to_e(I_target, self.voltage, self.frequency)