Skip to content

Commit

Permalink
Use sparse memory model. Each block is 32MB - the same size as a sing…
Browse files Browse the repository at this point in the history
…le core-local memory, or default shared RAM. This is intended to speed up short running programs, who spend most of their time constructing blank memory.
  • Loading branch information
Sarah Mount committed Jul 28, 2016
1 parent bfe05e6 commit 1cea27d
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 91 deletions.
4 changes: 2 additions & 2 deletions revelation/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from revelation.isa import decode, reg_map
from revelation.logger import Logger
from revelation.machine import State
from revelation.storage import MemoryFactory
from revelation.storage import Memory

import time

Expand All @@ -23,7 +23,7 @@


def new_memory(logger):
return MemoryFactory(size=MEMORY_SIZE, logger=logger)
return Memory(block_size=2**20, logger=logger)


class Revelation(Sim):
Expand Down
94 changes: 17 additions & 77 deletions revelation/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from pydgin.debug import Debug, pad, pad_hex
from pydgin.jit import elidable, unroll_safe, hint
from pydgin.storage import _ByteMemory
from pydgin.utils import specialize


Expand Down Expand Up @@ -30,114 +29,60 @@ def get_register_size_by_address(register_address):
return size


def MemoryFactory(data=None, size=2**10, logger=None):
"""Choose memory model depending on whether we are translated.
"""
try:
from rpython.rlib.objectmodel import we_are_translated
sparse_storage = not we_are_translated()
except ImportError:
sparse_storage = True
if not sparse_storage:
return _RevelationByteMemory(data, size, logger)
else:
print "NOTE: Using sparse storage"
return _SparseMemory(_ByteMemory, logger=logger)


