Source code for spinn_front_end_common.interface.interface_functions.host_no_bitfield_router_compression

# 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.

import logging
import struct
from typing import List
from spinn_utilities.config_holder import get_config_bool
from spinn_utilities.log import FormatAdapter
from spinn_utilities.progress_bar import ProgressBar
from spinn_utilities.typing.coords import XY
from spinn_machine import CoreSubsets, Router
from spinnman.model import ExecutableTargets
from spinnman.model.enums import CPUState, ExecutableType, UserRegister
from pacman.model.routing_tables import AbstractMulticastRoutingTable
from spinn_front_end_common.data import FecDataView
from spinn_front_end_common.utilities.exceptions import SpinnFrontEndException
from spinn_front_end_common.utilities.system_control_logic import (
    run_system_application)
from spinn_front_end_common.utilities.helpful_functions import (
    get_defaultable_source_id)
from spinn_front_end_common.utilities.constants import COMPRESSOR_SDRAM_TAG

_FOUR_WORDS = struct.Struct("<IIII")
_THREE_WORDS = struct.Struct("<III")
logger = FormatAdapter(logging.getLogger(__name__))


[docs] def pair_compression() -> None: """ Load routing tables and compress then using the Pair Algorithm. See ``pacman/operations/router_compressors/pair_compressor.py`` which is the exact same algorithm implemented in Python. :raises SpinnFrontEndException: If compression fails """ compression = Compression( FecDataView.get_executable_path("simple_pair_compressor.aplx"), "Running pair routing table compression on chip", result_register=UserRegister.USER_1) compression.compress()
[docs] def ordered_covering_compression() -> None: """ Load routing tables and compress then using the unordered Algorithm. To the best of our knowledge this is the same algorithm as :py:func:`mundy_on_chip_router_compression`, except this one is still buildable and can be maintained. :raises SpinnFrontEndException: If compression fails """ compression = Compression( FecDataView.get_executable_path("simple_unordered_compressor.aplx"), "Running unordered routing table compression on chip", result_register=UserRegister.USER_1) compression.compress()
[docs] class Compression(object): """ Compression algorithm implementation that uses a on-chip router compressor in order to parallelise. """ __slots__ = ( "_binary_path", "_compress_as_much_as_possible", "_compress_only_when_needed", "_compressor_app_id", "_progresses_text", "__result_register", "_routing_tables", "__failures") def __init__( self, binary_path: str, progress_text: str, result_register: UserRegister): """ :param str binary_path: What binary to run :param ~spinn_machine.Machine machine: The machine model :param str progress_text: Text to use in progress bar :param int result_register: number of the user register to check for the result code """ self._binary_path = binary_path self._compress_as_much_as_possible = get_config_bool( "Mapping", "router_table_compress_as_far_as_possible") # Only used by Mundy compressor we can not rebuild self._compress_only_when_needed = None self._routing_tables = FecDataView.get_precompressed() self._progresses_text = progress_text self._compressor_app_id = -1 self.__failures: List[XY] = [] self.__result_register = result_register
[docs] def compress(self) -> None: """ Apply the on-machine compression algorithm. :raises SpinnFrontEndException: If compression fails """ # build progress bar progress_bar = ProgressBar( len(self._routing_tables.routing_tables) * 2, self._progresses_text) if len(self._routing_tables.routing_tables) == 0: progress_bar.end() return self._compressor_app_id = FecDataView().get_new_id() # figure size of SDRAM needed for each chip for storing the routing # table for routing_table in progress_bar.over(self._routing_tables, False): self._load_routing_table(routing_table) # load the router compressor executable executable_targets = self._load_executables() run_system_application( executable_targets, self._compressor_app_id, get_config_bool("Reports", "write_compressor_iobuf") or False, self._check_for_success, frozenset([CPUState.FINISHED]), False, "compressor_on_{}_{}_{}.txt", [self._binary_path], progress_bar) if self.__failures: raise SpinnFrontEndException( f"The router compressor failed on {self.__failures}")
def _load_routing_table(self, table: AbstractMulticastRoutingTable): """ :param pacman.model.routing_tables.AbstractMulticastRoutingTable table: the router table to load """ transceiver = FecDataView.get_transceiver() data = self._build_data(table) # go to spinnman and ask for a memory region of that size per chip. base_address = transceiver.malloc_sdram( table.x, table.y, len(data), self._compressor_app_id, COMPRESSOR_SDRAM_TAG) # write SDRAM requirements per chip transceiver.write_memory(table.x, table.y, base_address, data) def _check_for_success( self, executable_targets: ExecutableTargets) -> bool: """ Goes through the cores checking for cores that have failed to compress the routing tables to the level where they fit into the router. :param ~spinnman.model.ExecutableTargets executable_targets: """ transceiver = FecDataView.get_transceiver() for core_subset in executable_targets.all_core_subsets: x, y = core_subset.x, core_subset.y for p in core_subset.processor_ids: # Read the result from specified register result = transceiver.read_user(x, y, p, self.__result_register) # The result is 0 if success, otherwise failure if result != 0: self.__failures.append((x, y)) return len(self.__failures) == 0 def _load_executables(self) -> ExecutableTargets: """ Plans the loading of the router compressor onto the chips. :return: the executable targets that represent all cores/chips which have active routing tables :rtype: ~spinnman.model.ExecutableTargets """ # build core subsets core_subsets = CoreSubsets() for routing_table in self._routing_tables.routing_tables: # add to the core subsets core_subsets.add_processor( routing_table.x, routing_table.y, routing_table.chip.placable_processors_ids[0]) # build executable targets executable_targets = ExecutableTargets() executable_targets.add_subsets(self._binary_path, core_subsets, ExecutableType.SYSTEM) return executable_targets def _build_data(self, table: AbstractMulticastRoutingTable) -> bytes: """ Convert the router table into the data needed by the router compressor C code. :param table: the PACMAN router table instance :type table: ~pacman.model.routing_tables.AbstractMulticastRoutingTable :return: The byte array of data :rtype: bytearray """ # write header data of the app ID to load the data, if to store # results in SDRAM and the router table entries data = b'' if self._compress_only_when_needed is None: data += _THREE_WORDS.pack( FecDataView.get_app_id(), int(self._compress_as_much_as_possible or False), # Write the size of the table table.number_of_entries) else: # Mundy compressor can not be changed so uses it own structure data += _FOUR_WORDS.pack( FecDataView.get_app_id(), int(self._compress_only_when_needed), int(self._compress_as_much_as_possible), # Write the size of the table table.number_of_entries) for entry in table.multicast_routing_entries: data += _FOUR_WORDS.pack( entry.key, entry.mask, Router.convert_routing_table_entry_to_spinnaker_route(entry), get_defaultable_source_id(entry)) return bytearray(data)