sceptic-biomem/ScEpTIC/analysis/utils/saved_state_calculator.py
2026-07-10 10:38:57 +02:00

159 lines
6.2 KiB
Python

from ScEpTIC.AST.misc.virtual_memory_enum import VirtualMemoryEnum
from ScEpTIC.emulator.energy.mcu import MCUClockCycleAction
class SavedStateCalculator:
"""
Collection of methods that help the calculation of state-saving operations cost.
"""
def __init__(self, vm):
"""
:param vm: ScEpTIC vm
"""
self.vm = vm
virtual_memory_enabled = 'virtual_memory' in self.vm.state.transformations
# overwrite functions
if virtual_memory_enabled:
self.get_memory_access_mcu_action = self.get_virtual_memory_mcu_action
self.get_call_ret_mcu_action = self.get_virtual_memory_mcu_action
self.get_saved_registers = self.virtual_memory_get_saved_registers
self.get_saved_memory_cells = self.virtual_memory_calculate_saved_memory_cells
else:
self.get_saved_memory_cells = self.calculate_saved_memory_cells
def virtual_memory_get_saved_registers(self, state_save_instr):
"""
:param state_save_instr: the state saving instruction
:return: if the state-save operation saves only the program counter and the number of registers
"""
save_only_pc = not state_save_instr.checkpoint_save_esp
n_registers = len(state_save_instr.checkpoint_save_regs)
return save_only_pc, n_registers
def get_virtual_memory_mcu_action(self, instruction):
"""
:param instruction: an instruction
:return: the MCUClockCycleAction of the memory access
"""
if instruction.virtual_memory_target == VirtualMemoryEnum.NON_VOLATILE:
return MCUClockCycleAction.NON_VOLATILE_MEMORY_ACCESS, None
else:
return MCUClockCycleAction.VOLATILE_MEMORY_ACCESS, None
def get_memory_access_mcu_action(self, instruction):
"""
:param instruction: a load or store instruction
:return: the MCUClockCycleAction of the memory access, the physical memory name (None if not a physical memory access)
"""
address = instruction.get_memory_address()
prefix = self.vm.state.memory.extract_prefix_from_address(address)
# Physical memory access
if prefix in self.vm.state.memory.mmus:
return MCUClockCycleAction.PHYSICAL_MEMORY_ACCESS, self.vm.state.memory.mmus[prefix].get_name()
# Non-volatile memory access
elif prefix in self.vm.state.memory.nvm_prefixes:
return MCUClockCycleAction.NON_VOLATILE_MEMORY_ACCESS, None
# Volatile memory access
else:
return MCUClockCycleAction.VOLATILE_MEMORY_ACCESS, None
def get_saved_registers(self, state_save_instr):
"""
:param state_save_instr: the state saving instruction
:return: if the state-save operation saves only the program counter and the number of registers
"""
save_only_pc = False
n_registers = self.vm.state.config.register_file.n_physical_registers
return save_only_pc, n_registers
def get_call_ret_mcu_action(self, instruction):
"""
:param instruction: a call or ret instruction
:return: the MCUClockCycleAction of the instruction memory accesses, the physical memory name (None if not a physical memory access)
"""
# Physical memory access
if self.vm.state.memory.mmu_targets['stack'] is not None:
return MCUClockCycleAction.PHYSICAL_MEMORY_ACCESS, self.vm.state.memory.mmu_targets['stack'].get_name()
# Non-volatile memory access
if self.vm.state.memory.nvm_content['stack']:
return MCUClockCycleAction.NON_VOLATILE_MEMORY_ACCESS, None
# Volatile memory access
else:
return MCUClockCycleAction.VOLATILE_MEMORY_ACCESS, None
def virtual_memory_calculate_saved_memory_cells(self, state_save_instr):
"""
:param state_save_instr: the state saving instruction
:return: the number of memory cells that need to be preserved
"""
if state_save_instr.checkpoint_save_ram:
return self.calculate_saved_memory_cells(state_save_instr)
return 0
def calculate_saved_memory_cells(self, state_save_instr):
"""
:param state_save_instr: the state saving instruction
:return: the number of memory cells that need to be preserved
"""
ignore_str_bss = '@.str'
n_global_vars_memory_cells = 0
n_stack_memory_cells = 0
n_heap_memory_cells = 0
# Global variables
if self.vm.state_retention.restore_volatile_gst:
gst = self.vm.state.memory.gst._get_gst_from_ram_name('volatile')
# gst may be disabled
if gst is not None:
# Real cells -> the ones without @.str
gst_cells = list(filter(lambda x: x.metadata not in ignore_str_bss, gst._memory.values()))
n_global_vars_memory_cells = len(gst_cells)
#for memory_cell in gst._memory.values():
# if ignore_str_bss not in memory_cell.metadata:
# n_global_vars_memory_cells += 1
# Stack
if self.vm.state_retention.restore_stack and self.vm.state.memory.memory_positions['stack'] == 'volatile':
stack = self.vm.state.memory.stack
# Active cells -> the ones whose address is not exceding stack.top_address
active_cells = list(filter(lambda x: x < stack.top_address, stack._memory.keys()))
n_stack_memory_cells = len(active_cells)
#for address, memory_cell in sorted(stack._memory.items()):
# if address < stack_pointer:
# n_stack_memory_cells += 1
# Heap
if self.vm.state_retention.restore_heap and self.vm.state.memory.memory_positions['heap'] == 'volatile':
heap = self.vm.state.memory.heap
# Active cells -> the ones not marked as garbage
active_cells = list(filter(lambda x: not x.garbage, heap._memory.values()))
n_heap_memory_cells = len(active_cells)
#for memory_cell in heap._memory.values():
# if not memory_cell.garbage:
# n_heap_memory_cells += 1
return n_global_vars_memory_cells + n_stack_memory_cells + n_heap_memory_cells