119 lines
5.1 KiB
Plaintext
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)
|