diff --git a/.coveragerc b/.coveragerc index c784898..fcf7847 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 diff --git a/revelation/argument_parser.py b/revelation/argument_parser.py index 494b91a..3c790e1 100644 --- a/revelation/argument_parser.py +++ b/revelation/argument_parser.py @@ -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': diff --git a/revelation/sim.py b/revelation/sim.py index ec83035..c668de4 100644 --- a/revelation/sim.py +++ b/revelation/sim.py @@ -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 @@ -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 @@ -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: @@ -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) @@ -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)) @@ -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()) diff --git a/revelation/storage.py b/revelation/storage.py index de8fc62..bc1845d 100644 --- a/revelation/storage.py +++ b/revelation/storage.py @@ -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 @@ -30,58 +29,32 @@ 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 @@ -89,55 +62,27 @@ def iread(self, start_addr, num_bytes, from_core=0x808, quiet=False): 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): @@ -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) @@ -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): @@ -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))) @@ -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)))) diff --git a/revelation/test/test_argument_parser.py b/revelation/test/test_argument_parser.py index a960065..225eeef 100644 --- a/revelation/test/test_argument_parser.py +++ b/revelation/test/test_argument_parser.py @@ -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 @@ -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', diff --git a/revelation/test/test_compiled_c.py b/revelation/test/test_compiled_c.py index dfc75e8..1a264ca 100644 --- a/revelation/test/test_compiled_c.py +++ b/revelation/test/test_compiled_c.py @@ -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) diff --git a/revelation/test/test_multicore.py b/revelation/test/test_multicore.py index 23ba7c2..89bb5b4 100644 --- a/revelation/test/test_multicore.py +++ b/revelation/test/test_multicore.py @@ -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) diff --git a/revelation/test/test_syscall_layout.py b/revelation/test/test_syscall_layout.py index 71ec3ec..ef30be6 100644 --- a/revelation/test/test_syscall_layout.py +++ b/revelation/test/test_syscall_layout.py @@ -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)