def load_application(self, controller): """Load the netlist to a SpiNNaker machine. Parameters ---------- controller : :py:class:`~rig.machine_control.MachineController` Controller to use to communicate with the machine. """ # Build and load the routing tables, first by building a mapping from # nets to keys and masks. logger.debug("Loading routing tables") net_keys = {n: (n.keyspace.get_value(tag=self.keyspaces.routing_tag), n.keyspace.get_mask(tag=self.keyspaces.routing_tag)) for n in self.nets} routing_tables = build_routing_tables(self.routes, net_keys) controller.load_routing_tables(routing_tables) # Assign memory to each vertex as required logger.debug("Assigning application memory") self.vertices_memory = sdram_alloc_for_vertices( controller, self.placements, self.allocations ) # Inform the vertices of where that chunk of memory is for vertex, memory in iteritems(self.vertices_memory): x, y = self.placements[vertex] p = self.allocations[vertex][Cores].start controller.write_vcpu_struct_field( "user0", memory.address, x, y, p) # Call each loading function in turn logger.debug("Loading data") for fn in self.load_functions: fn(self, controller) # Load the applications onto the machine logger.debug("Loading application executables") vertices_applications = {v: v.application for v in self.vertices if v.application is not None} application_map = build_application_map( vertices_applications, self.placements, self.allocations ) controller.load_application(application_map)
def run(self, app_id=0x42, create_group_if_none_exist=True, ignore_deadline_errors=False): """Run the experiment on SpiNNaker and return the results. If placements, allocations or routes have not been provided, the vertices and nets will be automatically placed, allocated and routed using the default algorithms in Rig. Following placement, the experimental parameters are loaded onto the machine and each experimental group is executed in turn. Results are recorded by the machine and at the end of the experiment are read back. .. warning:: Though a global synchronisation barrier is used between the execution of each group, the timers in each vertex may drift out of sync during each group's execution. Further, the barrier synchronisation does not give any guarantees about how closely-synchronised the timers will be at the start of each run. Parameters ---------- app_id : int *Optional.* The SpiNNaker application ID to use for the experiment. create_group_if_none_exist : bool *Optional.* If True (the default), a single group will be automatically created if none have been defined with :py:meth:`.new_group`. This is the most sensible behaviour for most applications. If you *really* want to run an experiment with no experimental groups (where no traffic will ever be generated and no results recorded), you can set this option to False. ignore_deadline_errors : bool If True, any realtime deadline-missed errors will no longer cause this method to raise an exception. Other errors will still cause an exception to be raised. This option is useful when running experiments which involve over-saturating packet sinks or the network in some experimental groups. Returns ------- :py:class:`Results` If no vertices reported errors, the experimental results are returned. See the :py:class:`Results` object for details. Raises ------ NetworkTesterError A :py:exc:`NetworkTesterError` is raised if any vertices reported an error. The most common error is likely to be a 'deadline missed' error as a result of the experimental timestep being too short or the load on some vertices too high in extreme circumstances. Other types of error indicate far more severe problems. Any results recorded during the run will be included in the ``results`` attribute of the exception. See the :py:class:`Results` object for details. """ # Sensible default: Create a single experimental group if none defined. if create_group_if_none_exist and len(self._groups) == 0: self.new_group() # Place and route the vertices (if required) self.place_and_route() # Add nodes to unused chips to access router registers/counters (if # necessary). (vertices, router_access_vertices, placements, allocations, routes) = \ self._add_router_recording_vertices() # Assign a unique routing key to each net net_keys = {net: num << 8 for num, net in enumerate(self._nets)} routing_tables = build_routing_tables( routes, {net: (key, 0xFFFFFF00) for net, key in iteritems(net_keys)}) network_tester_binary = pkg_resources.resource_filename( "network_tester", "binaries/network_tester.aplx") reinjector_binary = pkg_resources.resource_filename( "network_tester", "binaries/reinjector.aplx") # Specify the appropriate binary for the network tester vertices. application_map = build_application_map( {vertex: network_tester_binary for vertex in vertices}, placements, allocations) # Get the set of source and sink nets for each vertex. Also sets an # explicit ordering of the sources/sinks within each. # {vertex: [source_or_sink, ...], ...} vertices_source_nets = {v: [] for v in vertices} vertices_sink_nets = {v: [] for v in vertices} for net in self._nets: vertices_source_nets[net.source].append(net) for sink in net.sinks: vertices_sink_nets[sink].append(net) vertices_records = self._get_vertex_record_lookup( vertices, router_access_vertices, placements, vertices_source_nets, vertices_sink_nets) # Fill out the set of commands for each vertex vertices_commands = { vertex: self._construct_vertex_commands( vertex=vertex, source_nets=vertices_source_nets[vertex], sink_nets=vertices_sink_nets[vertex], net_keys=net_keys, records=[cntr for obj, cntr in vertices_records[vertex]], router_access_vertex=vertex in router_access_vertices) for vertex in vertices } # The data size for the results from each vertex total_num_samples = sum(g.num_samples for g in self._groups) vertices_result_size = { vertex: ( # The error flag (one word) 1 + # One word per recorded value per sample. (total_num_samples * len(vertices_records[vertex])) ) * 4 for vertex in vertices} # The raw result data for each vertex. vertices_result_data = {} # Actually load and run the experiment on the machine. with self._mc.application(app_id): # Allocate SDRAM. This is enough to fit the commands and also any # recored results. vertices_sdram = {} logger.info("Allocating SDRAM...") for vertex in vertices: size = max( # Size of commands (with length prefix) vertices_commands[vertex].size, # Size of results (plus the flags) vertices_result_size[vertex], ) x, y = placements[vertex] p = allocations[vertex][Cores].start vertices_sdram[vertex] = self._mc.sdram_alloc_as_filelike( size, x=x, y=y, tag=p) # Load each vertex's commands logger.info("Loading {} bytes of commands...".format( sum(c.size for c in itervalues(vertices_commands)))) for vertex, sdram in iteritems(vertices_sdram): sdram.write(vertices_commands[vertex].pack()) # Load routing tables logger.info("Loading routing tables...") self._mc.load_routing_tables(routing_tables) # Load the packet-reinjection application if used. This must be # completed before the main application since it creates a tagged # memory allocation. if self._reinjection_used(): logger.info("Loading packet-reinjection application...") self._mc.load_application(reinjector_binary, {xy: set([1]) for xy in self.machine}) # Load the application logger.info("Loading application on to {} cores...".format( len(vertices))) self._mc.load_application(application_map) # Run through each experimental group next_barrier = "sync0" for group_num, group in enumerate(self._groups): # Reach the barrier before the run starts logger.info("Waiting for barrier...") num_at_barrier = self._mc.wait_for_cores_to_reach_state( next_barrier, len(vertices), timeout=2.0) assert num_at_barrier == len(vertices), \ "Not all cores reached the barrier " \ "before {}.".format(group) self._mc.send_signal(next_barrier) next_barrier = "sync1" if next_barrier == "sync0" else "sync0" # Give the run time to complete warmup = self._get_option_value("warmup", group) duration = self._get_option_value("duration", group) cooldown = self._get_option_value("cooldown", group) flush_time = self._get_option_value("flush_time", group) total_time = warmup + duration + cooldown + flush_time logger.info( "Running group {} ({} of {}) for {} seconds...".format( group.name, group_num + 1, len(self._groups), total_time)) time.sleep(total_time) # Wait for all cores to exit after their final run logger.info("Waiting for barrier...") num_at_barrier = self._mc.wait_for_cores_to_reach_state( "exit", len(vertices), timeout=2.0) assert num_at_barrier == len(vertices), \ "Not all cores reached the final barrier." # Read recorded data back logger.info("Reading back {} bytes of results...".format( sum(itervalues(vertices_result_size)))) for vertex, sdram in iteritems(vertices_sdram): sdram.seek(0) vertices_result_data[vertex] = \ sdram.read(vertices_result_size[vertex]) # Process read results results = Results(self, self._vertices, self._nets, vertices_records, router_access_vertices, placements, routes, vertices_result_data, self._groups) if any(not e.is_deadline if ignore_deadline_errors else True for e in results.errors): logger.error( "Experiment completed with errors: {}".format(results.errors)) raise NetworkTesterError(results) else: logger.info("Experiment completed successfully") return results
def wrapper(vertices_resources, vertices_applications, nets, net_keys, machine, constraints=[], reserve_monitor=True, align_sdram=True, place=default_place, place_kwargs={}, allocate=default_allocate, allocate_kwargs={}, route=default_route, route_kwargs={}, core_resource=Cores, sdram_resource=SDRAM): """Wrapper for core place-and-route tasks for the common case. At a high level this function essentially takes a set of vertices and nets and produces placements, memory allocations, routing tables and application loading information. .. warning:: This function is deprecated. New users should use :py:func:`.place_and_route_wrapper` along with :py:meth:`rig.machine_control.MachineController.get_system_info` in place of this function. The new wrapper automatically reserves cores and SDRAM already in use in the target machine, improving on the behaviour of this wrapper which blindly reserves certain ranges of resources presuming only core 0 (the monitor processor) is not idle. Parameters ---------- vertices_resources : {vertex: {resource: quantity, ...}, ...} A dictionary from vertex to the required resources for that vertex. This dictionary must include an entry for every vertex in the application. Resource requirements are specified by a dictionary `{resource: quantity, ...}` where `resource` is some resource identifier and `quantity` is a non-negative integer representing the quantity of that resource required. vertices_applications : {vertex: application, ...} A dictionary from vertices to the application binary to load onto cores associated with that vertex. Applications are given as a string containing the file name of the binary to load. nets : [:py:class:`~rig.netlist.Net`, ...] A list (in no particular order) defining the nets connecting vertices. net_keys : {:py:class:`~rig.netlist.Net`: (key, mask), ...} A dictionary from nets to (key, mask) tuples to be used in SpiNNaker routing tables for routes implementing this net. The key and mask should be given as 32-bit integers. machine : :py:class:`rig.place_and_route.Machine` A data structure which defines the resources available in the target SpiNNaker machine. constraints : [constraint, ...] A list of constraints on placement, allocation and routing. Available constraints are provided in the :py:mod:`rig.place_and_route.constraints` module. reserve_monitor : bool (Default: True) **Optional.** If True, reserve core zero since it will be used as the monitor processor using a :py:class:`rig.place_and_route.constraints.ReserveResourceConstraint`. align_sdram : bool (Default: True) **Optional.** If True, SDRAM allocations will be aligned to 4-byte addresses. Specifically, the supplied constraints will be augmented with an `AlignResourceConstraint(sdram_resource, 4)`. place : function (Default: :py:func:`rig.place_and_route.place`) **Optional.** Placement algorithm to use. place_kwargs : dict (Default: {}) **Optional.** Algorithm-specific arguments for the placer. allocate : function (Default: :py:func:`rig.place_and_route.allocate`) **Optional.** Allocation algorithm to use. allocate_kwargs : dict (Default: {}) **Optional.** Algorithm-specific arguments for the allocator. route : function (Default: :py:func:`rig.place_and_route.route`) **Optional.** Routing algorithm to use. route_kwargs : dict (Default: {}) **Optional.** Algorithm-specific arguments for the router. core_resource : resource (Default: :py:data:`~rig.place_and_route.Cores`) **Optional.** The resource identifier used for cores. sdram_resource : resource (Default: :py:data:`~rig.place_and_route.SDRAM`) **Optional.** The resource identifier used for SDRAM. Returns ------- placements : {vertex: (x, y), ...} A dictionary from vertices to the chip coordinate produced by placement. allocations : {vertex: {resource: slice, ...}, ...} A dictionary from vertices to the resources allocated to it. Resource allocations are dictionaries from resources to a :py:class:`slice` defining the range of the given resource type allocated to the vertex. These :py:class:`slice` objects have `start` <= `end` and `step` set to None. application_map : {application: {(x, y): set([core_num, ...]), ...}, ...} A dictionary from application to the set of cores it should be loaded onto. The set of cores is given as a dictionary from chip to sets of core numbers. routing_tables : {(x, y): \ [:py:class:`~rig.routing_table.RoutingTableEntry`, \ ...], ...} The generated routing tables. Provided as a dictionary from chip to a list of routing table entries. """ warnings.warn( "rig.place_and_route.wrapper is deprecated " "use rig.place_and_route.place_and_route_wrapper instead in " "new applications.", DeprecationWarning) constraints = constraints[:] # Augment constraints with (historically) commonly used constraints if reserve_monitor: constraints.append( ReserveResourceConstraint(core_resource, slice(0, 1))) if align_sdram: constraints.append(AlignResourceConstraint(sdram_resource, 4)) # Place/Allocate/Route placements = place(vertices_resources, nets, machine, constraints, **place_kwargs) allocations = allocate(vertices_resources, nets, machine, constraints, placements, **allocate_kwargs) routes = route(vertices_resources, nets, machine, constraints, placements, allocations, core_resource, **route_kwargs) # Build data-structures ready to feed to the machine loading functions application_map = build_application_map(vertices_applications, placements, allocations, core_resource) # Build data-structures ready to feed to the machine loading functions from rig.place_and_route.utils import build_routing_tables routing_tables = build_routing_tables(routes, net_keys) return placements, allocations, application_map, routing_tables
parser.add_argument("--verbose", "-v", action="count", default=0, help="verbosity level (may be given multiple times)") args = parser.parse_args() if args.verbose >= 2: logging.basicConfig(level=logging.DEBUG) elif args.verbose >= 1: logging.basicConfig(level=logging.INFO) with open(args.routes, "r") as f: routes = unpack_routes(json.load(f)) with open(args.routing_keys, "r") as f: net_keys = unpack_net_keys(json.load(f)) tables = build_routing_tables(routes, net_keys, not args.keep_default_routes) with open(args.routing_tables, "w") as f: json.dump([ { "chip": xy, "entries": [ { "key": entry.key, "mask": entry.mask, "directions": [route_name(r) for r in entry.route] } for entry in entries ] }
parser.add_argument("--verbose", "-v", action="count", default=0, help="verbosity level (may be given multiple times)") args = parser.parse_args() if args.verbose >= 2: logging.basicConfig(level=logging.DEBUG) elif args.verbose >= 1: logging.basicConfig(level=logging.INFO) with open(args.routes, "r") as f: routes = unpack_routes(json.load(f)) with open(args.routing_keys, "r") as f: net_keys = unpack_net_keys(json.load(f)) tables = build_routing_tables(routes, net_keys, not args.keep_default_routes) with open(args.routing_tables, "w") as f: json.dump([{ "chip": xy, "entries": [{ "key": entry.key, "mask": entry.mask, "directions": [route_name(r) for r in entry.route] } for entry in entries] } for xy, entries in iteritems(tables)], f)