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

119 lines
5.1 KiB
Plaintext

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)