sceptic-biomem/ScEpTIC/emulator/memory/virtual_memory_cell.py
2026-07-10 10:38:57 +02:00

179 lines
6.4 KiB
Python

import collections
import copy
import logging
from ScEpTIC import tools
from ScEpTIC.emulator.memory.memory_access_trace import MemoryAccessTrace
from ScEpTIC.exceptions import RuntimeException
class VirtualMemoryCell:
"""
Class that represents a single memory cell.
Cells do not have a fixed dimension, but each cell refers to a single element (e.g. variable)
"""
_vmstate = None
def __init__(self, address, address_prefix, dimension, content=None, garbage=False, alignment=None):
self.address = address
self.address_prefix = address_prefix
self.dimension = dimension
self.garbage = garbage
self.metadata = ''
self._init_lookup()
self.other_memory_trace_addresses = []
self.alignment = alignment
# Init memory cell to zero if no value provided
if content is None:
self.content = 0
else:
self.content = content
logging.debug('created {}'.format(repr(self)))
def get_bit_size(self):
return self.dimension * 8
def get_content(self):
return self.content
def set_content(self, content):
self.content = content
def _init_lookup(self):
"""
Inits/resets the lookup table of the memory cell.
"""
self.lookup = {'old_content': None, 'write_pc': None, 'write_global_clock': -1, 'memory_mapped': -1,
'memory_mapped_pc': None, 'input': tools.build_input_lookup_data(None, None)}
def remap(self, dimension):
"""
Remaps the dimension of the cell and invalidates the cell content.
"""
logging.debug('Remapping cell at address {} with dimension {} bytes.'.format(hex(self.address), dimension))
self.dimension = dimension
self._init_lookup()
self.content = None
def free(self):
"""
Frees the cell like it is done with C function "free"
"""
logging.debug('Memory cell at address {} freed.'.format(hex(self.address)))
self._init_lookup()
self.content = None
self.garbage = True
def set_lookup(self, old_content, current_pc, current_clock, memory_mapped=False):
"""
Sets lookup informations for memory anomaly checks.
"""
if not self._vmstate.do_data_anomaly_check:
return
self.lookup['old_content'] = old_content
self.lookup['write_pc'] = copy.deepcopy(current_pc)
self.lookup['write_global_clock'] = current_clock
if memory_mapped:
self.set_memory_mapped(current_pc, current_clock, self.address, self.dimension)
def set_input_lookup(self, input_lookup_data):
"""
Sets the input lookup information of the cell.
"""
self.lookup['input'] = input_lookup_data
def get_input_lookup(self):
"""
Returns the input lookup information of the cell.
"""
return self.lookup['input']
def set_memory_mapped(self, current_pc, current_clock, old_memory_address, old_dimension):
"""
Sets lookup informations for memory anomaly checks.
"""
self.lookup['memory_mapped_pc'] = copy.deepcopy(current_pc)
self.lookup['memory_mapped'] = current_clock
self.lookup['old_memory_address'] = old_memory_address
self.lookup['old_dimension'] = old_dimension
@property
def absolute_address(self):
"""
Returns the absolute address of the cell
"""
return "{}{}".format(self.address_prefix, hex(self.address))
def __str__(self):
return '[{}{} - {}{} ({} bytes)] {}{}'.format(self.address_prefix, hex(self.address), self.address_prefix,
hex(self.address + self.dimension - 1),
self.dimension, self.content,
' {GARBAGE}' if self.garbage else '')
def __repr__(self):
return 'VirtualMemoryCell({}{}, {}, {}, {}, {})'.format(self.address_prefix, hex(self.address), self.dimension,
self.content, self.garbage, self.alignment)
def __eq__(self, other):
if not isinstance(other, VirtualMemoryCell):
return False
return self.address == other.address and self.address_prefix == other.address_prefix and self.dimension == other.dimension and self.content == other.content and self.garbage == other.garbage and self.lookup == other.lookup
def collect_memory_trace(self, op_type):
# If ScEpTIC not set to collect memory traces, skip
if not self._vmstate.collect_memory_trace:
return
# If memory cell not in memory_trace_prefixes (i.e. not in NVM)
if self.address_prefix not in self._vmstate.memory_trace_prefixes:
return
if op_type not in ['read', 'write', 'allocation', 'deallocation']:
raise RuntimeException('Wrong operation type for memory trace!')
# Create memory trace
addr = self.absolute_address
clock = self._vmstate.global_clock
# Adjust for deallocation
dimension = self.dimension if op_type != 'deallocation' else None
traced_address = addr if op_type != 'deallocation' else None
if traced_address is None:
self.metadata = 'Memory address {}'.format(addr)
memory_trace = MemoryAccessTrace(clock, self._vmstate.register_file.pc, traced_address, self.content, dimension,
op_type, self.metadata)
addresses = [addr]
# on write propagate also write operations that exceeds original cell boundaries
if op_type == 'write':
addresses = addresses + self.other_memory_trace_addresses
for addr in addresses:
# Create data structure, if needed
if addr not in self._vmstate.memory_trace:
self._vmstate.memory_trace[addr] = {'read': collections.OrderedDict(),
'write': collections.OrderedDict(),
'allocation': collections.OrderedDict(),
'deallocation': collections.OrderedDict()}
# Add memory trace to collected memory traces
self._vmstate.memory_trace[addr][op_type][clock] = memory_trace