class _RevelationByteMemory(object):
"""Memory model adapted from Pydgin.
Re-map addresses to core-local memory, deal with memory aliases.
class _BlockMemory(object):
"""32MB block of memory, initialised to zero.
"""

def __init__(self, data=None, size=2**10, logger=None):
def __init__(self, size=2**10, logger=None):
"""Initialise all memory to zero, as we don't know which memory.
segments might hold memory-mapped registers.
"""
self.data = data if data else ['\0'] * size
self.data = ['\0'] * size
self.size = len(self.data)
self.debug = Debug()
self.logger = logger

@unroll_safe
def read(self, start_addr, num_bytes, from_core=0x808, quiet=False):
if is_local_address(start_addr):
start_addr |= (from_core << 20)
def read(self, start_addr, num_bytes):
value = 0
for i in range(num_bytes - 1, -1, -1):
value = value << 8
value = value | ord(self.data[start_addr + i])
if (self.debug.enabled('mem') and self.logger and
not is_register_address(start_addr)):
self.logger.log(' :: RD.MEM[%s] = %s' %\
(pad_hex(start_addr), pad_hex(value)))
return value

@elidable
def iread(self, start_addr, num_bytes, from_core=0x808, quiet=False):
def iread(self, start_addr, num_bytes):
"""This is instruction read, which is otherwise identical to read. The
only difference is the elidable annotation, which we assume the
instructions are not modified (no side effects, assumes the addresses
correspond to the same instructions).
"""
if is_local_address(start_addr):
start_addr |= (from_core << 20)
value = 0
for i in range(num_bytes - 1, -1, -1):
value = value << 8
value = value | ord(self.data[start_addr + i])
return value

@unroll_safe
def write(self, start_addr, num_bytes, value, from_core=0x808, quiet=False):
if is_local_address(start_addr):
start_addr |= (from_core << 20)
# Deal with register writes that are aliases to other locations. Better
# not to put these in the instruction semantics, as the aliased
# registers may be written to by a variety of instructions, and accessed
# as either registers or memory locations.
coreid_mask = start_addr & 0xfff00000
if start_addr & 0xfffff == 0xf042c: # ILATST
ilat = self.read(coreid_mask | 0xf0428, 4) & 0x3ff
ilat |= (value & ((2 << 10) - 1))
self.write(coreid_mask | 0xf0428, 4, ilat)
elif start_addr & 0xfffff == 0xf0430: # ILATCL
ilat = self.read(coreid_mask | 0xf0428, 4) & 0x3ff
ilat &= ~(value & ((2 << 10) - 1))
self.write(coreid_mask | 0xf0428, 4, ilat)
elif start_addr & 0xfffff == 0xf0440: # FSTATUS
status = self.read(coreid_mask | 0xf0404, 4)
status |= (value & 0xfffffffc) # Can't write to lowest 2 bits.
self.write(coreid_mask | 0xf0404, 4, status)
def write(self, start_addr, num_bytes, value, from_core=0x808):
for i in range(num_bytes):
self.data[start_addr + i] = chr(value & 0xff)
value = value >> 8
if (self.debug.enabled('mem') and self.logger and not quiet and
not is_register_address(start_addr)):
self.logger.log(' :: WR.MEM[%s] = %s' % \
(pad_hex(start_addr), pad_hex(value)))


class _SparseMemory(object):
class Memory(object):
"""Sparse memory model adapted from Pydgin.
"""
_immutable_fields_ = ['BlockMemory', 'block_size', 'addr_mask', 'block_mask',
'logger']
_immutable_fields_ = ['block_size', 'addr_mask', 'block_mask', 'logger']

def __init__(self, BlockMemory, block_size=2**10, logger=None):
self.BlockMemory = BlockMemory
def __init__(self, block_size=2**20, logger=None):
self.block_size = block_size
self.debug = Debug()
self.logger = logger
self.addr_mask = block_size - 1
self.block_mask = 0xffffffff ^ self.addr_mask
print 'sparse memory size %x addr mask %x block mask %x' \
% (self.block_size, self.addr_mask, self.block_mask)
self.block_dict = {}

def add_block(self, block_addr):
self.block_dict[block_addr] = self.BlockMemory(data=['\0'] * self.block_size,
size=self.block_size)
self.block_dict[block_addr] = _BlockMemory(size=self.block_size)

@elidable
def get_block_mem(self, block_addr):
Expand All @@ -147,7 +92,7 @@ def get_block_mem(self, block_addr):
return block_mem

@elidable
def iread(self, start_addr, num_bytes, from_core=0x808, quiet=False):
def iread(self, start_addr, num_bytes, from_core=0x808):
if is_local_address(start_addr):
start_addr |= (from_core << 20)
start_addr = hint(start_addr, promote=True)
Expand All @@ -173,19 +118,13 @@ def iread(self, start_addr, num_bytes, from_core=0x808, quiet=False):
value = value1 | (value2 << (num_bytes1 * 8))
return value

def read(self, start_addr, num_bytes, from_core=0x808, quiet=False):
def read(self, start_addr, num_bytes, from_core=0x808):
if is_local_address(start_addr):
start_addr |= (from_core << 20)
block_addr = self.block_mask & start_addr
block_addr = hint(block_addr, promote=True)
block_mem = self.get_block_mem(block_addr)
value = block_mem.read(start_addr & self.addr_mask, num_bytes)
masked_addr = 0xfffff & start_addr
if (self.debug.enabled('mem') and self.logger and
not is_register_address(masked_addr)):
self.logger.log(' :: RD.MEM[%s] = %s' %\
(pad_hex(start_addr), pad_hex(value)))
return value
return block_mem.read(start_addr & self.addr_mask, num_bytes)

def write(self, start_addr, num_bytes, value, from_core=0x808, quiet=False):
if is_local_address(start_addr):
Expand Down Expand Up @@ -244,7 +183,7 @@ def __init__(self, memory, coreid, logger):
def __getitem__(self, index):
address, bitsize, _ = _register_map[index]
mask = (1 << bitsize) - 1
value = self.memory.read(address, 4, from_core=self.coreid) & mask
value = self.memory.iread(address, 4, from_core=self.coreid) & mask
if self.debug.enabled('rf') and self.logger and index < 64:
self.logger.log(' :: RD.RF[%s] = %s' % (pad('%d' % index, 2),
pad_hex(value, len=self.debug_nchars)))
Expand All @@ -256,7 +195,8 @@ def __setitem__(self, index, value):
return # registers need to be accessed by instructions.
address, bitsize, _ = _register_map[index] # Lower 20 bits of address.
mask = (1 << bitsize) - 1
self.memory.write(address, 4, value & mask, from_core=self.coreid)
self.memory.write(address, 4, value & mask, from_core=self.coreid,
quiet=True)
if self.debug.enabled('rf') and self.logger and index < 64:
self.logger.log(' :: WR.RF[%s] = %s' % ((pad('%d' % index, 2),
pad_hex(value, len=self.debug_nchars))))
Expand Down
5 changes: 1 addition & 4 deletions revelation/test/test_compiled_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ def test_compiled_c_with_output(elf_file, expected, capfd):
assert not revelation.states[0].running
out, err = capfd.readouterr()
assert err == ''
expected_full = (('NOTE: Using sparse storage\n'
'sparse memory size 400 addr mask 3ff '
'block mask fffffc00\n'
'Loading program %s on to core 0x808\n' % elf_filename)
expected_full = (('Loading program %s on to core 0x808\n' % elf_filename)
+ expected)
assert out.startswith(expected_full)

Expand Down
5 changes: 1 addition & 4 deletions revelation/test/test_multicore.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ def test_two_cores_with_output(elf_file, expected, capfd):
assert not revelation.states[1].running
out, err = capfd.readouterr()
assert err == ''
expected_full = (('NOTE: Using sparse storage\n'
'sparse memory size 400 addr mask 3ff '
'block mask fffffc00\n'
'Loading program %s on to core 0x808\n'
expected_full = (('Loading program %s on to core 0x808\n'
'Loading program %s on to core 0x809\n'
% (elf_filename, elf_filename)) + expected)
assert out.startswith(expected_full)
5 changes: 1 addition & 4 deletions revelation/test/test_syscall_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ def test_open_close_syscall_layout(capfd):
assert err == ''
expected = ('open() successful.\nRead: 14 bytes.\nHello, world!\n'
'Read: 0 bytes.\nclose() successful.\n')
expected_full = (('NOTE: Using sparse storage\n'
'sparse memory size 400 addr mask 3ff '
'block mask fffffc00\n'
'Loading program %s on to core 0x808\n' % elf_filename)
expected_full = (('Loading program %s on to core 0x808\n' % elf_filename)
+ expected)
assert out.startswith(expected_full)

0 comments on commit 1cea27d

Please sign in to comment.