Esempio n. 1
0
    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
Esempio n. 3
0
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)