def __init__(self, hostname, port=17893): self.conn = SCPConnection(hostname, port) self.app_calls = AppCalls(self) self.memory_calls = MemoryCalls(self) self.packet_calls = PacketCalls(self) self._x = 0 self._y = 0 self.utility = None
class Transceiver(object): """ A Transceiver is instantiated by a :py:func:`pacman103.pacman.control.Controller` object in order to communicate with a SpiNNaker board. The Transceiver class is simply a wrapper around the :py:mod:`pacman103.scp` module, which does the actual interfaces over Ethernet. An instance of the class maintains a SCP connection to a machine, through which all interfaces takes place. :param string hostname: hostname of the SpiNNaker machine on which the simulation is to be run. """ def __init__(self, hostname, port=17893): self.conn = SCPConnection(hostname, port) self.app_calls = AppCalls(self) self.memory_calls = MemoryCalls(self) self.packet_calls = PacketCalls(self) self._x = 0 self._y = 0 self.utility = None def read_memory(self, x, y, p, a, l, dtype): """ Read memory from the SpiNNaker machine. :param int x: x-chip-coordinate. :param int y: y-chip-coordinate. :param int p: processor ID. :param int a: target load address. :param int l: length of data to read (bytes). :param numpy.dtype dtype: datatype of the returned numpy array (e.g. numpy.int32) :returns: numpy.ndarray containing the requested memory. """ self.select(x, y, p) memory = self.memory_calls.read_mem(a, scamp.TYPE_WORD, l) memory = numpy.fromstring(memory, dtype=dtype) return memory def organise_targets(self, targets): ''' method that takes the targets and converts them into a list of chip scoped targets where each entry contains the chip ''' organised_targets = dict() for target in targets: filename = "{},{}".format(target.targets[0]['x'], target.targets[0]['y']) proc = target.targets[0]['p'] if organised_targets.has_key(target.filename): chip_collection = organised_targets.get(target.filename) if filename in chip_collection: chip_collection[filename].append(proc) else: chip_collection[filename] = [proc] else: #add with the chip definition chip_collection = dict() chip_collection[filename] = [proc] organised_targets[target.filename] = chip_collection return organised_targets def checkfile(self, test_file): real_file = test_file if not os.path.isfile(real_file): directory = os.path.dirname(boot_files.__file__) real_file = os.path.join(directory, real_file) if not os.path.isfile(real_file): print "File %s not found" % test_file sys.exit(3) return real_file def select (self, *args): """ Select the target node and processor. :param args: variadic argument (usage below) :raises: ValueError This function has the following calling conventions: ``conn.select ('root')`` Short-hand to select node (0, 0, 0) ``conn.select (N)`` Selects processor N on the currently selected node ``conn.select (X, Y)`` Selects processor 0 on node (``X``, ``Y``) ``conn.select (X, Y, N)`` Selects processor ``N`` on node (``X``, ``Y``) """ # extract the arguments if len (args) == 1 and type (args[0]) == str and args[0] == "root": (x, y, cpu) = (0, 0, 0) elif len (args) == 1 and type (args[0]) == int: (x, y, cpu) = (self._x, self._y, args[0]) elif len (args) == 2: (x, y, cpu) = (args[0], args[1], 0) elif len (args) == 3: (x, y, cpu) = args else: raise ValueError ("invalid arguments given for SCPConnection." "select call.") # make sure that the variables are all ints if type (x) != int or type (y) != int or type (cpu) != int: raise ValueError ("invalid argument types given expecting ints or " "a single string 'root'.") # save the variables self.app_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.memory_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.packet_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.conn.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self._x = x & 0xFF self._y = y & 0xFF
class Transceiver(object): """ A Transceiver is instantiated by a :py:func:`pacman103.pacman.control.Controller` object in order to communicate with a SpiNNaker board. The Transceiver class is simply a wrapper around the :py:mod:`pacman103.scp` module, which does the actual interfaces over Ethernet. An instance of the class maintains a SCP connection to a machine, through which all interfaces takes place. :param string hostname: hostname of the SpiNNaker machine on which the simulation is to be run. """ def __init__(self, hostname, port=17893): self.conn = SCPConnection(hostname, port) self.app_calls = AppCalls(self) self.memory_calls = MemoryCalls(self) self.packet_calls = PacketCalls(self) self._x = 0 self._y = 0 self.utility = None def read_memory(self, x, y, p, a, l, dtype): """ Read memory from the SpiNNaker machine. :param int x: x-chip-coordinate. :param int y: y-chip-coordinate. :param int p: processor ID. :param int a: target load address. :param int l: length of data to read (bytes). :param numpy.dtype dtype: datatype of the returned numpy array (e.g. numpy.int32) :returns: numpy.ndarray containing the requested memory. """ self.select(x, y, p) memory = self.memory_calls.read_mem(a, scamp.TYPE_WORD, l) memory = numpy.fromstring(memory, dtype=dtype) return memory def load_targets(self, dao): """ Loads LoadTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.load_targets_raw`. """ if conf.config.get("Reports", "write_reload_steps"): self.utility = SpinnmanUtilities(dao=dao) #check to see if the machine is contactable & loadable self.check_target_machine(dao.machine.hostname, dao.machine.y_dim, dao.machine.y_dim) # now get the data to load and then do it targets = dao.get_load_targets() self.load_targets_raw(targets) def load_targets_raw(self, targets): """ Uses the SCP connection to load LoadTargets to the machine. :param list targets: list of :py:class:`pacman103.lib.lib_map.LoadTarget` instances. """ for target in targets: if conf.config.get("Reports", "write_reload_steps"): self.utility.write_selects(target.x, target.y, target.p) self.utility.write_mem_from_file(target.address, scamp.TYPE_WORD, target.filename) self.select(target.x, target.y, target.p) self.memory_calls.write_mem_from_file(target.address, scamp.TYPE_WORD, target.filename) def load_targets_load(self, file_name): self.utility = SpinnmanUtilities(input_file=file_name) commands = self.utility.get_mem_writes_from_file() for index in range(0, len(commands), 2): self.select(int(commands[index]['x']), int(commands[index]['y']), int(commands[index]['p'])) self.memory_calls.write_mem_from_file(int(commands[index+1]['address']), int(commands[index+1]['type_word']), commands[index+1]['filename']) def load_write_mem(self, dao): """ Loads WriteMemTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.load_write_mem_raw`. """ targets = dao.get_mem_write_targets() self.load_write_mem_raw(targets) def load_write_mem_raw(self, targets): """ Uses the SCP connection to load WriteMemTargets to the machine. :param list targets: list of :py:class:`pacman103.lib.lib_map.WriteMemTarget` instances. """ for target in targets: if conf.config.get("Reports", "write_reload_steps"): self.utility.write_selects(target.x, target.y, target.p) self.utility.write_mem(target.address, scamp.TYPE_WORD, struct.pack("I", target.data)) self.select(target.x, target.y, target.p) self.memory_calls.write_mem(target.address, scamp.TYPE_WORD, struct.pack("I", target.data)) def load_write_mem_load(self, file_name): self.utility = SpinnmanUtilities(input_file=file_name) commands = self.utility.get_mem_writes() for index in range(0, len(commands), 2): self.select(int(commands[index]['x']), int(commands[index]['y']), int(commands[index]['p'])) self.memory_calls.write_mem(int(commands[index+1]['address']), int(commands[index+1]['type_word']), commands[index+1]['structure']) def reset_board(self): """ TODO Implement to reset Spinn4 boards over SCP. """ """ #TODO check board is version 4 or greater scp.reset(kwargs['bmp']) time.sleep(5) scp.boot(hostname, '/home/tomxsharp/Thesis/spinnaker/tools/scamp-200.boot')#COMMENT ME time.sleep(2) self.txrx.conn.init_p2p_tables(self.dao.machine.x_dim, self.dao.machine.y_dim) self.txrx.conn.set_iptag(0, 'localhost', 17892) """ pass def run(self, dao, app_id): """ Loads ExecutableTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.run_raw`. """ machine = dao.get_machine() targets = dao.get_executable_targets() iptags = dao.get_iptags() run_time = dao.run_time #TODO accessor method self.run_raw(machine, targets, run_time, app_id, iptags, dao) def run_raw(self, machine, targets, run_time, app_id, iptags, dao): """ Uses the SCP connection to trigger simulation. :param `pacman103.lib.lib_machine.Machine` machine: machine to run the simulation on. :param list targets: list of :py:class:`pacman103.lib.lib_map.ExecutableTarget` instances. :param int run_time: run time of the simulation (milliseconds) """ # Select monitor pacman on ethernet-adjacent chip self.select(0, 0) # Set IP tags for iptag in iptags: retries = 0 while retries < 3: try: logger.info("Setting up ip tag {} to {}:{}".format( iptag.tag, iptag.hostname, iptag.port)) self.conn.set_iptag(iptag.tag, iptag.hostname, iptag.port) retries = 3 except Exception as e: if retries == 3: exceptions.SpinnManException("IPTags failed to be set " "due to {}". format(e.message)) retries += 1 time.sleep(0.1) total_processors = 0 targets = self.organise_targets(targets) for key in targets.keys(): chips = targets[key] core_mask = 0 for chip in chips: processors = chips[chip] core_part_of_region = "" first = True for processor in processors: core_mask += processor if first: core_part_of_region += "{}".format(processor) first = False else: core_part_of_region += ",{}".format(processor) total_processors += 1 (x, y) = chip.split(",") region = Utility.calculate_region_id(int(x), int(y)) if conf.config.get("Reports", "write_reload_steps"): self.utility.write_app_load_command(key, region, core_part_of_region, app_id) self.app_calls.app_load(key, region, core_part_of_region, app_id) processors_ready = 0 logger.debug("checking that the processors currently" " flood filled are ready for future flood fills") while processors_ready < total_processors: processors_ready = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.debug("{} processors out of {} " "processors are ready".format(processors_ready, total_processors)) logger.info("Waiting for application to finish loading") if conf.config.get("Reports", "write_reload_steps"): self.utility.write_extra_data(run_time, total_processors) self.utility.close() self.check_synco_and_run(total_processors, app_id, run_time, dao) def check_synco_and_run(self, total_processors, app_id, run_time, dao): ''' checks that all processors have reached sync0 and runs the application for the given runtime ''' processors_ready = 0 while processors_ready < total_processors: processors_ready = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.info("Starting application") self.app_calls.app_signal(app_id, scamp.SIGNAL_SYNC0) logger.info("Checking that the application has started") processors_running = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RUN) if processors_running < total_processors: raise exceptions.SpinnManException("Only {} of {} processors " "started".format(processors_running, total_processors)) if run_time is not None: logger.info("Application started - waiting for it to stop") dao.write_running_msg() time.sleep(run_time / 1000.0) processors_not_finished = processors_ready while processors_not_finished != 0: processors_not_finished = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RUN) processors_rte = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RTE) if processors_rte > 0: raise exceptions.SpinnManException("{} cores have gone into " "a run time error state.". format(processors_rte)) processors_exited = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_EXIT) if processors_exited < total_processors: raise exceptions.\ SpinnManException("{} of the processors " "failed to exit successfully" .format(total_processors - processors_exited)) logger.info("Application has run to completion") # update the dao holder so that stuff can be read from the SDRAM dao.has_ran = True else: logger.info("Application is set to run forever - PACMAN is exiting") retry_counts = self.conn.get_retries() logger.debug("Total retries on this run due to RC_TIMEOUT: {}, " + "RC_P2P_TIMEOUT: {}, RC_LEN: {}".format(retry_counts[0], retry_counts[1], retry_counts[2])) def run_load(self, utility_file): utility = SpinnmanUtilities(utility_file) run_time = utility.get_run_time() total_processors_of_app = utility.get_total_processors() app_loads = utility.get_app_loads() total_processors = 0 app_id = None for app_load in app_loads: self.app_calls.app_load(app_load['key'], app_load['region'], app_load['core_part_of_region'], app_load['app_id']) app_id = app_load['app_id'] processors_ready = 0 logger.debug("checking that the processors currently" " flood filled are ready for future flood fills") while processors_ready < total_processors: processors_ready = \ self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.debug("{} processors out of {} processors are " "ready".format(processors_ready, total_processors)) self.check_synco_and_run(total_processors_of_app, app_id, run_time) def organise_targets(self, targets): ''' method that takes the targets and converts them into a list of chip scoped targets where each entry contains the chip ''' organised_targets = dict() for target in targets: key = "{},{}".format(target.targets[0]['x'], target.targets[0]['y']) proc = target.targets[0]['p'] if organised_targets.has_key(target.filename): chip_collection = organised_targets.get(target.filename) if key in chip_collection: chip_collection[key].append(proc) else: chip_collection[key] = [proc] else: #add with the chip definition chip_collection = dict() chip_collection[key] = [proc] organised_targets[target.filename] = chip_collection return organised_targets def check_target_machine(self, hostname, x, y): """ This routine takes the requested dimension hints and the hostname of the machine, then checks that it is pingable on the network, has been booted and has physical dimensions that at least match the requested dimensions. :param int hostname: address of the physical machine that we'll be using :param int x: dimensions requested of the machine (may be smaller than the physical machine, but not bigger!) :param int y: dimensions requested of the machine (may be smaller than the physical machine, but not bigger!) :returns: xdims, and ydims which contain the physical dimensions detected in the machine :raises: ExploreException """ # check if machine is active and on the network pingtimeout=5 # number of times to retry operations to wake up the board / ARP entries on the network while (pingtimeout): process = None if (platform.platform().lower().startswith("windows")): process = subprocess.Popen("ping -n 1 -w 1 " + hostname, shell=True, stdout=subprocess.PIPE) else: process = subprocess.Popen("ping -c 1 -W 1 " + hostname, shell=True, stdout=subprocess.PIPE) process.wait() if (process.returncode == 0): break # ping worked else: print "." pingtimeout-=1 if (pingtimeout==0): raise exceptions.ExploreException("EXPLORE ERROR: Cannot ping" ,hostname,"- is it active on " "the network?") # board booted conn = Transceiver(hostname, 17893) # open a connection to the board for probing with SCP packets bootedtimeout=5 # number of times to retry operations to wake up the board / ARP entries on the network while (bootedtimeout): try: conn.select('root') version = conn.conn.version(retries=3) print version.desc break except socket.error: print "." bootedtimeout-=1 except Exception: self.explore_a_reboot(hostname) # recover dimensions ydims = int(numpy.fromstring(conn.memory_calls.read_mem(0xf5007f02, scamp.TYPE_BYTE, 1), dtype=numpy.uint8)) xdims = int(numpy.fromstring(conn.memory_calls.read_mem(0xf5007f03, scamp.TYPE_BYTE, 1), dtype=numpy.uint8)) myString = "EXPLORE: found machine '%s' that is contactable," % hostname myString += " and has dimensions (x:%d, y:%d)" % (xdims, ydims) logger.debug(myString) if (xdims==0): errorString = "EXPLORE ERROR: '%s' is booted," % hostname errorString += " but x-dimension is returning zero!" raise exceptions.ExploreException(errorString) if (ydims==0): errorString = "EXPLORE ERROR: '%s' is booted," % hostname errorString += " but y-dimension is returning zero!" raise exceptions.ExploreException(errorString) if (y!=None or x!=None): # hints provided so better check them if (xdims<x or ydims<y): raise exceptions.ExploreException("EXPLORE ERROR: '", hostname, "' the x and y hints supplied " "are bigger than the actual " "machine dimensions") return xdims,ydims def explore_a_reboot(self, hostname): if (conf.config.has_option("Machine", "tryReboot") and conf.config.getboolean("Machine", "tryReboot")): logger.info("cannot ping {}- will try to reboot board".format(hostname)) version_number = conf.config.get("Machine", "version") if version_number is None or version_number == "None" or int(version_number) > 5: raise exceptions.SpinnManException("version number is not defined in pacman.cfg. Please enter and retry") boot_file = self.checkfile("scamp-130.boot") struct_file = self.checkfile("sark-130.struct") config_file = self.checkfile("spin{}.conf".format(version_number)) boot.boot(hostname, boot_file, config_file, struct_file) #used to hold up and wait for spinn board to have completed its boot up (only on rowleys board) time.sleep(1.0) else: raise exceptions.ExploreException("EXPLORE ERROR: Cannot ping" ,hostname,"- is it active on " "the network?") def checkfile(self, test_file): real_file = test_file if not os.path.isfile(real_file): components = os.path.abspath(conf.__file__).split(os.sep) directory = os.path.abspath(os.path.join(os.sep, *components[1:components.index("pacman103")])) real_file = os.path.join(directory, "spinnaker_tools", "boot", real_file) print real_file if not os.path.isfile(real_file): print "File %s not found" % test_file sys.exit(3) return real_file def select (self, *args): """ Select the target node and processor. :param args: variadic argument (usage below) :raises: ValueError This function has the following calling conventions: ``conn.select ('root')`` Short-hand to select node (0, 0, 0) ``conn.select (N)`` Selects processor N on the currently selected node ``conn.select (X, Y)`` Selects processor 0 on node (``X``, ``Y``) ``conn.select (X, Y, N)`` Selects processor ``N`` on node (``X``, ``Y``) """ # extract the arguments if len (args) == 1 and type (args[0]) == str and args[0] == "root": (x, y, cpu) = (0, 0, 0) elif len (args) == 1 and type (args[0]) == int: (x, y, cpu) = (self._x, self._y, args[0]) elif len (args) == 2: (x, y, cpu) = (args[0], args[1], 0) elif len (args) == 3: (x, y, cpu) = args else: raise ValueError ("invalid arguments given for SCPConnection." "select call.") # make sure that the variables are all ints if type (x) != int or type (y) != int or type (cpu) != int: raise ValueError ("invalid argument types given expecting ints or " "a single string 'root'.") # save the variables self.app_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.memory_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.packet_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.conn.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self._x = x & 0xFF self._y = y & 0xFF
class Transceiver(object): """ A Transceiver is instantiated by a :py:func:`pacman103.pacman.control.Controller` object in order to communicate with a SpiNNaker board. The Transceiver class is simply a wrapper around the :py:mod:`pacman103.scp` module, which does the actual interfaces over Ethernet. An instance of the class maintains a SCP connection to a machine, through which all interfaces takes place. :param string hostname: hostname of the SpiNNaker machine on which the simulation is to be run. """ def __init__(self, hostname, port=17893): self.conn = SCPConnection(hostname, port) self.app_calls = AppCalls(self) self.memory_calls = MemoryCalls(self) self.packet_calls = PacketCalls(self) self._x = 0 self._y = 0 self.utility = None def read_memory(self, x, y, p, a, l, dtype): """ Read memory from the SpiNNaker machine. :param int x: x-chip-coordinate. :param int y: y-chip-coordinate. :param int p: processor ID. :param int a: target load address. :param int l: length of data to read (bytes). :param numpy.dtype dtype: datatype of the returned numpy array (e.g. numpy.int32) :returns: numpy.ndarray containing the requested memory. """ self.select(x, y, p) memory = self.memory_calls.read_mem(a, scamp.TYPE_WORD, l) memory = numpy.fromstring(memory, dtype=dtype) return memory def load_targets(self, dao): """ Loads LoadTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.load_targets_raw`. """ if conf.config.get("Reports", "write_reload_steps"): self.utility = SpinnmanUtilities(dao=dao) #check to see if the machine is contactable & loadable self.check_target_machine(dao.machine.hostname, dao.machine.y_dim, dao.machine.y_dim) # now get the data to load and then do it targets = dao.get_load_targets() self.load_targets_raw(targets) def load_targets_raw(self, targets): """ Uses the SCP connection to load LoadTargets to the machine. :param list targets: list of :py:class:`pacman103.lib.lib_map.LoadTarget` instances. """ for target in targets: if conf.config.get("Reports", "write_reload_steps"): self.utility.write_selects(target.x, target.y, target.p) self.utility.write_mem_from_file(target.address, scamp.TYPE_WORD, target.filename) self.select(target.x, target.y, target.p) self.memory_calls.write_mem_from_file(target.address, scamp.TYPE_WORD, target.filename) def load_targets_load(self, file_name): self.utility = SpinnmanUtilities(input_file=file_name) commands = self.utility.get_mem_writes_from_file() for index in range(0, len(commands), 2): self.select(int(commands[index]['x']), int(commands[index]['y']), int(commands[index]['p'])) self.memory_calls.write_mem_from_file( int(commands[index + 1]['address']), int(commands[index + 1]['type_word']), commands[index + 1]['filename']) def load_write_mem(self, dao): """ Loads WriteMemTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.load_write_mem_raw`. """ targets = dao.get_mem_write_targets() self.load_write_mem_raw(targets) def load_write_mem_raw(self, targets): """ Uses the SCP connection to load WriteMemTargets to the machine. :param list targets: list of :py:class:`pacman103.lib.lib_map.WriteMemTarget` instances. """ for target in targets: if conf.config.get("Reports", "write_reload_steps"): self.utility.write_selects(target.x, target.y, target.p) self.utility.write_mem(target.address, scamp.TYPE_WORD, struct.pack("I", target.data)) self.select(target.x, target.y, target.p) self.memory_calls.write_mem(target.address, scamp.TYPE_WORD, struct.pack("I", target.data)) def load_write_mem_load(self, file_name): self.utility = SpinnmanUtilities(input_file=file_name) commands = self.utility.get_mem_writes() for index in range(0, len(commands), 2): self.select(int(commands[index]['x']), int(commands[index]['y']), int(commands[index]['p'])) self.memory_calls.write_mem(int(commands[index + 1]['address']), int(commands[index + 1]['type_word']), commands[index + 1]['structure']) def reset_board(self): """ TODO Implement to reset Spinn4 boards over SCP. """ """ #TODO check board is version 4 or greater scp.reset(kwargs['bmp']) time.sleep(5) scp.boot(hostname, '/home/tomxsharp/Thesis/spinnaker/tools/scamp-200.boot')#COMMENT ME time.sleep(2) self.txrx.conn.init_p2p_tables(self.dao.machine.x_dim, self.dao.machine.y_dim) self.txrx.conn.set_iptag(0, 'localhost', 17892) """ pass def run(self, dao, app_id): """ Loads ExecutableTargets from the datastore and calls :py:func:`pacman103.pacman.transceiver.Transceiver.run_raw`. """ machine = dao.get_machine() targets = dao.get_executable_targets() iptags = dao.get_iptags() run_time = dao.run_time #TODO accessor method self.run_raw(machine, targets, run_time, app_id, iptags, dao) def run_raw(self, machine, targets, run_time, app_id, iptags, dao): """ Uses the SCP connection to trigger simulation. :param `pacman103.lib.lib_machine.Machine` machine: machine to run the simulation on. :param list targets: list of :py:class:`pacman103.lib.lib_map.ExecutableTarget` instances. :param int run_time: run time of the simulation (milliseconds) """ # Select monitor pacman on ethernet-adjacent chip self.select(0, 0) # Set IP tags for iptag in iptags: retries = 0 while retries < 3: try: logger.info("Setting up ip tag {} to {}:{}".format( iptag.tag, iptag.hostname, iptag.port)) self.conn.set_iptag(iptag.tag, iptag.hostname, iptag.port) retries = 3 except Exception as e: if retries == 3: exceptions.SpinnManException("IPTags failed to be set " "due to {}".format( e.message)) retries += 1 time.sleep(0.1) total_processors = 0 targets = self.organise_targets(targets) for key in targets.keys(): chips = targets[key] core_mask = 0 for chip in chips: processors = chips[chip] core_part_of_region = "" first = True for processor in processors: core_mask += processor if first: core_part_of_region += "{}".format(processor) first = False else: core_part_of_region += ",{}".format(processor) total_processors += 1 (x, y) = chip.split(",") region = Utility.calculate_region_id(int(x), int(y)) if conf.config.get("Reports", "write_reload_steps"): self.utility.write_app_load_command( key, region, core_part_of_region, app_id) self.app_calls.app_load(key, region, core_part_of_region, app_id) processors_ready = 0 logger.debug("checking that the processors currently" " flood filled are ready for future flood fills") while processors_ready < total_processors: processors_ready = self.app_calls.app_signal( app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.debug("{} processors out of {} " "processors are ready".format( processors_ready, total_processors)) logger.info("Waiting for application to finish loading") if conf.config.get("Reports", "write_reload_steps"): self.utility.write_extra_data(run_time, total_processors) self.utility.close() self.check_synco_and_run(total_processors, app_id, run_time, dao) def check_synco_and_run(self, total_processors, app_id, run_time, dao): ''' checks that all processors have reached sync0 and runs the application for the given runtime ''' processors_ready = 0 while processors_ready < total_processors: processors_ready = self.app_calls.app_signal( app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.info("Starting application") self.app_calls.app_signal(app_id, scamp.SIGNAL_SYNC0) logger.info("Checking that the application has started") processors_running = self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RUN) if processors_running < total_processors: raise exceptions.SpinnManException("Only {} of {} processors " "started".format( processors_running, total_processors)) if run_time is not None: logger.info("Application started - waiting for it to stop") dao.write_running_msg() time.sleep(run_time / 1000.0) processors_not_finished = processors_ready while processors_not_finished != 0: processors_not_finished = self.app_calls.app_signal( app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RUN) processors_rte = self.app_calls.app_signal( app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_RTE) if processors_rte > 0: raise exceptions.SpinnManException( "{} cores have gone into " "a run time error state.".format(processors_rte)) processors_exited = self.app_calls.app_signal( app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_EXIT) if processors_exited < total_processors: raise exceptions.\ SpinnManException("{} of the processors " "failed to exit successfully" .format(total_processors - processors_exited)) logger.info("Application has run to completion") # update the dao holder so that stuff can be read from the SDRAM dao.has_ran = True else: logger.info( "Application is set to run forever - PACMAN is exiting") retry_counts = self.conn.get_retries() logger.debug("Total retries on this run due to RC_TIMEOUT: {}, " + "RC_P2P_TIMEOUT: {}, RC_LEN: {}".format( retry_counts[0], retry_counts[1], retry_counts[2])) def run_load(self, utility_file): utility = SpinnmanUtilities(utility_file) run_time = utility.get_run_time() total_processors_of_app = utility.get_total_processors() app_loads = utility.get_app_loads() total_processors = 0 app_id = None for app_load in app_loads: self.app_calls.app_load(app_load['key'], app_load['region'], app_load['core_part_of_region'], app_load['app_id']) app_id = app_load['app_id'] processors_ready = 0 logger.debug("checking that the processors currently" " flood filled are ready for future flood fills") while processors_ready < total_processors: processors_ready = \ self.app_calls.app_signal(app_id, scamp.SIGNAL_COUNT, scamp.PROCESSOR_SYNC0) logger.debug("{} processors out of {} processors are " "ready".format(processors_ready, total_processors)) self.check_synco_and_run(total_processors_of_app, app_id, run_time) def organise_targets(self, targets): ''' method that takes the targets and converts them into a list of chip scoped targets where each entry contains the chip ''' organised_targets = dict() for target in targets: key = "{},{}".format(target.targets[0]['x'], target.targets[0]['y']) proc = target.targets[0]['p'] if organised_targets.has_key(target.filename): chip_collection = organised_targets.get(target.filename) if key in chip_collection: chip_collection[key].append(proc) else: chip_collection[key] = [proc] else: #add with the chip definition chip_collection = dict() chip_collection[key] = [proc] organised_targets[target.filename] = chip_collection return organised_targets def check_target_machine(self, hostname, x, y): """ This routine takes the requested dimension hints and the hostname of the machine, then checks that it is pingable on the network, has been booted and has physical dimensions that at least match the requested dimensions. :param int hostname: address of the physical machine that we'll be using :param int x: dimensions requested of the machine (may be smaller than the physical machine, but not bigger!) :param int y: dimensions requested of the machine (may be smaller than the physical machine, but not bigger!) :returns: xdims, and ydims which contain the physical dimensions detected in the machine :raises: ExploreException """ # check if machine is active and on the network pingtimeout = 5 # number of times to retry operations to wake up the board / ARP entries on the network while (pingtimeout): process = None if (platform.platform().lower().startswith("windows")): process = subprocess.Popen("ping -n 1 -w 1 " + hostname, shell=True, stdout=subprocess.PIPE) else: process = subprocess.Popen("ping -c 1 -W 1 " + hostname, shell=True, stdout=subprocess.PIPE) process.wait() if (process.returncode == 0): break # ping worked else: print "." pingtimeout -= 1 if (pingtimeout == 0): raise exceptions.ExploreException( "EXPLORE ERROR: Cannot ping", hostname, "- is it active on " "the network?") # board booted conn = Transceiver(hostname, 17893) # open a connection to the board for probing with SCP packets bootedtimeout = 5 # number of times to retry operations to wake up the board / ARP entries on the network while (bootedtimeout): try: conn.select('root') version = conn.conn.version(retries=3) print version.desc break except socket.error: print "." bootedtimeout -= 1 except Exception: self.explore_a_reboot(hostname) # recover dimensions ydims = int( numpy.fromstring(conn.memory_calls.read_mem( 0xf5007f02, scamp.TYPE_BYTE, 1), dtype=numpy.uint8)) xdims = int( numpy.fromstring(conn.memory_calls.read_mem( 0xf5007f03, scamp.TYPE_BYTE, 1), dtype=numpy.uint8)) myString = "EXPLORE: found machine '%s' that is contactable," % hostname myString += " and has dimensions (x:%d, y:%d)" % (xdims, ydims) logger.debug(myString) if (xdims == 0): errorString = "EXPLORE ERROR: '%s' is booted," % hostname errorString += " but x-dimension is returning zero!" raise exceptions.ExploreException(errorString) if (ydims == 0): errorString = "EXPLORE ERROR: '%s' is booted," % hostname errorString += " but y-dimension is returning zero!" raise exceptions.ExploreException(errorString) if (y != None or x != None): # hints provided so better check them if (xdims < x or ydims < y): raise exceptions.ExploreException( "EXPLORE ERROR: '", hostname, "' the x and y hints supplied " "are bigger than the actual " "machine dimensions") return xdims, ydims def explore_a_reboot(self, hostname): if (conf.config.has_option("Machine", "tryReboot") and conf.config.getboolean("Machine", "tryReboot")): logger.info( "cannot ping {}- will try to reboot board".format(hostname)) version_number = conf.config.get("Machine", "version") if version_number is None or version_number == "None" or int( version_number) > 5: raise exceptions.SpinnManException( "version number is not defined in pacman.cfg. Please enter and retry" ) boot_file = self.checkfile("scamp-130.boot") struct_file = self.checkfile("sark-130.struct") config_file = self.checkfile("spin{}.conf".format(version_number)) boot.boot(hostname, boot_file, config_file, struct_file) #used to hold up and wait for spinn board to have completed its boot up (only on rowleys board) time.sleep(1.0) else: raise exceptions.ExploreException( "EXPLORE ERROR: Cannot ping", hostname, "- is it active on " "the network?") def checkfile(self, test_file): real_file = test_file if not os.path.isfile(real_file): components = os.path.abspath(conf.__file__).split(os.sep) directory = os.path.abspath( os.path.join(os.sep, *components[1:components.index("pacman103")])) real_file = os.path.join(directory, "spinnaker_tools", "boot", real_file) print real_file if not os.path.isfile(real_file): print "File %s not found" % test_file sys.exit(3) return real_file def select(self, *args): """ Select the target node and processor. :param args: variadic argument (usage below) :raises: ValueError This function has the following calling conventions: ``conn.select ('root')`` Short-hand to select node (0, 0, 0) ``conn.select (N)`` Selects processor N on the currently selected node ``conn.select (X, Y)`` Selects processor 0 on node (``X``, ``Y``) ``conn.select (X, Y, N)`` Selects processor ``N`` on node (``X``, ``Y``) """ # extract the arguments if len(args) == 1 and type(args[0]) == str and args[0] == "root": (x, y, cpu) = (0, 0, 0) elif len(args) == 1 and type(args[0]) == int: (x, y, cpu) = (self._x, self._y, args[0]) elif len(args) == 2: (x, y, cpu) = (args[0], args[1], 0) elif len(args) == 3: (x, y, cpu) = args else: raise ValueError("invalid arguments given for SCPConnection." "select call.") # make sure that the variables are all ints if type(x) != int or type(y) != int or type(cpu) != int: raise ValueError("invalid argument types given expecting ints or " "a single string 'root'.") # save the variables self.app_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.memory_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.packet_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.conn.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self._x = x & 0xFF self._y = y & 0xFF
class Transceiver(object): """ A Transceiver is instantiated by a :py:func:`pacman103.pacman.control.Controller` object in order to communicate with a SpiNNaker board. The Transceiver class is simply a wrapper around the :py:mod:`pacman103.scp` module, which does the actual interfaces over Ethernet. An instance of the class maintains a SCP connection to a machine, through which all interfaces takes place. :param string hostname: hostname of the SpiNNaker machine on which the simulation is to be run. """ def __init__(self, hostname, port=17893): self.conn = SCPConnection(hostname, port) self.app_calls = AppCalls(self) self.memory_calls = MemoryCalls(self) self.packet_calls = PacketCalls(self) self._x = 0 self._y = 0 self.utility = None def read_memory(self, x, y, p, a, l, dtype): """ Read memory from the SpiNNaker machine. :param int x: x-chip-coordinate. :param int y: y-chip-coordinate. :param int p: processor ID. :param int a: target load address. :param int l: length of data to read (bytes). :param numpy.dtype dtype: datatype of the returned numpy array (e.g. numpy.int32) :returns: numpy.ndarray containing the requested memory. """ self.select(x, y, p) memory = self.memory_calls.read_mem(a, scamp.TYPE_WORD, l) memory = numpy.fromstring(memory, dtype=dtype) return memory def organise_targets(self, targets): ''' method that takes the targets and converts them into a list of chip scoped targets where each entry contains the chip ''' organised_targets = dict() for target in targets: filename = "{},{}".format(target.targets[0]['x'], target.targets[0]['y']) proc = target.targets[0]['p'] if organised_targets.has_key(target.filename): chip_collection = organised_targets.get(target.filename) if filename in chip_collection: chip_collection[filename].append(proc) else: chip_collection[filename] = [proc] else: #add with the chip definition chip_collection = dict() chip_collection[filename] = [proc] organised_targets[target.filename] = chip_collection return organised_targets def checkfile(self, test_file): real_file = test_file if not os.path.isfile(real_file): directory = os.path.dirname(boot_files.__file__) real_file = os.path.join(directory, real_file) if not os.path.isfile(real_file): print "File %s not found" % test_file sys.exit(3) return real_file def select(self, *args): """ Select the target node and processor. :param args: variadic argument (usage below) :raises: ValueError This function has the following calling conventions: ``conn.select ('root')`` Short-hand to select node (0, 0, 0) ``conn.select (N)`` Selects processor N on the currently selected node ``conn.select (X, Y)`` Selects processor 0 on node (``X``, ``Y``) ``conn.select (X, Y, N)`` Selects processor ``N`` on node (``X``, ``Y``) """ # extract the arguments if len(args) == 1 and type(args[0]) == str and args[0] == "root": (x, y, cpu) = (0, 0, 0) elif len(args) == 1 and type(args[0]) == int: (x, y, cpu) = (self._x, self._y, args[0]) elif len(args) == 2: (x, y, cpu) = (args[0], args[1], 0) elif len(args) == 3: (x, y, cpu) = args else: raise ValueError("invalid arguments given for SCPConnection." "select call.") # make sure that the variables are all ints if type(x) != int or type(y) != int or type(cpu) != int: raise ValueError("invalid argument types given expecting ints or " "a single string 'root'.") # save the variables self.app_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.memory_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.packet_calls.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self.conn.set_view(x & 0xFF, y & 0xFF, cpu, (self._x << 8) | self._y) self._x = x & 0xFF self._y = y & 0xFF