179 lines
6.4 KiB
Python
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
|
|
|
|
|