684 lines
27 KiB
Python
684 lines
27 KiB
Python
import copy
|
|
import gc
|
|
import logging
|
|
|
|
from ScEpTIC import tools
|
|
from ScEpTIC.emulator.intermittent_executor.anomalies import WARAnomaly, MemoryMapAnomaly, InputPolicyAnomaly
|
|
from ScEpTIC.emulator.io.input import InputManager
|
|
from ScEpTIC.exceptions import MemoryException
|
|
from ScEpTIC.emulator.memory.virtual_memory_cell import VirtualMemoryCell
|
|
|
|
|
|
class VirtualMemory:
|
|
"""
|
|
Basic representation of the memory.
|
|
It contains cells inside the dictionary _memory (the keys are the initial addresses of each cell)
|
|
"""
|
|
|
|
_vmstate = None
|
|
|
|
def __init__(self, base_address, address_prefix, mmu=None):
|
|
logging.debug('Creating {} memory space starting at address {}{}.'.format(self.__class__.__name__, address_prefix, hex(base_address)))
|
|
|
|
if base_address < 0:
|
|
raise MemoryException('[{}] Base address {} not valid! It must be >= 0.'.format(self.__class__.__name__, base_address))
|
|
|
|
self.base_address = base_address
|
|
self.top_address = base_address
|
|
self.address_prefix = address_prefix
|
|
self._memory = {}
|
|
self._mmu = mmu
|
|
|
|
if mmu is not None:
|
|
from ScEpTIC.emulator.memory.physical.physical_memory_management_unit import PhysicalMemoryManagementUnit
|
|
|
|
if not isinstance(mmu, PhysicalMemoryManagementUnit):
|
|
raise Exception(f"Memory physical model should be PhysicalMemoryManagementUnit, {mmu.__class__.__name__} was given instead")
|
|
|
|
mmu.attach_virtual_memory(self)
|
|
|
|
def get_address_prefix(self):
|
|
return self.address_prefix
|
|
|
|
|
|
def __str__(self):
|
|
dimension = self.top_address - self.base_address
|
|
return 'Virtual Memory: {}\nStarting address: {}{}\nEnding address: {}{}\nAllocated cells: {}\nOccupied memory: {} bytes'.format(self.mem_type, self.address_prefix, hex(self.base_address), self.address_prefix, hex(self.top_address), len(self._memory), dimension)
|
|
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, VirtualMemory):
|
|
return False
|
|
|
|
return self.base_address == other.base_address and self.address_prefix == other.address_prefix and self.top_address == other.top_address and self._memory == other._memory
|
|
|
|
|
|
def get_used_size(self):
|
|
"""
|
|
:return: the used size in bytes
|
|
"""
|
|
return self.top_address - self.base_address
|
|
|
|
|
|
def get_visual_dump(self):
|
|
"""
|
|
Returns a string representing the memory
|
|
"""
|
|
|
|
dump = ''
|
|
|
|
for i in sorted(self._memory.keys()):
|
|
dump += str(self._memory[i])+"\n"
|
|
|
|
if len(dump) == 0:
|
|
dump = '\n'
|
|
|
|
return dump
|
|
|
|
|
|
def dump(self):
|
|
"""
|
|
Returns a dump of the virtual memory.
|
|
"""
|
|
|
|
logging.debug('Creating dump of {}.'.format(self.mem_type))
|
|
|
|
return {
|
|
"top_address": copy.deepcopy(self.top_address),
|
|
"memory": copy.deepcopy(self._memory)
|
|
}
|
|
|
|
#return copy.deepcopy(self)
|
|
|
|
|
|
def restore(self, dump):
|
|
"""
|
|
Restores a dump of the virtual memory.
|
|
"""
|
|
|
|
logging.debug('Restoring dump of {}.'.format(self.mem_type))
|
|
|
|
del self.top_address
|
|
del self._memory
|
|
gc.collect()
|
|
|
|
#self.top_address = copy.deepcopy(dump.top_address)
|
|
#self._memory = copy.deepcopy(dump._memory)
|
|
self.top_address = copy.deepcopy(dump["top_address"])
|
|
self._memory = copy.deepcopy(dump["memory"])
|
|
|
|
|
|
def reset(self):
|
|
"""
|
|
Performs the CPU reset operation
|
|
"""
|
|
|
|
logging.info('Resetting {}'.format(self.mem_type))
|
|
|
|
del self.top_address
|
|
del self._memory
|
|
gc.collect()
|
|
|
|
if self._mmu is not None:
|
|
self.__mmu.reset()
|
|
|
|
self.top_address = self.base_address
|
|
self._memory = {}
|
|
|
|
|
|
@property
|
|
def mem_type(self):
|
|
"""
|
|
Returns the class of the memory. VirtualMemory is extended by other memory representations.
|
|
"""
|
|
|
|
return self.__class__.__name__
|
|
|
|
|
|
def convert_dimension(self, dimension, dimension_in_bits):
|
|
"""
|
|
Converts a dimension from bits to bytes, if dimension_in_bits is True.
|
|
"""
|
|
|
|
if dimension_in_bits:
|
|
if dimension % 8 != 0:
|
|
raise MemoryException('[{}] Invalid memory dimension: {} bits. Dimension must be a multiple of a byte (8 bits).'.format(self.mem_type, dimension))
|
|
|
|
# convert dimension in bytes
|
|
dimension = dimension // 8 # int division
|
|
|
|
return dimension
|
|
|
|
|
|
def get_real_address(self, address):
|
|
"""
|
|
Returns the actual address (in integer) by removing the address prefix char.
|
|
"""
|
|
|
|
try:
|
|
address = address.replace(self.address_prefix, '')
|
|
# address is in hex
|
|
return int(address, 16)
|
|
|
|
except (ValueError, AttributeError):
|
|
raise MemoryException('[{}] Invalid address {}. Address must start with {} and must be in hex format.'.format(self.mem_type, address, self.address_prefix))
|
|
|
|
def get_address_from_real_address(self, address):
|
|
return f"{self.address_prefix}{hex(address)}"
|
|
|
|
|
|
def _get_cell_base_address(self, address):
|
|
"""
|
|
Returns the start address of cell given its address (which can be the starting one or inside it).
|
|
"""
|
|
|
|
logging.debug('[{}] Getting cell base address of {}{}'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
# if at start of memory cell
|
|
if address in self._memory:
|
|
return address
|
|
|
|
# if inside memory cell
|
|
for addr in self._memory:
|
|
cell = self._memory[addr]
|
|
|
|
if cell.address < address < (cell.address + cell.dimension):
|
|
return cell.address
|
|
|
|
return None
|
|
|
|
|
|
def _rebase(self, address, dimension):
|
|
"""
|
|
Given an address, it redimansionates a cell to fit a given dimension.
|
|
"""
|
|
|
|
logging.debug('[{}] Rebasing address {}{} to {} bytes.'.format(self.mem_type, self.address_prefix, hex(address), dimension))
|
|
|
|
# dimension is already in bytes
|
|
if dimension < 1:
|
|
raise MemoryException('[{}] Unable to perform memory rebasing operation: invalid dimension {} (PC={})'.format(self.mem_type, dimension, self._vmstate.register_file.pc))
|
|
|
|
if address not in self._memory:
|
|
raise MemoryException('[{}] Unable to perform memory rebasing operation: no memory cell starts at address {}{}'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
cell = self._memory[address]
|
|
old_address = cell.address
|
|
old_dimension = cell.dimension
|
|
|
|
if cell.dimension == dimension:
|
|
cell.set_memory_mapped(self._vmstate.register_file.pc, self._vmstate.global_clock, old_address, old_dimension)
|
|
# no actual remap
|
|
return
|
|
|
|
elif dimension < cell.dimension:
|
|
# cell must be downsized.
|
|
# find the address from which the cell is not used, and its dimension
|
|
new_addr = cell.address + dimension
|
|
new_dim = cell.dimension - dimension
|
|
|
|
# create and apped the cell to the memory
|
|
unused_space = VirtualMemoryCell(new_addr, cell.address_prefix, new_dim, None, cell.garbage, cell.alignment)
|
|
unused_space.set_lookup(None, self._vmstate.register_file.pc, self._vmstate.global_clock, True)
|
|
|
|
self._memory[new_addr] = unused_space
|
|
|
|
else:
|
|
# The cell must become bigger
|
|
needed_dim = dimension - cell.dimension
|
|
next_addr = cell.address + cell.dimension
|
|
|
|
while needed_dim > 0:
|
|
|
|
if next_addr not in self._memory:
|
|
raise MemoryException('[{}] Memory Overflow: unable to extend address {}{} to {} bytes. No cell spaces available.'.format(self.mem_type, self.address_prefix, hex(address), dimension))
|
|
|
|
tmp_cell = self._memory[next_addr]
|
|
|
|
# if tmp_cell is bigger than needed_dim, just rebase it and stop
|
|
# otherwise, update needed_dim and next_address
|
|
if tmp_cell.dimension > needed_dim:
|
|
self._rebase(tmp_cell.address, needed_dim)
|
|
needed_dim = 0
|
|
|
|
else:
|
|
needed_dim = needed_dim - tmp_cell.dimension
|
|
next_addr = tmp_cell.address + tmp_cell.dimension
|
|
|
|
# Collect memory trace for remapping cell
|
|
tmp_cell.collect_memory_trace('deallocation')
|
|
|
|
# remove tmp_cell from memory (will be "inglobated" by cell)
|
|
del self._memory[tmp_cell.address]
|
|
|
|
# remap cell to wanted dimension
|
|
cell.remap(dimension)
|
|
cell.collect_memory_trace('allocation')
|
|
cell.set_memory_mapped(self._vmstate.register_file.pc, self._vmstate.global_clock, old_address, old_dimension)
|
|
|
|
|
|
def _check_input_lookup(self, cell):
|
|
"""
|
|
Verifies the input lookup for input anomalies for a given cell.
|
|
"""
|
|
|
|
if not self._vmstate.input_lookup_enabled:
|
|
return None
|
|
|
|
checkpoint_clock = self._vmstate.state_save_clock
|
|
|
|
# get lookup info from memory cell
|
|
input_lookup_data = cell.get_input_lookup()
|
|
|
|
# checkpoint_clock: [list_of_dependencies]
|
|
for clock_id in sorted(input_lookup_data.keys()):
|
|
|
|
# consistency observed
|
|
measured_consistency = InputManager.LONG_TERM if clock_id < checkpoint_clock else InputManager.MOST_RECENT
|
|
|
|
for input_name in input_lookup_data[clock_id]:
|
|
consistency = InputManager.get_consistency_model(input_name)
|
|
|
|
# if no consistency is set for considered input, skip
|
|
if consistency is None:
|
|
continue
|
|
|
|
if consistency != measured_consistency:
|
|
checkpoint_pc = self._vmstate.state_save_clock_pc_maps[clock_id]
|
|
access_pc = self._vmstate.register_file.pc
|
|
anomaly = InputPolicyAnomaly(checkpoint_pc, checkpoint_clock, access_pc, clock_id, input_name, measured_consistency, consistency)
|
|
|
|
if anomaly not in self._vmstate.anomalies:
|
|
self._vmstate.anomalies.append(anomaly)
|
|
|
|
# stats
|
|
self._vmstate.stats.anomaly_found()
|
|
|
|
# Stop test if user requires it
|
|
self._vmstate.handle_stop_request(anomaly)
|
|
|
|
|
|
def _check_for_memory_map_anomaly(self, cell):
|
|
"""
|
|
Checks for memory mapping anomalies for a given cell.
|
|
"""
|
|
|
|
if not self._vmstate.do_data_anomaly_check:
|
|
return None
|
|
|
|
if cell.address_prefix not in self._vmstate.memory_trace_prefixes or cell.address_prefix != self._vmstate.memory.address_prefixes['heap']:
|
|
return
|
|
|
|
raise_exception = True
|
|
|
|
if cell.lookup['memory_mapped'] > self._vmstate.global_clock:
|
|
if cell.lookup['old_memory_address'] != cell.address:
|
|
# address changed
|
|
anomaly = MemoryMapAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, False)
|
|
|
|
elif cell.lookup['old_dimension'] != cell.dimension:
|
|
# dimension changed
|
|
anomaly = MemoryMapAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, False)
|
|
|
|
elif cell.lookup['old_content'] != cell.content:
|
|
# content changed (freed probably)
|
|
anomaly = WARAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, False)
|
|
anomaly.from_memory_map = True
|
|
|
|
else:
|
|
anomaly = MemoryMapAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, True)
|
|
raise_exception = False
|
|
|
|
# append only if the anomaly is not in the anomaly set.
|
|
if anomaly not in self._vmstate.anomalies:
|
|
self._vmstate.anomalies.append(anomaly)
|
|
|
|
# stats
|
|
self._vmstate.stats.anomaly_found()
|
|
|
|
# check if not false-positive
|
|
if not anomaly.is_false_positive:
|
|
# Stop test if user requires it
|
|
self._vmstate.handle_stop_request(anomaly)
|
|
|
|
else:
|
|
index = self._vmstate.anomalies.index(anomaly)
|
|
self._vmstate.anomalies[index].is_false_positive &= anomaly.is_false_positive
|
|
|
|
if raise_exception:
|
|
|
|
# If dynamic checkpoint, then raise exception
|
|
if self._vmstate.raise_memory_anomaly_exceptions:
|
|
anomaly.raise_exception()
|
|
|
|
# If static checkpoint, signal it (so further analysis on subsequent instructions can be done)
|
|
else:
|
|
self._vmstate.trigger_reset_and_restore = True
|
|
|
|
|
|
def _check_for_memory_war_anomaly(self, cell):
|
|
"""
|
|
Checks for memory write-after-read anomalies for a given cell.
|
|
"""
|
|
|
|
# check for write after read hazards only if enabled.
|
|
if not self._vmstate.do_data_anomaly_check:
|
|
return None
|
|
|
|
if cell.address_prefix not in self._vmstate.memory_trace_prefixes:
|
|
return
|
|
|
|
raise_exception = False
|
|
|
|
# if write timestamp > current timestamp, possibly there is an error
|
|
if cell.lookup['write_global_clock'] is not None and cell.lookup['write_global_clock'] > self._vmstate.global_clock:
|
|
|
|
# false-positive
|
|
if cell.lookup['old_content'] == cell.content:
|
|
logging.info('{} <- {}'.format(cell.lookup['write_global_clock'], self._vmstate.global_clock))
|
|
anomaly = WARAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, True)
|
|
|
|
else:
|
|
anomaly = WARAnomaly(cell, self._vmstate.register_file.pc, self._vmstate.global_clock, False)
|
|
raise_exception = True
|
|
|
|
# append only if the anomaly is not in the anomaly set.
|
|
if anomaly not in self._vmstate.anomalies:
|
|
self._vmstate.anomalies.append(anomaly)
|
|
|
|
# stats
|
|
self._vmstate.stats.anomaly_found()
|
|
|
|
# check if not false-positive
|
|
if not anomaly.is_false_positive:
|
|
# Stop test if user requires it
|
|
self._vmstate.handle_stop_request(anomaly)
|
|
|
|
else:
|
|
index = self._vmstate.anomalies.index(anomaly)
|
|
self._vmstate.anomalies[index].is_false_positive &= anomaly.is_false_positive
|
|
|
|
if raise_exception:
|
|
|
|
# If dynamic checkpoint, then raise exception
|
|
if self._vmstate.raise_memory_anomaly_exceptions:
|
|
anomaly.raise_exception()
|
|
|
|
# If static checkpoint, signal it (so further analysis on subsequent instructions can be done)
|
|
else:
|
|
self._vmstate.trigger_reset_and_restore = True
|
|
|
|
|
|
def _get_cell(self, address, dimension, write_access):
|
|
"""
|
|
Returns the cell present at the given address.
|
|
Dimension is in bytes and is used to verify that the cell is consistent w.r.t. the requested type.
|
|
If write_access is set, the cell dimension is adjusted to fit the given one, using the rebase method.
|
|
"""
|
|
|
|
# dimension is already in bytes
|
|
logging.debug('[{}] Getting cell at address {}{}{}.'.format(self.mem_type, self.address_prefix, hex(address), ' with write access' if write_access else ''))
|
|
|
|
# If address not directly in memory, it can be contained inside a cell.
|
|
# To obtain the address, rebase the container cell to make the wanted address directly accessible.
|
|
if address not in self._memory:
|
|
|
|
# get containing cell's address
|
|
base_address = self._get_cell_base_address(address)
|
|
|
|
if base_address is None:
|
|
raise MemoryException('[{}] No memory cell starts at address {}{}'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
base_cell = self._memory[base_address]
|
|
self._check_for_memory_map_anomaly(base_cell)
|
|
|
|
# if cell not directly addressable, read must not fail but value must be None (in real life read will lead to a unattended value)
|
|
if not write_access:
|
|
cell = VirtualMemoryCell(address, self.address_prefix, dimension, None, False, None)
|
|
cell.lookup = copy.deepcopy(base_cell.lookup)
|
|
return cell
|
|
|
|
# considered only in HEAP
|
|
if base_cell.garbage:
|
|
raise MemoryException('[{}] Unable to access address {}{}. Memory location is set as garbage'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
# calculate unwanted portion and rebase to separate from the wanted portion.
|
|
new_dim = address - base_address
|
|
self._rebase(base_address, new_dim)
|
|
# now address is directly accessible in memory
|
|
|
|
cell = self._memory[address]
|
|
|
|
self._check_for_memory_map_anomaly(cell)
|
|
|
|
# considered only in HEAP
|
|
if cell.garbage:
|
|
raise MemoryException('[{}] Unable to access address {}{}. Memory location is set as garbage'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
if cell.dimension != dimension:
|
|
# if cell is not of the correct dimention, read must not fail but value must be None (in real life read will lead to a unattended value)
|
|
if not write_access:
|
|
|
|
# partial fix for in-memory accesses for structures
|
|
# e.g. 32bit memcpy integer into a {16bit, 16bit}.
|
|
# -> the problem is the memory representation, which does not allow that
|
|
# future update: binary memory representation, instead of "contextual"
|
|
if cell.dimension > dimension:
|
|
return cell
|
|
|
|
return VirtualMemoryCell(address, self.address_prefix, dimension, None, False)
|
|
|
|
# rebase to the wanted dimension
|
|
self._rebase(address, dimension)
|
|
|
|
return cell
|
|
|
|
|
|
def read(self, address, dimension, dimension_in_bits = True, resolve_address = True, ignore_mmu=False):
|
|
"""
|
|
Returns the content of a cell, given its address and dimension.
|
|
If dimension is different from the one of the cell, it returns an invalid value (None)
|
|
"""
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
dimension = self.convert_dimension(dimension, dimension_in_bits)
|
|
|
|
logging.debug('[{}] Reading {} bytes of memory at address {}{}.'.format(self.mem_type, dimension, self.address_prefix, hex(address)))
|
|
|
|
cell = self._get_cell(address, dimension, False)
|
|
|
|
self._check_for_memory_war_anomaly(cell)
|
|
|
|
self._check_input_lookup(cell)
|
|
|
|
cell.collect_memory_trace('read')
|
|
|
|
# Execute operation on physical memory through its MMU
|
|
if self._mmu is not None and not ignore_mmu:
|
|
self._mmu.memory_read(address, dimension*8)
|
|
|
|
return cell.content
|
|
|
|
|
|
def get_cells_from_address(self, address, dimension, dimension_in_bits = True, resolve_address = True):
|
|
"""
|
|
Returns the cells starting from a given address, s.t. the overall dimension is the given one.
|
|
"""
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
dimension = self.convert_dimension(dimension, dimension_in_bits)
|
|
|
|
logging.debug('[{}] Getting {} bytes of memory from address {}{}.'.format(self.mem_type, dimension, self.address_prefix, hex(address)))
|
|
|
|
cells = []
|
|
|
|
while dimension > 0:
|
|
if address not in self._memory:
|
|
break
|
|
|
|
cell = self._memory[address]
|
|
self._check_for_memory_map_anomaly(cell)
|
|
self._check_for_memory_war_anomaly(cell)
|
|
|
|
dimension -= cell.dimension
|
|
address += cell.dimension
|
|
|
|
if dimension >= 0:
|
|
cells.append(copy.deepcopy(cell))
|
|
|
|
return cells
|
|
|
|
def simulate_mmu_read_on_cells(self, cells):
|
|
if self._mmu is None:
|
|
return
|
|
|
|
for cell in cells:
|
|
self._mmu.memory_read(cell.address, cell.get_bit_size())
|
|
|
|
|
|
def set_cells_from_address(self, address, cells, resolve_address = True):
|
|
"""
|
|
Copies the content from a given list of cells starting from the given address.
|
|
"""
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
logging.debug('[{}] Setting memory from address {}{}.'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
for cell in cells:
|
|
self.write(address, cell.dimension, cell.content, False, False)
|
|
|
|
# preserve input lookup
|
|
if self._vmstate.input_lookup_enabled:
|
|
self.set_cell_input_lookup(address, copy.deepcopy(cell.get_input_lookup()), False)
|
|
|
|
address += cell.dimension
|
|
|
|
|
|
def set_cell_input_lookup(self, address, input_lookup_data, resolve_address = True):
|
|
"""
|
|
Sets the input lookup information of cell in a given address.
|
|
"""
|
|
|
|
# NB: emptiness of input lookup if input_lookup_data is [] is guaranteed by write operations
|
|
# which removes completely the lookup infos.
|
|
|
|
if len(input_lookup_data) == 0:
|
|
return
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
logging.debug('[{}] Setting memory input lookup at address {}{}.'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
if address not in self._memory:
|
|
raise MemoryException('[{}] No memory cell starts at address {}{}'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
# get cell
|
|
cell = self._memory[address]
|
|
|
|
# set input lookup
|
|
if self._vmstate.input_lookup_enabled:
|
|
cell.set_input_lookup(input_lookup_data)
|
|
|
|
|
|
def get_cell_input_lookup(self, address, resolve_address = True):
|
|
"""
|
|
Returns the input lookup information of cell in a given address.
|
|
"""
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
logging.debug('[{}] Getting memory input lookup at address {}{}.'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
if address not in self._memory:
|
|
raise MemoryException('[{}] No memory cell starts at address {}{}'.format(self.mem_type, self.address_prefix, hex(address)))
|
|
|
|
# get cell
|
|
cell = self._memory[address]
|
|
|
|
return cell.get_input_lookup()
|
|
|
|
|
|
def write(self, address, dimension, content, dimension_in_bits = True, resolve_address = True, ignore_mmu=False):
|
|
"""
|
|
Writes the given content into a cell, given its address.
|
|
If dimension is different from the one of the cell, it gets rebased.
|
|
"""
|
|
if isinstance(content, list):
|
|
raise Exception(f"Cannot write a list into memory (instrucion {self._vmstate.register_file.pc})")
|
|
|
|
if resolve_address:
|
|
address = self.get_real_address(address)
|
|
|
|
dimension = self.convert_dimension(dimension, dimension_in_bits)
|
|
|
|
logging.debug('[{}] Writing {} bytes of memory at address {}{} with content \'{}\'.'.format(self.mem_type, dimension, self.address_prefix, hex(address), content))
|
|
|
|
cell = self._get_cell(address, dimension, True)
|
|
old_content = copy.deepcopy(cell.content)
|
|
|
|
content = copy.deepcopy(content)
|
|
|
|
cell.content = content
|
|
|
|
# Execute operation on physical memory through its MMU
|
|
if self._mmu is not None and not ignore_mmu:
|
|
self._mmu.memory_write(content, address, dimension*8)
|
|
|
|
# set lookup information, but only if simulation started and only if required
|
|
if self._vmstate.global_clock > 0:
|
|
|
|
if self._vmstate.do_data_anomaly_check:
|
|
cell.set_lookup(old_content, self._vmstate.register_file.pc, self._vmstate.global_clock)
|
|
|
|
if self._vmstate.input_lookup_enabled:
|
|
cell.set_input_lookup(tools.build_input_lookup_data(None, None))
|
|
|
|
cell.collect_memory_trace('write')
|
|
|
|
|
|
def diff(self, dump):
|
|
"""
|
|
Returns the difference between the current state of the register file and the one saved inside a dump.
|
|
"""
|
|
|
|
logging.debug('[{}] Comparing current state with a given dump.'.format(self.mem_type))
|
|
|
|
if not isinstance(dump, self.__class__):
|
|
raise MemoryException('Unable to compare {} dump: {} object expected, {} given.'.format(self.mem_type, self.mem_type, dump.__class__.__name__))
|
|
|
|
diff = []
|
|
|
|
if self._memory != dump._memory:
|
|
mem_keys = list(self._memory.keys())
|
|
dump_keys = list(dump._memory.keys())
|
|
|
|
common_elements = [item for item in mem_keys if item in dump_keys]
|
|
only_mem_keys = [item for item in mem_keys if item not in dump_keys]
|
|
only_dump_keys = [item for item in dump_keys if item not in mem_keys]
|
|
|
|
for i in common_elements:
|
|
if self._memory[i] != dump._memory[i]:
|
|
address = '{}{}'.format(self.address_prefix, hex(dump._memory[i].address))
|
|
diff.append({'address': address, 'dump_value': dump._memory[i].content, 'current_value': self._memory[i].content})
|
|
|
|
for i in only_mem_keys:
|
|
address = '{}{}'.format(self.address_prefix, hex(self._memory[i].address))
|
|
diff.append({'address': address, 'dump_value': None, 'current_value': self._memory[i].content})
|
|
|
|
for i in only_dump_keys:
|
|
address = '{}{}'.format(self.address_prefix, hex(dump._memory[i].address))
|
|
diff.append({'address': address, 'dump_value': dump._memory[i].content, 'current_value': None})
|
|
|
|
if self.top_address != dump.top_address:
|
|
diff.append({'element': '{} config'.format(self.mem_type), 'element_name': 'top_address', 'dump_value': copy.deepcopy(dump.top_address), 'current_value': copy.deepcopy(self.top_address)})
|
|
|
|
return diff
|