Source code for spinnman.processes.get_machine_process

# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
import functools
from spinn_utilities.log import FormatAdapter
from spinn_machine import (
    Processor, Router, Chip, SDRAM, Link, machine_from_size)
from spinn_machine.machine_factory import machine_repair
from spinnman.constants import ROUTER_REGISTER_P2P_ADDRESS
from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException
from spinnman.messages.scp.impl import ReadMemory, ReadLink, GetChipInfo
from spinnman.model import P2PTable
from spinnman.model.enums import CPUState
from .abstract_multi_connection_process import AbstractMultiConnectionProcess

logger = FormatAdapter(logging.getLogger(__name__))


[docs]class GetMachineProcess(AbstractMultiConnectionProcess): """ A process for getting the machine details over a set of connections. """ __slots__ = [ "_chip_info", "_ignore_chips", "_ignore_cores", "_ignore_links", "_max_core_id", "_max_sdram_size", "_p2p_column_data"] def __init__(self, connection_selector, ignore_chips, ignore_cores, ignore_links, max_core_id, max_sdram_size=None): # pylint: disable=too-many-arguments super(GetMachineProcess, self).__init__(connection_selector) self._ignore_chips = ignore_chips if ignore_chips is not None else {} self._ignore_cores = ignore_cores if ignore_cores is not None else {} self._ignore_links = ignore_links if ignore_links is not None else {} self._max_core_id = max_core_id self._max_sdram_size = max_sdram_size self._p2p_column_data = list() # A dictionary of (x, y) -> ChipInfo self._chip_info = dict() def _make_chip(self, chip_info, machine): """ Creates a chip from a ChipSummaryInfo structure. :param chip_info: \ The ChipSummaryInfo structure to create the chip from :type chip_info: \ :py:class:`spinnman.model.ChipSummaryInfo` :return: The created chip :rtype: :py:class:`spinn_machine.Chip` """ # Create the processor list processors = list() max_core_id = chip_info.n_cores - 1 core_states = chip_info.core_states if self._max_core_id is not None and max_core_id > self._max_core_id: max_core_id = self._max_core_id for virtual_core_id in range(max_core_id + 1): # Add the core provided it is not to be ignored if ((chip_info.x, chip_info.y, virtual_core_id) not in self._ignore_cores): if virtual_core_id == 0: processors.append(Processor.factory( virtual_core_id, is_monitor=True)) elif core_states[virtual_core_id] == CPUState.IDLE: processors.append(Processor.factory(virtual_core_id)) else: logger.warning( "Not using core {}, {}, {} in state {}", chip_info.x, chip_info.y, virtual_core_id, core_states[virtual_core_id]) # Create the router router = self._make_router(chip_info, machine) # Create the chip's SDRAM object sdram_size = chip_info.largest_free_sdram_block if (self._max_sdram_size is not None and sdram_size > self._max_sdram_size): sdram_size = self._max_sdram_size sdram = SDRAM(size=sdram_size) # Create the chip return Chip( x=chip_info.x, y=chip_info.y, processors=processors, router=router, sdram=sdram, ip_address=chip_info.ethernet_ip_address, nearest_ethernet_x=chip_info.nearest_ethernet_x, nearest_ethernet_y=chip_info.nearest_ethernet_y) def _make_router(self, chip_info, machine): links = list() for link in chip_info.working_links: dest = machine.xy_over_link(chip_info.x, chip_info.y, link) if ((chip_info.x, chip_info.y, link) not in self._ignore_links and dest not in self._ignore_chips and dest in self._chip_info): dest_x, dest_y = dest links.append(Link( chip_info.x, chip_info.y, link, dest_x, dest_y)) return Router( links=links, n_available_multicast_entries=( chip_info.n_free_multicast_routing_entries)) def _receive_p2p_data(self, column, scp_read_response): self._p2p_column_data[column] = ( scp_read_response.data, scp_read_response.offset) def _receive_chip_info(self, scp_read_chip_info_response): chip_info = scp_read_chip_info_response.chip_info self._chip_info[chip_info.x, chip_info.y] = chip_info def _receive_error(self, request, exception, tb): # If we get an ReadLink with a # SpinnmanUnexpectedResponseCodeException, this is a failed link # and so can be ignored if isinstance(request, ReadLink): if isinstance(exception, SpinnmanUnexpectedResponseCodeException): return super(GetMachineProcess, self)._receive_error(request, exception, tb)
[docs] def get_machine_details(self, boot_x, boot_y, width, height, repair_machine, ignore_bad_ethernets): # Get the P2P table - 8 entries are packed into each 32-bit word p2p_column_bytes = P2PTable.get_n_column_bytes(height) self._p2p_column_data = [None] * width for column in range(width): offset = P2PTable.get_column_offset(column) self._send_request( ReadMemory( x=boot_x, y=boot_y, base_address=(ROUTER_REGISTER_P2P_ADDRESS + offset), size=p2p_column_bytes), functools.partial(self._receive_p2p_data, column)) self._finish() self.check_for_error() p2p_table = P2PTable(width, height, self._p2p_column_data) # Get the chip information for each chip for (x, y) in p2p_table.iterchips(): self._send_request(GetChipInfo(x, y), self._receive_chip_info) self._finish() try: self.check_for_error() except Exception: # pylint: disable=broad-except # Ignore errors so far, as any error here just means that a chip # is down that wasn't marked as down pass # Warn about unexpected missing chips for (x, y) in p2p_table.iterchips(): if (x, y) not in self._chip_info: logger.warning( "Chip {}, {} was expected but didn't reply", x, y) return self.create_machine( width, height, repair_machine, ignore_bad_ethernets)
[docs] def create_machine( self, width, height, repair_machine, ignore_bad_ethernets): bad_ethernets = [] machine = machine_from_size(width, height) for chip_info in sorted( self._chip_info.values(), key=lambda chip: (chip.x, chip.y)): if (chip_info.x, chip_info.y) not in self._ignore_chips: if (chip_info.ethernet_ip_address is not None and (chip_info.x != chip_info.nearest_ethernet_x or chip_info.y != chip_info.nearest_ethernet_y)): bad_ethernets.append( (chip_info, chip_info.ethernet_ip_address)) if ignore_bad_ethernets: # pylint: disable=protected-access chip_info._ethernet_ip_address = None machine.add_chip( self._make_chip(chip_info, machine)) else: machine.add_chip(self._make_chip(chip_info, machine)) removed_chips = [] if len(bad_ethernets) > 0: msg = "" for chip_info, claim in bad_ethernets: chip = self._make_chip(chip_info, machine) local_x, local_y = machine.get_local_xy(chip) ethernet = machine.get_chip_at( chip.nearest_ethernet_x, chip.nearest_ethernet_y) msg += "x:{} y:{} on board:{} claims it has ethernet {} " \ "".format(local_x, local_y, ethernet.ip_address, claim) removed_chips.append((chip.x, chip.y)) logger.warning(msg) if ignore_bad_ethernets: # No chips actually removed removed_chips = [] machine.validate() return machine_repair(machine, repair_machine, removed_chips)
[docs] def get_chip_info(self): """ Get the chip information for the machine. .. note:: :py:meth:`get_machine_details` must have been called first. """ return self._chip_info