Source code for spinn_front_end_common.interface.interface_functions.system_multicast_routing_generator

# Copyright (c) 2017 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import defaultdict
import logging
from spinn_utilities.log import FormatAdapter
from pacman.exceptions import (PacmanRoutingException)
from pacman.model.routing_tables import (
    MulticastRoutingTables, UnCompressedMulticastRoutingTable)
from spinn_machine import MulticastRoutingEntry
from spinn_utilities.progress_bar import ProgressBar
from spinn_front_end_common.data import FecDataView

# ADDRESS_KEY, DATA_KEY, BOUNDARY_KEY
N_KEYS_PER_PARTITION_ID = 8
# timeout key, timeout emergency key, clear reinjection queue.
N_KEYS_PER_REINJECTION_PARTITION = 3
KEY_START_VALUE = 8
ROUTING_MASK = 0xFFFFFFF8

logger = FormatAdapter(logging.getLogger(__name__))


[docs]def system_multicast_routing_generator(): """ Generates routing table entries used by the data in processes with the extra monitor cores. :return: routing tables, destination-to-key map, board-locn-to-timeout-key map :rtype: tuple(~pacman.model.routing_tables.MulticastRoutingTables, dict(tuple(int,int),int), dict(tuple(int,int),int)) """ generator = _SystemMulticastRoutingGenerator() # pylint: disable=protected-access return generator._run()
class _SystemMulticastRoutingGenerator(object): """ Generates routing table entries used by the data in processes with the extra monitor cores. """ __slots__ = ["_key_to_destination_map", "_machine", "_routing_tables", "_time_out_keys_by_board"] def __init__(self): """ :param ~pacman.model.placements.Placements placements: """ self._machine = FecDataView.get_machine() self._routing_tables = MulticastRoutingTables() self._key_to_destination_map = dict() self._time_out_keys_by_board = dict() def _run(self): """ :return: routing tables, destination-to-key map, board-locn-to-timeout-key map :rtype: tuple(~pacman.model.routing_tables.MulticastRoutingTables, dict(tuple(int,int),int), dict(tuple(int,int),int)) """ # create progress bar progress = ProgressBar( self._machine.ethernet_connected_chips, "Generating routing tables for data in system processes") for ethernet_chip in progress.over( self._machine.ethernet_connected_chips): tree = self._generate_routing_tree(ethernet_chip) if tree is None: tree = self._logging_retry(ethernet_chip) self._add_routing_entries(ethernet_chip, tree) return (self._routing_tables, self._key_to_destination_map, self._time_out_keys_by_board) def _generate_routing_tree(self, ethernet_chip): """ Generates a map for each chip to over which link it gets its data. :param ~spinn_machine.Chip ethernet_chip: :return: Map of chip.x, chip.y tp (source.x, source.y, source.link) :rtype: dict(tuple(int, int), tuple(int, int, int)) """ eth_x = ethernet_chip.x eth_y = ethernet_chip.y tree = dict() to_reach = set( self._machine.get_existing_xys_by_ethernet(eth_x, eth_y)) reached = set() reached.add((eth_x, eth_y)) to_reach.remove((eth_x, eth_y)) found = set() found.add((eth_x, eth_y)) while len(to_reach) > 0: just_reached = found found = set() for x, y in just_reached: # Check links starting with the most direct from 0,0 for link_id in [1, 0, 2, 5, 3, 4]: # Get protential destination destination = self._machine.xy_over_link(x, y, link_id) # If it is useful if destination in to_reach: # check it actually exits if self._machine.is_link_at(x, y, link_id): # Add to tree and record chip reachable tree[destination] = (x, y, link_id) to_reach.remove(destination) found.add(destination) if len(found) == 0: return None return tree def _logging_retry(self, ethernet_chip): eth_x = ethernet_chip.x eth_y = ethernet_chip.y tree = dict() to_reach = set( self._machine.get_existing_xys_by_ethernet(eth_x, eth_y)) reached = set() reached.add((eth_x, eth_y)) to_reach.remove((eth_x, eth_y)) found = set() found.add((eth_x, eth_y)) logger.warning("In _logging_retry") for x, y in to_reach: logger.warning("Still need to reach {}:{}", x, y) while len(to_reach) > 0: just_reached = found found = set() for x, y in just_reached: logger.warning("Trying from {}:{}", x, y) # Check links starting with the most direct from 0,0 for link_id in [1, 0, 2, 5, 3, 4]: # Get protential destination destination = self._machine.xy_over_link(x, y, link_id) # If it is useful if destination in to_reach: logger.warning( "Could reach {} over {}", destination, link_id) # check it actually exits if self._machine.is_link_at(x, y, link_id): # Add to tree and record chip reachable tree[destination] = (x, y, link_id) to_reach.remove(destination) found.add(destination) else: logger.error("Link down") logger.warning("Found {}", len(found)) if len(found) == 0: raise PacmanRoutingException( "Unable to do data in routing on " f"{ethernet_chip.ip_address}.") return tree def _add_routing_entry(self, x, y, key, processor_id=None, link_ids=None): """ Adds a routing entry on this chip, creating the table if needed. :param int x: chip.x :param int y: chip.y :param int key: The key to use :param int processor_id: placement.p of the monitor vertex if applicable :param int link_id: If of the link out if applicable """ table = self._routing_tables.get_routing_table_for_chip(x, y) if table is None: table = UnCompressedMulticastRoutingTable(x, y) self._routing_tables.add_routing_table(table) if processor_id is None: processor_ids = [] else: processor_ids = [processor_id] if link_ids is None: link_ids = [] entry = MulticastRoutingEntry( routing_entry_key=key, mask=ROUTING_MASK, processor_ids=processor_ids, link_ids=link_ids, defaultable=False) table.add_multicast_routing_entry(entry) def _add_routing_entries(self, ethernet_chip, tree): """ Adds the routing entires based on the tree. For every chip with this ethernet: - A key is generated (and saved) for this chip. - A local route to the monitor core is added. - The tree is walked adding a route on each source to get here :param ~spinn_machine.Chip ethernet_chip: the ethernet chip to make entries for :param dict(tuple(int,int),tuple(int,int,int)) tree: map of chips and links """ eth_x = ethernet_chip.x eth_y = ethernet_chip.y key = KEY_START_VALUE for (x, y) in self._machine.get_existing_xys_by_ethernet( eth_x, eth_y): self._key_to_destination_map[x, y] = key placement = FecDataView.get_placement_of_vertex( FecDataView.get_monitor_by_xy(x, y)) self._add_routing_entry(x, y, key, processor_id=placement.p) while (x, y) in tree: x, y, link = tree[(x, y)] self._add_routing_entry(x, y, key, link_ids=[link]) key += N_KEYS_PER_PARTITION_ID # accum links to make a broad cast links_per_chip = defaultdict(list) for chip_key in tree: x, y, link = tree[chip_key] links_per_chip[x, y].append(link) # add broadcast router timeout keys time_out_key = key for (x, y) in self._machine.get_existing_xys_by_ethernet( eth_x, eth_y): placement = FecDataView.get_placement_of_vertex( FecDataView.get_monitor_by_xy(x, y)) self._add_routing_entry( x, y, time_out_key, processor_id=placement.p, link_ids=links_per_chip[x, y]) # update tracker self._time_out_keys_by_board[(eth_x, eth_y)] = key