Skip to content

Commit

Permalink
Merge pull request #31 from futurecore/faster-memory-creation
Browse files Browse the repository at this point in the history
Faster memory creation
  • Loading branch information
snim2 committed Jul 29, 2016
2 parents 384fe8e + 9ab8188 commit d9763a7
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 97 deletions.
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ exclude_lines =
# Don't complain about unimplemented code:
raise NotImplementedError

# Do not report on undocumented profiling code.
if self.profile

# Don't complain about Ctrl+c
except KeyboardInterrupt

Expand Down
2 changes: 2 additions & 0 deletions revelation/argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def cli_parser(argv, simulator, debug_enabled):
print (USAGE_TEXT % (simulator.arch_name, argv[0], argv[0],
argv[0], argv[0], argv[0]))
raise DoNotInterpretError
elif token == '--profile' or token == '-p': # Undocumented.
simulator.profile = True
elif token == '--time' or token == '-t':
simulator.collect_times = True
elif token == '--debug' or token == '-d':
Expand Down
24 changes: 21 additions & 3 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 @@ -19,17 +19,18 @@
EXIT_FILE_ERROR = 126
EXIT_CTRL_C = 130
LOG_FILENAME = 'r_trace.out'
MEMORY_SIZE = 2**32 # Global on-chip address space.


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


class Revelation(Sim):

def __init__(self):
# DO NOT call Sim.__init__() -- Revelation JIT differs from Pydgin.
self.profile = False
self.timer = .0
self.arch_name_human = 'Revelation'
self.arch_name = self.arch_name_human.lower()
self.jit_enabled = True
Expand Down Expand Up @@ -88,6 +89,7 @@ def fetch_latch(self):

def get_entry_point(self):
def entry_point(argv):
self.timer = time.time()
if self.jit_enabled:
set_param(self.jitdriver, 'trace_limit', self.default_trace_limit)
try:
Expand All @@ -104,6 +106,10 @@ def entry_point(argv):
except IOError:
print 'Could not open file %s' % fname
return EXIT_FILE_ERROR
if self.profile:
timer = time.time()
print 'CLI parser took: %fs' % (timer - self.timer)
self.timer = timer
self.init_state(elf_file, fname, False)
for state in self.states: # FIXME: Interleaved log.
self.debug.set_state(state)
Expand Down Expand Up @@ -280,7 +286,15 @@ def init_state(self, elf_file, filename, testbin, is_test=False):
if self.debug.enabled_flags:
print 'Trace will be written to: %s.' % LOG_FILENAME
self.logger = Logger(LOG_FILENAME)
if self.profile:
timer = time.time()
print 'Debugging set up took: %fs' % (timer - self.timer)
self.timer = timer
self.memory = new_memory(self.logger)
if self.profile:
timer = time.time()
print 'Memory creation took: %fs' % (timer - self.timer)
self.timer = timer
for ncore in xrange(self.rows * self.cols):
coreid = self.first_core + ncore
print 'Loading program %s on to core %s' % (filename, hex(coreid))
Expand All @@ -290,6 +304,10 @@ def init_state(self, elf_file, filename, testbin, is_test=False):
self.hardware_loops.append(False)
self.states.append(State(self.memory, self.debug,
logger=self.logger, coreid=coreid))
if self.profile:
timer = time.time()
print 'ELF file loader took: %fs' % (timer - self.timer)
self.timer = timer


init_sim(Revelation())
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
13 changes: 8 additions & 5 deletions revelation/test/test_argument_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from revelation.argument_parser import USAGE_TEXT, DoNotInterpretError
from revelation.argument_parser import USAGE_TEXT
from revelation.sim import Revelation

import os.path
Expand Down Expand Up @@ -67,16 +67,19 @@ def test_argv_debug_flags(capfd):
assert flag in revelation.debug.enabled_flags


@pytest.mark.parametrize('argv', [('sim.py', '--time', ELF_FILE),
('sim.py', '-t', ELF_FILE)])
def test_argv_flags_with_no_args(argv, capfd):
@pytest.mark.parametrize('argv,attribute',
[(('sim.py', '--time', ELF_FILE), 'collect_times'),
(('sim.py', '-t', ELF_FILE), 'collect_times'),
(('sim.py', '--profile', ELF_FILE), 'profile'),
(('sim.py', '-p', ELF_FILE), 'profile'),])
def test_argv_flags_with_no_args(argv, attribute, capfd):
revelation = Revelation()
entry_point = revelation.get_entry_point()
retval = entry_point(argv)
assert retval == 0 # Exit success.
_, err = capfd.readouterr()
assert err == ''
assert revelation.collect_times
assert getattr(revelation, attribute)


@pytest.mark.parametrize('argv,expected',
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 d9763a7

Please sign in to comment.