class RiscVFPRegisterFile(object): def __init__(self, num_regs=32, nbits=64): self.num_regs = num_regs self.regs = [r_uint(0)] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 def __getitem__(self, idx): if self.debug.enabled("rf"): print ':: RD.RF[%s] = %s' % (pad( "%d" % idx, 2), pad_hex(self.regs[idx], len=self.debug_nchars)), return self.regs[idx] @specialize.argtype(2) def __setitem__(self, idx, value): value = trim_64(value) self.regs[idx] = value if self.debug.enabled("rf"): print ':: WR.RF[%s] = %s' % (pad( "%d" % idx, 2), pad_hex(self.regs[idx], len=self.debug_nchars)), #----------------------------------------------------------------------- # print_regs #----------------------------------------------------------------------- # prints all registers (register dump) # per_row specifies the number of registers to display per row def print_regs(self, per_row=6): for c in xrange(0, self.num_regs, per_row): str = "" for r in xrange(c, min(self.num_regs, c + per_row)): str += "%s:%s " % (pad("%d" % r, 2), pad_hex(self.regs[r])) print str
def __init__(self, block_size=2**20, logger=None): self.block_size = block_size self.debug = Debug() self.logger = logger self.first_core = 0x808 self.addr_mask = block_size - 1 self.block_mask = 0xffffffff ^ self.addr_mask self.block_dict = {} self.code_blocks = []
def __init__(self, memory, coreid, logger): self.debug = Debug() self.logger = logger self.memory = memory self.coreid = coreid self.coreid_mask = coreid << 20 self.is_first_core = False self.memory.write(self.coreid_mask | 0xf0704, 4, coreid & 0xfff, from_core=coreid, quiet=True) self.num_regs = len(reg_memory_map) self.debug_nchars = 8
class RiscVFPRegisterFile( object ): def __init__( self, num_regs=32, nbits=64 ): self.num_regs = num_regs self.regs = [ r_uint(0) ] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 def __getitem__( self, idx ): if self.debug.enabled( "rf" ): print ':: RD.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( self.regs[idx], len=self.debug_nchars ) ), return self.regs[idx] @specialize.argtype(2) def __setitem__( self, idx, value ): value = trim_64(value) self.regs[idx] = value if self.debug.enabled( "rf" ): print ':: WR.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( self.regs[idx], len=self.debug_nchars ) ), #----------------------------------------------------------------------- # print_regs #----------------------------------------------------------------------- # prints all registers (register dump) # per_row specifies the number of registers to display per row def print_regs( self, per_row=6 ): for c in xrange( 0, self.num_regs, per_row ): str = "" for r in xrange( c, min( self.num_regs, c+per_row ) ): str += "%s:%s " % ( pad( "%d" % r, 2 ), pad_hex( self.regs[r] ) ) print str
class MemoryMappedRegisterFile(object): """Simulate the memory-mapped registers of a single Epiphany core. Note that memory objects can only read and write aligned words. """ def __init__(self, memory, coreid, logger): self.debug = Debug() self.logger = logger self.memory = memory self.coreid = coreid self.coreid_mask = coreid << 20 self.is_first_core = False self.memory.write(self.coreid_mask | 0xf0704, 4, coreid & 0xfff, from_core=coreid, quiet=True) self.num_regs = len(reg_memory_map) self.debug_nchars = 8 def __getitem__(self, index): address, bitsize, _ = reg_memory_map[index] mask = (1 << bitsize) - 1 value = self.memory.iread(address, 4, from_core=self.coreid) & mask if (self.debug.enabled('rf') and self.logger and index < 64 and self.is_first_core): self.logger.log(' :: RD.RF[%s] = %s' % (pad('%d' % index, 2), pad_hex(value, len=self.debug_nchars))) return value def __setitem__(self, index, value): if index == 0x65: # COREID register. Read only. Other Read/Write only return # registers need to be accessed by instructions. address, bitsize, _ = reg_memory_map[index] # Lower 20 bits of address. mask = (1 << bitsize) - 1 self.memory.write(self.coreid_mask | address, 4, value & mask, from_core=self.coreid, quiet=True) if (self.debug.enabled('rf') and self.logger and index < 64 and self.is_first_core): self.logger.log(' :: WR.RF[%s] = %s' % ((pad('%d' % index, 2), pad_hex(value, len=self.debug_nchars))))
def init_state(self, elf_file, filename, testbin, is_test=False): """Revelation has custom logging infrastructure that differs from the default provided by Pydgin. This matches e-sim, in that log messages are written to a file rather than to STDOUT. This prevents some specious differences in the two traces; e.g. the mode of an fstat object differing between Revelation and e-sim when the Revelation log is redirected from STDOUT to a file. This makes scripts/diff_trace.py much more useful. """ if is_test: self.debug = Debug() Debug.global_enabled = True if self.debug.enabled_flags and Debug.global_enabled: 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 f_row = (self.first_core >> 6) & 0x3f f_col = self.first_core & 0x3f elf = elf_reader(elf_file, is_64bit=False) coreids = [] for row in xrange(self.rows): for col in xrange(self.cols): coreid = get_coreid_from_coords(f_row + row, f_col + col) coreids.append(coreid) print ('Loading program %s on to core %s (%s, %s)' % (filename, hex(coreid), zfill(str(f_row + row), 2), zfill(str(f_col + col), 2))) self.states[coreid] = State(self.memory, self.debug, logger=self.logger, coreid=coreid) code_blocks = load_program(elf, self.memory, coreids, ext_base=self.ext_base, ext_size=self.ext_size) for section in code_blocks: self.memory.code_blocks.append(section) self.states[coreids[0]].set_first_core(True) if self.profile: timer = time.time() print 'ELF file loader took: %fs' % (timer - self.timer) self.timer = timer
def entry_point(argv): self.timer = time.time() if self.jit_enabled: set_param(self.jitdriver, 'trace_limit', self.default_trace_limit) try: fname, jit, flags = cli_parser(argv, self, Debug.global_enabled) except DoNotInterpretError: # CLI option such as --help or -h. return EXIT_SUCCESS except (SyntaxError, ValueError): return EXIT_SYNTAX_ERROR if jit: # pragma: no cover set_user_param(self.jitdriver, jit) self.debug = Debug(flags, 0) try: elf_file = open(fname, 'rb') 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 coreid in self.states: self.debug.set_state(self.states[coreid]) elf_file.close() try: exit_code, tick_counter = self.run() except KeyboardInterrupt: exit_code = EXIT_CTRL_C tick_counter = -1 if self.collect_times: self.end_time = time.time() if self.logger: self.logger.close() self._print_summary_statistics(tick_counter) return exit_code
class Sim( object ): def __init__( self, arch_name_human, arch_name="", jit_enabled=False ): # the human-friendly architecture name can contain large caps, special # characters etc. self.arch_name_human = arch_name_human if arch_name == "": self.arch_name = arch_name_human.lower() else: self.arch_name = arch_name self.jit_enabled = jit_enabled if jit_enabled: self.jitdriver = JitDriver( greens =['pc',], reds = ['max_insts', 'state', 'sim',], virtualizables =['state',], get_printable_location=self.get_location, ) # Set the default trace limit here. Different ISAs can override this # value if necessary self.default_trace_limit = 400000 self.max_insts = 0 #----------------------------------------------------------------------- # decode #----------------------------------------------------------------------- # This needs to be implemented in the child class def decode( self, bits ): raise NotImplementedError() #----------------------------------------------------------------------- # hooks provided for isa implementations #----------------------------------------------------------------------- def pre_execute( self ): pass def post_execute( self ): pass #----------------------------------------------------------------------- # init_state #----------------------------------------------------------------------- # This needs to be implemented in the child class def init_state( self, exe_file, exe_name, run_argv, testbin ): raise NotImplementedError() #----------------------------------------------------------------------- # help message #----------------------------------------------------------------------- # the help message to display on --help help_message = """ Pydgin %s Instruction Set Simulator usage: %s <args> <sim_exe> <sim_args> <sim_exe> the executable to be simulated <sim_args> arguments to be passed to the simulated executable <args> the following optional arguments are supported: --help,-h Show this message and exit --test Run in testing mode (for running asm tests) --env,-e <NAME>=<VALUE> Set an environment variable to be passed to the simulated program. Can use multiple --env flags to set multiple environment variables. --debug,-d <flags>[:<start_after>] Enable debug flags in a comma-separated form (e.g. "--debug syscalls,insts"). If provided, debugs starts after <start_after> cycles. The following flags are supported: insts cycle-by-cycle instructions rf register file accesses mem memory accesses regdump register dump syscalls syscall information bootstrap initial stack and register state --max-insts <i> Run until the maximum number of instructions --jit <flags> Set flags to tune the JIT (see rpython.rlib.jit.PARAMETER_DOCS) """ #----------------------------------------------------------------------- # get_location #----------------------------------------------------------------------- # for debug printing in PYPYLOG @staticmethod def get_location( pc ): # TODO: add the disassembly of the instruction here as well return "pc: %x" % pc #----------------------------------------------------------------------- # run #----------------------------------------------------------------------- def run( self ): self = hint( self, promote=True ) s = self.state max_insts = self.max_insts jitdriver = self.jitdriver while s.running: jitdriver.jit_merge_point( pc = s.fetch_pc(), max_insts = max_insts, state = s, sim = self, ) # constant-fold pc and mem pc = hint( s.fetch_pc(), promote=True ) old = pc mem = hint( s.mem, promote=True ) if s.debug.enabled( "insts" ): print pad( "%x" % pc, 8, " ", False ), # the print statement in memcheck conflicts with @elidable in iread. # So we use normal read if memcheck is enabled which includes the # memory checks if s.debug.enabled( "memcheck" ): inst_bits = mem.read( pc, 4 ) else: # we use trace elidable iread instead of just read inst_bits = mem.iread( pc, 4 ) try: inst, exec_fun = self.decode( inst_bits ) if s.debug.enabled( "insts" ): print "%s %s %s" % ( pad_hex( inst_bits ), pad( inst.str, 12 ), pad( "%d" % s.num_insts, 8 ), ), self.pre_execute() exec_fun( s, inst ) except NotImplementedInstError: # re-decode instruction to get the instruction name inst, _ = self.decode( inst_bits ) print "Instruction not implemented: %s (pc: 0x%s), aborting!" \ % ( inst.str, pad_hex( pc ) ) break except FatalError as error: print "Exception in execution (pc: 0x%s), aborting!" % pad_hex( pc ) print "Exception message: %s" % error.msg break s.num_insts += 1 # TODO: should this be done inside instruction definition? if s.stats_en: s.stat_num_insts += 1 self.post_execute() if s.debug.enabled( "insts" ): print if s.debug.enabled( "regdump" ): s.rf.print_regs( per_row=4 ) # check if we have reached the end of the maximum instructions and # exit if necessary if max_insts != 0 and s.num_insts >= max_insts: print "Reached the max_insts (%d), exiting." % max_insts break if s.fetch_pc() < old: jitdriver.can_enter_jit( pc = s.fetch_pc(), max_insts = max_insts, state = s, sim = self, ) print 'DONE! Status =', s.status print 'Instructions Executed =', s.num_insts #----------------------------------------------------------------------- # get_entry_point #----------------------------------------------------------------------- # generates and returns the entry_point function used to start the # simulator def get_entry_point( self ): def entry_point( argv ): # set the trace_limit parameter of the jitdriver if self.jit_enabled: set_param( self.jitdriver, "trace_limit", self.default_trace_limit ) filename_idx = 0 debug_flags = [] debug_starts_after = 0 testbin = False max_insts = 0 envp = [] # we're using a mini state machine to parse the args prev_token = "" # list of tokens that require an additional arg tokens_with_args = [ "-h", "--help", "-e", "--env", "-d", "--debug", "--max-insts", "--jit", ] # go through the args one by one and parse accordingly for i in xrange( 1, len( argv ) ): token = argv[i] if prev_token == "": if token == "--help" or token == "-h": print self.help_message % ( self.arch_name_human, argv[0] ) return 0 elif token == "--test": testbin = True elif token == "--debug" or token == "-d": prev_token = token # warn the user if debugs are not enabled for this translation if not Debug.global_enabled: print "WARNING: debugs are not enabled for this translation. " + \ "To allow debugs, translate with --debug option." elif token in tokens_with_args: prev_token = token elif token[:1] == "-": # unknown option print "Unknown argument %s" % token return 1 else: # this marks the start of the program name filename_idx = i break else: if prev_token == "--env" or prev_token == "-e": envp.append( token ) elif prev_token == "--debug" or prev_token == "-d": # if debug start after provided (using a colon), parse it debug_tokens = token.split( ":" ) if len( debug_tokens ) > 1: debug_starts_after = int( debug_tokens[1] ) debug_flags = debug_tokens[0].split( "," ) elif prev_token == "--max-insts": self.max_insts = int( token ) elif prev_token == "--jit": # pass the jit flags to rpython.rlib.jit set_user_param( self.jitdriver, token ) prev_token = "" if filename_idx == 0: print "You must supply a filename" return 1 # create a Debug object which contains the debug flags self.debug = Debug( debug_flags, debug_starts_after ) filename = argv[ filename_idx ] # args after program are args to the simulated program run_argv = argv[ filename_idx : ] # Open the executable for reading try: exe_file = open( filename, 'rb' ) except IOError: print "Could not open file %s" % filename return 1 # Call ISA-dependent init_state to load program, initialize memory # etc. self.init_state( exe_file, filename, run_argv, envp, testbin ) # pass the state to debug for cycle-triggered debugging self.debug.set_state( self.state ) # Close after loading exe_file.close() # Execute the program self.run() return 0 return entry_point #----------------------------------------------------------------------- # target #----------------------------------------------------------------------- # Enables RPython translation of our interpreter. def target( self, driver, args ): # if --debug flag is provided in translation, we enable debug printing if "--debug" in args: print "Enabling debugging" Debug.global_enabled = True else: print "Disabling debugging" # form a name exe_name = "pydgin-%s" % self.arch_name if driver.config.translation.jit: exe_name += "-jit" else: exe_name += "-nojit" if Debug.global_enabled: exe_name += "-debug" print "Translated binary name:", exe_name driver.exe_name = exe_name # NOTE: RPython has an assertion to check the type of entry_point to # be function (not a bound method). So we use get_entry_point which # generates a function type #return self.entry_point, None return self.get_entry_point(), None
def entry_point( argv ): # set the trace_limit parameter of the jitdriver if self.jit_enabled: set_param( self.jitdriver, "trace_limit", self.default_trace_limit ) filename_idx = 0 debug_flags = [] debug_starts_after = 0 testbin = False max_insts = 0 envp = [] # we're using a mini state machine to parse the args prev_token = "" # list of tokens that require an additional arg tokens_with_args = [ "-h", "--help", "-e", "--env", "-d", "--debug", "--max-insts", "--jit", ] # go through the args one by one and parse accordingly for i in xrange( 1, len( argv ) ): token = argv[i] if prev_token == "": if token == "--help" or token == "-h": print self.help_message % ( self.arch_name_human, argv[0] ) return 0 elif token == "--test": testbin = True elif token == "--debug" or token == "-d": prev_token = token # warn the user if debugs are not enabled for this translation if not Debug.global_enabled: print "WARNING: debugs are not enabled for this translation. " + \ "To allow debugs, translate with --debug option." elif token in tokens_with_args: prev_token = token elif token[:1] == "-": # unknown option print "Unknown argument %s" % token return 1 else: # this marks the start of the program name filename_idx = i break else: if prev_token == "--env" or prev_token == "-e": envp.append( token ) elif prev_token == "--debug" or prev_token == "-d": # if debug start after provided (using a colon), parse it debug_tokens = token.split( ":" ) if len( debug_tokens ) > 1: debug_starts_after = int( debug_tokens[1] ) debug_flags = debug_tokens[0].split( "," ) elif prev_token == "--max-insts": self.max_insts = int( token ) elif prev_token == "--jit": # pass the jit flags to rpython.rlib.jit set_user_param( self.jitdriver, token ) prev_token = "" if filename_idx == 0: print "You must supply a filename" return 1 # create a Debug object which contains the debug flags self.debug = Debug( debug_flags, debug_starts_after ) filename = argv[ filename_idx ] # args after program are args to the simulated program run_argv = argv[ filename_idx : ] # Open the executable for reading try: exe_file = open( filename, 'rb' ) except IOError: print "Could not open file %s" % filename return 1 # Call ISA-dependent init_state to load program, initialize memory # etc. self.init_state( exe_file, filename, run_argv, envp, testbin ) # pass the state to debug for cycle-triggered debugging self.debug.set_state( self.state ) # Close after loading exe_file.close() # Execute the program self.run() return 0
def __init__( self, num_regs=32, nbits=64 ): self.num_regs = num_regs self.regs = [ r_uint(0) ] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4
class Memory(object): """Sparse memory model adapted from Pydgin. """ def __init__(self, block_size=2**20, logger=None): self.block_size = block_size self.debug = Debug() self.logger = logger self.first_core = 0x808 self.addr_mask = block_size - 1 self.block_mask = 0xffffffff ^ self.addr_mask self.block_dict = {} self.code_blocks = [] def add_block(self, block_addr): self.block_dict[block_addr] = _BlockMemory(size=self.block_size) def get_block_mem(self, block_addr): if block_addr not in self.block_dict: self.add_block(block_addr) block_mem = self.block_dict[block_addr] return block_mem def iread(self, start_addr, num_bytes, from_core=0x808): if is_local_address(start_addr): start_addr |= (from_core << 20) end_addr = start_addr + num_bytes - 1 block_addr = self.block_mask & start_addr block_mem = self.get_block_mem(block_addr) # For mixed-width ISAs, the start_addr is not necessarily # word-aligned, and can cross block memory boundaries. If there is # such a case, we have two instruction reads and then form the word # for it. block_end_addr = self.block_mask & end_addr if block_addr == block_end_addr: value = block_mem.iread(start_addr & self.addr_mask, num_bytes) else: num_bytes1 = min(self.block_size - (start_addr & self.addr_mask), num_bytes) num_bytes2 = num_bytes - num_bytes1 block_mem1 = block_mem block_mem2 = self.get_block_mem(block_end_addr) value1 = block_mem1.iread(start_addr & self.addr_mask, num_bytes1) value2 = block_mem2.iread(0, num_bytes2) value = value1 | (value2 << (num_bytes1 * 8)) return value 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_mem = self.get_block_mem(block_addr) masked_addr = 0xfffff & start_addr value = block_mem.read(start_addr & self.addr_mask, num_bytes) if (self.debug.enabled('mem') and self.logger and not is_register_address(masked_addr) and (start_addr >> 20) == self.first_core): if start_addr >> 20 == from_core: start_addr = start_addr & 0xfffff self.logger.log(' :: RD.MEM[%s] = %s' % \ (pad_hex(start_addr), pad_hex(value))) return value def write(self, start_addr, num_bytes, value, from_core=0x808, quiet=False): """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. """ if is_local_address(start_addr): start_addr |= (from_core << 20) coreid_mask = start_addr & 0xfff00000 for start, end in self.code_blocks: if start_addr >= start and (start_addr + num_bytes) <= end: print 'WARNING: self-modifying code @', pad_hex(start_addr) 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) elif start_addr & 0xfffff == 0xf0438 and value == 0: # CTIMER0 expired. ilat = self.read(coreid_mask | 0xf0428, 4) & 0x3ff ilat |= 0x8 self.write(coreid_mask | 0xf0428, 4, ilat) elif start_addr & 0xfffff == 0xf043c and value == 0: # CTIMER1 expired. ilat = self.read(coreid_mask | 0xf0428, 4) & 0x3ff ilat |= 0x10 self.write(coreid_mask | 0xf0428, 4, ilat) block_addr = self.block_mask & start_addr block_mem = self.get_block_mem(block_addr) block_mem.write(start_addr & self.addr_mask, num_bytes, value) masked_addr = 0xfffff & start_addr if (self.debug.enabled('mem') and self.logger and not quiet and not is_register_address(masked_addr) and (start_addr >> 20) == self.first_core): if start_addr >> 20 == from_core: start_addr = start_addr & 0xfffff self.logger.log(' :: WR.MEM[%s] = %s' % \ (pad_hex(start_addr), pad_hex(value)))
class Sim(object): def __init__(self, arch_name_human, arch_name="", jit_enabled=False): # the human-friendly architecture name can contain large caps, special # characters etc. self.arch_name_human = arch_name_human if arch_name == "": self.arch_name = arch_name_human.lower() else: self.arch_name = arch_name self.jit_enabled = jit_enabled if jit_enabled: self.jitdriver = JitDriver( greens=[ 'pc', ], reds=[ 'max_insts', 'state', 'sim', ], virtualizables=[ 'state', ], get_printable_location=self.get_location, ) # Set the default trace limit here. Different ISAs can override this # value if necessary self.default_trace_limit = 400000 self.max_insts = 0 #----------------------------------------------------------------------- # decode #----------------------------------------------------------------------- # This needs to be implemented in the child class def decode(self, bits): raise NotImplementedError() #----------------------------------------------------------------------- # hooks provided for isa implementations #----------------------------------------------------------------------- def pre_execute(self): pass def post_execute(self): pass #----------------------------------------------------------------------- # init_state #----------------------------------------------------------------------- # This needs to be implemented in the child class def init_state(self, exe_file, exe_name, run_argv, testbin): raise NotImplementedError() #----------------------------------------------------------------------- # help message #----------------------------------------------------------------------- # the help message to display on --help help_message = """ Pydgin %s Instruction Set Simulator usage: %s <args> <sim_exe> <sim_args> <sim_exe> the executable to be simulated <sim_args> arguments to be passed to the simulated executable <args> the following optional arguments are supported: --help,-h Show this message and exit --test Run in testing mode (for running asm tests) --env,-e <NAME>=<VALUE> Set an environment variable to be passed to the simulated program. Can use multiple --env flags to set multiple environment variables. --debug,-d <flags>[:<start_after>] Enable debug flags in a comma-separated form (e.g. "--debug syscalls,insts"). If provided, debugs starts after <start_after> cycles. The following flags are supported: insts cycle-by-cycle instructions rf register file accesses mem memory accesses regdump register dump syscalls syscall information bootstrap initial stack and register state --max-insts <i> Run until the maximum number of instructions --jit <flags> Set flags to tune the JIT (see rpython.rlib.jit.PARAMETER_DOCS) """ #----------------------------------------------------------------------- # get_location #----------------------------------------------------------------------- # for debug printing in PYPYLOG @staticmethod def get_location(pc): # TODO: add the disassembly of the instruction here as well return "pc: %x" % pc #----------------------------------------------------------------------- # run #----------------------------------------------------------------------- def run(self): self = hint(self, promote=True) s = self.state max_insts = self.max_insts jitdriver = self.jitdriver while s.running: jitdriver.jit_merge_point( pc=s.fetch_pc(), max_insts=max_insts, state=s, sim=self, ) # constant-fold pc and mem pc = hint(s.fetch_pc(), promote=True) old = pc mem = hint(s.mem, promote=True) if s.debug.enabled("insts"): print pad("%x" % pc, 8, " ", False), # the print statement in memcheck conflicts with @elidable in iread. # So we use normal read if memcheck is enabled which includes the # memory checks if s.debug.enabled("memcheck"): inst_bits = mem.read(pc, 4) else: # we use trace elidable iread instead of just read inst_bits = mem.iread(pc, 4) try: inst, exec_fun = self.decode(inst_bits) if s.debug.enabled("insts"): print "%s %s %s" % ( pad_hex(inst_bits), pad(inst.str, 12), pad("%d" % s.num_insts, 8), ), self.pre_execute() exec_fun(s, inst) except NotImplementedInstError: # re-decode instruction to get the instruction name inst, _ = self.decode(inst_bits) print "Instruction not implemented: %s (pc: 0x%s), aborting!" \ % ( inst.str, pad_hex( pc ) ) break except FatalError as error: print "Exception in execution (pc: 0x%s), aborting!" % pad_hex( pc) print "Exception message: %s" % error.msg break s.num_insts += 1 # TODO: should this be done inside instruction definition? if s.stats_en: s.stat_num_insts += 1 self.post_execute() if s.debug.enabled("insts"): print if s.debug.enabled("regdump"): s.rf.print_regs(per_row=4) # check if we have reached the end of the maximum instructions and # exit if necessary if max_insts != 0 and s.num_insts >= max_insts: print "Reached the max_insts (%d), exiting." % max_insts break if s.fetch_pc() < old: jitdriver.can_enter_jit( pc=s.fetch_pc(), max_insts=max_insts, state=s, sim=self, ) print 'DONE! Status =', s.status print 'Instructions Executed =', s.num_insts #----------------------------------------------------------------------- # get_entry_point #----------------------------------------------------------------------- # generates and returns the entry_point function used to start the # simulator def get_entry_point(self): def entry_point(argv): # set the trace_limit parameter of the jitdriver if self.jit_enabled: set_param(self.jitdriver, "trace_limit", self.default_trace_limit) filename_idx = 0 debug_flags = [] debug_starts_after = 0 testbin = False max_insts = 0 envp = [] # we're using a mini state machine to parse the args prev_token = "" # list of tokens that require an additional arg tokens_with_args = [ "-h", "--help", "-e", "--env", "-d", "--debug", "--max-insts", "--jit", ] # go through the args one by one and parse accordingly for i in xrange(1, len(argv)): token = argv[i] if prev_token == "": if token == "--help" or token == "-h": print self.help_message % (self.arch_name_human, argv[0]) return 0 elif token == "--test": testbin = True elif token == "--debug" or token == "-d": prev_token = token # warn the user if debugs are not enabled for this translation if not Debug.global_enabled: print "WARNING: debugs are not enabled for this translation. " + \ "To allow debugs, translate with --debug option." elif token in tokens_with_args: prev_token = token elif token[:1] == "-": # unknown option print "Unknown argument %s" % token return 1 else: # this marks the start of the program name filename_idx = i break else: if prev_token == "--env" or prev_token == "-e": envp.append(token) elif prev_token == "--debug" or prev_token == "-d": # if debug start after provided (using a colon), parse it debug_tokens = token.split(":") if len(debug_tokens) > 1: debug_starts_after = int(debug_tokens[1]) debug_flags = debug_tokens[0].split(",") elif prev_token == "--max-insts": self.max_insts = int(token) elif prev_token == "--jit": # pass the jit flags to rpython.rlib.jit set_user_param(self.jitdriver, token) prev_token = "" if filename_idx == 0: print "You must supply a filename" return 1 # create a Debug object which contains the debug flags self.debug = Debug(debug_flags, debug_starts_after) filename = argv[filename_idx] # args after program are args to the simulated program run_argv = argv[filename_idx:] # Open the executable for reading try: exe_file = open(filename, 'rb') except IOError: print "Could not open file %s" % filename return 1 # Call ISA-dependent init_state to load program, initialize memory # etc. self.init_state(exe_file, filename, run_argv, envp, testbin) # pass the state to debug for cycle-triggered debugging self.debug.set_state(self.state) # Close after loading exe_file.close() # Execute the program self.run() return 0 return entry_point #----------------------------------------------------------------------- # target #----------------------------------------------------------------------- # Enables RPython translation of our interpreter. def target(self, driver, args): # if --debug flag is provided in translation, we enable debug printing if "--debug" in args: print "Enabling debugging" Debug.global_enabled = True else: print "Disabling debugging" # form a name exe_name = "pydgin-%s" % self.arch_name if driver.config.translation.jit: exe_name += "-jit" else: exe_name += "-nojit" if Debug.global_enabled: exe_name += "-debug" print "Translated binary name:", exe_name driver.exe_name = exe_name # NOTE: RPython has an assertion to check the type of entry_point to # be function (not a bound method). So we use get_entry_point which # generates a function type #return self.entry_point, None return self.get_entry_point(), None
def entry_point(argv): # set the trace_limit parameter of the jitdriver if self.jit_enabled: set_param(self.jitdriver, "trace_limit", self.default_trace_limit) filename_idx = 0 debug_flags = [] debug_starts_after = 0 testbin = False max_insts = 0 envp = [] # we're using a mini state machine to parse the args prev_token = "" # list of tokens that require an additional arg tokens_with_args = [ "-h", "--help", "-e", "--env", "-d", "--debug", "--max-insts", "--jit", ] # go through the args one by one and parse accordingly for i in xrange(1, len(argv)): token = argv[i] if prev_token == "": if token == "--help" or token == "-h": print self.help_message % (self.arch_name_human, argv[0]) return 0 elif token == "--test": testbin = True elif token == "--debug" or token == "-d": prev_token = token # warn the user if debugs are not enabled for this translation if not Debug.global_enabled: print "WARNING: debugs are not enabled for this translation. " + \ "To allow debugs, translate with --debug option." elif token in tokens_with_args: prev_token = token elif token[:1] == "-": # unknown option print "Unknown argument %s" % token return 1 else: # this marks the start of the program name filename_idx = i break else: if prev_token == "--env" or prev_token == "-e": envp.append(token) elif prev_token == "--debug" or prev_token == "-d": # if debug start after provided (using a colon), parse it debug_tokens = token.split(":") if len(debug_tokens) > 1: debug_starts_after = int(debug_tokens[1]) debug_flags = debug_tokens[0].split(",") elif prev_token == "--max-insts": self.max_insts = int(token) elif prev_token == "--jit": # pass the jit flags to rpython.rlib.jit set_user_param(self.jitdriver, token) prev_token = "" if filename_idx == 0: print "You must supply a filename" return 1 # create a Debug object which contains the debug flags self.debug = Debug(debug_flags, debug_starts_after) filename = argv[filename_idx] # args after program are args to the simulated program run_argv = argv[filename_idx:] # Open the executable for reading try: exe_file = open(filename, 'rb') except IOError: print "Could not open file %s" % filename return 1 # Call ISA-dependent init_state to load program, initialize memory # etc. self.init_state(exe_file, filename, run_argv, envp, testbin) # pass the state to debug for cycle-triggered debugging self.debug.set_state(self.state) # Close after loading exe_file.close() # Execute the program self.run() return 0
class Revelation(Sim): def __init__(self): # DO NOT call Sim.__init__() -- Revelation JIT differs from Pydgin. self.arch_name_human = 'Revelation' self.arch_name = self.arch_name_human.lower() self.jit_enabled = True if self.jit_enabled: self.jitdriver = JitDriver( greens = ['pc', ], reds = ['core', 'tick_counter', 'coreids', 'sim', 'state',], get_printable_location=get_printable_location) self.default_trace_limit = 400000 self.max_insts = 0 # --max-insts. self.logger = None # --debug output: self.logger.log(). self.rows = 1 # --rows, -r. self.cols = 1 # --cols, -c. self.states = {} # coreid -> revelation.machine.State. self.first_core = 0x808 # --first-core, -f. self.ext_base = 0x8e000000 # Base address of 'external' memory. self.ext_size = 32 # Size of 'external' memory in MB. self.switch_interval = 1 # --switch. TODO: currently ignored. self.user_environment = False # Superuser mode. TODO: currently ignored. self.collect_times = False # --time, -t option. self.start_time = .0 # --time, -t option. self.end_time = .0 # --time, -t option. self.profile = False # Undocumented --profile option. self.timer = .0 # Undocumented --profile option. 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: fname, jit, flags = cli_parser(argv, self, Debug.global_enabled) except DoNotInterpretError: # CLI option such as --help or -h. return EXIT_SUCCESS except (SyntaxError, ValueError): return EXIT_SYNTAX_ERROR if jit: # pragma: no cover set_user_param(self.jitdriver, jit) self.debug = Debug(flags, 0) try: elf_file = open(fname, 'rb') 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 coreid in self.states: self.debug.set_state(self.states[coreid]) elf_file.close() try: exit_code, tick_counter = self.run() except KeyboardInterrupt: exit_code = EXIT_CTRL_C tick_counter = -1 if self.collect_times: self.end_time = time.time() if self.logger: self.logger.close() self._print_summary_statistics(tick_counter) return exit_code return entry_point def run(self): """Fetch, decode, execute, service interrupts loop. Override Sim.run to provide multicore and close the logger on exit. """ coreids = self.states.keys() core = coreids[0] # Key to self.states dictionary. state = self.states[core] # revelation.machine.State object. pc = state.fetch_pc() # Program counter. tick_counter = 0 # Number of instructions executed by all cores. old_pc = 0 self.start_time = time.time() while True: self.jitdriver.jit_merge_point(pc=pc, core=core, tick_counter=tick_counter, coreids=coreids, sim=self, state=state,) # Fetch next instruction. opcode = state.mem.iread(pc, 4, from_core=state.coreid) try: # Decode instruction. mnemonic, function = decode(opcode) instruction = Instruction(opcode, mnemonic) # --debug if (state.is_first_core and self.logger and state.debug.enabled('trace')): state.logger.log('%s %s %s %s' % (pad('%x' % pc, 8, ' ', False), pad_hex(opcode), pad(instruction.name, 12), pad('%d' % state.num_insts, 8))) # Check whether or not we are in a hardware loop, and set # registers after the next instruction, as appropriate. See # Section 7.9 of the Architecture Reference Rev. 14.03.11. if (state.GID and state.pc == state.rf[reg_map['LE']]): state.rf[reg_map['LC']] -= 1 state.is_in_hardware_loop = True # Execute next instruction. function(state, instruction) # --debug if (state.is_first_core and state.logger and state.debug.enabled('trace')): state.logger.log('\n') # Check hardware loop registers. if state.is_in_hardware_loop and state.rf[reg_map['LC']] > 0: state.pc = state.rf[reg_map['LS']] state.is_in_hardware_loop = False # Service interrupts. if (state.rf[reg_map['ILAT']] > 0 and not (state.GID or state.rf[reg_map['DEBUGSTATUS']] == 1)): interrupt_level = state.get_latched_interrupt() if interrupt_level > -1: # Interrupt to process. # If a pending interrupt is of a higher priority than # the latched interrupt, carry on with the pending # interrupt. pending_interrupt = state.get_pending_interrupt() if (pending_interrupt == -1 or (pending_interrupt > -1 and interrupt_level <= pending_interrupt)): state.rf[reg_map['IRET']] = state.pc state.rf[reg_map['ILAT']] &= ~(1 << interrupt_level) state.rf[reg_map['IPEND']] |= 1 << interrupt_level state.GID = True # Set global interrupt disabled bit. state.pc = IVT[interrupt_level] state.ACTIVE = 1 # Wake up IDLE cores. except (FatalError, NotImplementedInstError) as error: mnemonic, _ = decode(opcode) print ('Exception in execution of %s (pc: 0x%s), aborting!' % (mnemonic, pad_hex(pc))) print 'Exception message: %s' % error.msg return EXIT_GENERAL_ERROR, tick_counter # pragma: no cover # Update instruction counters. tick_counter += 1 state.num_insts += 1 # Halt if we have reached the maximum instruction count. if self.max_insts != 0 and state.num_insts >= self.max_insts: print 'Reached the max_insts (%d), exiting.' % self.max_insts break # Check whether state has halted. if not state.running: if len(coreids) == 1: # Last running core has halted. break old_core = core core = coreids[(coreids.index(core) + 1) % len(coreids)] state = self.states[core] coreids.remove(old_core) # Switch cores after every instruction. TODO: Honour switch interval. elif len(coreids) > 1 and tick_counter % self.switch_interval == 0: while True: core = coreids[(coreids.index(core) + 1) % len(coreids)] if self.states[core].ACTIVE == 1: break # Idle cores can be made active by interrupts. elif (self.states[core].ACTIVE == 0 and self.states[core].rf[reg_map['ILAT']] > 0): interrupt_level = self.states[core].get_latched_interrupt() self.states[core].rf[reg_map['IRET']] = self.states[core].pc self.states[core].rf[reg_map['ILAT']] &= ~(1 << interrupt_level) self.states[core].rf[reg_map['IPEND']] |= 1 << interrupt_level self.states[core].GID = True # Set global interrupt disabled bit. self.states[core].pc = IVT[interrupt_level] self.states[core].ACTIVE = 1 # Wake up IDLE cores. break state = self.states[core] # Move program counter to next instruction. old_pc = pc pc = state.fetch_pc() if pc < old_pc: self.jitdriver.can_enter_jit(pc=pc, core=core, tick_counter=tick_counter, coreids=coreids, sim=self, state=state,) return EXIT_SUCCESS, tick_counter def _print_summary_statistics(self, ticks): """Print timing information. If simulation was interrupted by the user pressing Ctrl+c, 'ticks' will be -1. """ if ticks > -1: print 'Total ticks simulated = %s.' % format_thousands(ticks) for coreid in self.states: row, col = get_coords_from_coreid(coreid) print ('Core %s (%s, %s) STATUS: 0x%s, Instructions executed: %s' % (hex(coreid), zfill(str(row), 2), zfill(str(col), 2), pad_hex(self.states[coreid].rf[reg_map['STATUS']]), format_thousands(self.states[coreid].num_insts))) if self.collect_times: execution_time = self.end_time - self.start_time print 'Total execution time: %fs.' % (execution_time) if ticks > -1: speed = format_thousands(int(ticks / execution_time)) print 'Simulator speed: %s instructions / second.' % speed def init_state(self, elf_file, filename, testbin, is_test=False): """Revelation has custom logging infrastructure that differs from the default provided by Pydgin. This matches e-sim, in that log messages are written to a file rather than to STDOUT. This prevents some specious differences in the two traces; e.g. the mode of an fstat object differing between Revelation and e-sim when the Revelation log is redirected from STDOUT to a file. This makes scripts/diff_trace.py much more useful. """ if is_test: self.debug = Debug() Debug.global_enabled = True if self.debug.enabled_flags and Debug.global_enabled: 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 f_row = (self.first_core >> 6) & 0x3f f_col = self.first_core & 0x3f elf = elf_reader(elf_file, is_64bit=False) coreids = [] for row in xrange(self.rows): for col in xrange(self.cols): coreid = get_coreid_from_coords(f_row + row, f_col + col) coreids.append(coreid) print ('Loading program %s on to core %s (%s, %s)' % (filename, hex(coreid), zfill(str(f_row + row), 2), zfill(str(f_col + col), 2))) self.states[coreid] = State(self.memory, self.debug, logger=self.logger, coreid=coreid) code_blocks = load_program(elf, self.memory, coreids, ext_base=self.ext_base, ext_size=self.ext_size) for section in code_blocks: self.memory.code_blocks.append(section) self.states[coreids[0]].set_first_core(True) if self.profile: timer = time.time() print 'ELF file loader took: %fs' % (timer - self.timer) self.timer = timer
def __init__(self, num_regs=32, nbits=64): self.num_regs = num_regs self.regs = [r_uint(0)] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4
def entry_point(argv): filename_idx = 0 debug_flags = [] testbin = False max_insts = 0 # we're using a mini state machine to parse the args, and these are two # states we have ARGS = 0 DEBUG_FLAGS = 1 MAX_INSTS = 2 token_type = ARGS # go through the args one by one and parse accordingly for i in xrange(1, len(argv)): token = argv[i] if token_type == ARGS: if token == "--help": print help_message % argv[0] return 0 elif token == "--test": testbin = True elif token == "--debug": token_type = DEBUG_FLAGS # warn the user if debugs are not enabled for this translation if not Debug.global_enabled: print "WARNING: debugs are not enabled for this translation. " + \ "To allow debugs, translate with --debug option." elif token == "--max-insts": token_type = MAX_INSTS elif token[:2] == "--": # unknown option print "Unknown argument %s" % token return 1 else: # this marks the start of the program name filename_idx = i break elif token_type == DEBUG_FLAGS: debug_flags = token.split(",") token_type = ARGS elif token_type == MAX_INSTS: max_insts = int(token) token_type = ARGS if filename_idx == 0: print "You must supply a filename" return 1 filename = argv[filename_idx] # args after program are args to the simulated program run_argv = argv[filename_idx:] # Load the program into a memory object mem = Memory(size=memory_size, byte_storage=False) entrypoint, breakpoint = load_program( open(filename, 'rb'), mem, # TODO: GEM5 uses this alignment, remove? alignment=1 << 12) # create a Debug object which contains the debug flags debug = Debug() debug.set_flags(debug_flags) # Insert bootstrapping code into memory and initialize processor state state = syscall_init(mem, entrypoint, breakpoint, run_argv, debug) # Execute the program run(state, max_insts) return 0