From 3ced2f5a9632e213a11916c6d3bf7aca1e44d494 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 1 Aug 2023 16:25:15 +0100 Subject: [PATCH 01/39] move Transceiver and create_transceiver_from_hostname --- spinnman/data/spinnman_data_writer.py | 2 +- spinnman/extended/extended_transceiver.py | 5 +- spinnman/spalloc/spalloc_client.py | 2 +- spinnman/spalloc/spalloc_job.py | 2 +- spinnman/transceiver/__init__.py | 18 +++++ spinnman/{ => transceiver}/transceiver.py | 0 spinnman/transceiver/transceiver_factory.py | 90 +++++++++++++++++++++ unittests/data/test_data.py | 2 +- unittests/test_transceiver.py | 2 +- unittests/test_version.py | 2 +- 10 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 spinnman/transceiver/__init__.py rename spinnman/{ => transceiver}/transceiver.py (100%) create mode 100644 spinnman/transceiver/transceiver_factory.py diff --git a/spinnman/data/spinnman_data_writer.py b/spinnman/data/spinnman_data_writer.py index 2136f6247..f526ff051 100644 --- a/spinnman/data/spinnman_data_writer.py +++ b/spinnman/data/spinnman_data_writer.py @@ -16,7 +16,7 @@ from spinn_utilities.log import FormatAdapter from spinn_utilities.overrides import overrides from spinn_machine.data.machine_data_writer import MachineDataWriter -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from .spinnman_data_view import _SpiNNManDataModel, SpiNNManDataView logger = FormatAdapter(logging.getLogger(__name__)) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index f2120267f..b6f118621 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -23,7 +23,7 @@ from spinn_utilities.logger_utils import warn_once from spinn_machine import CoreSubsets from spinnman.constants import SYSTEM_VARIABLE_BASE_ADDRESS -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman.constants import ( ROUTER_REGISTER_BASE_ADDRESS, ROUTER_FILTER_CONTROLS_OFFSET, ROUTER_DIAGNOSTIC_FILTER_SIZE) @@ -42,7 +42,8 @@ from spinnman.processes import ( GetHeapProcess, ReadMemoryProcess, SendSingleCommandProcess, WriteMemoryProcess) -from spinnman.transceiver import _EXECUTABLE_ADDRESS, _ONE_BYTE, _ONE_WORD +from spinnman.transceiver.transceiver import ( + _EXECUTABLE_ADDRESS, _ONE_BYTE, _ONE_WORD) from spinnman.utilities.utility_functions import ( work_out_bmp_from_machine_details) diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index cd2758d4a..945cb27bd 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -35,7 +35,7 @@ from spinnman.constants import SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT from spinnman.exceptions import SpinnmanTimeoutException from spinnman.exceptions import SpallocException -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from .spalloc_state import SpallocState from .proxy_protocol import ProxyProtocol from .session import Session, SessionAware diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py index 3ed3edd16..81035a5ed 100644 --- a/spinnman/spalloc/spalloc_job.py +++ b/spinnman/spalloc/spalloc_job.py @@ -17,7 +17,7 @@ from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.constants import SCP_SCAMP_PORT -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman.connections.udp_packet_connections import UDPConnection from .spalloc_state import SpallocState from .spalloc_boot_connection import SpallocBootConnection diff --git a/spinnman/transceiver/__init__.py b/spinnman/transceiver/__init__.py new file mode 100644 index 000000000..899e31121 --- /dev/null +++ b/spinnman/transceiver/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 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 spinnman.transceiver.transceiver_factory import ( + create_transceiver_from_hostname) + +__all__ = ["create_transceiver_from_hostname"] diff --git a/spinnman/transceiver.py b/spinnman/transceiver/transceiver.py similarity index 100% rename from spinnman/transceiver.py rename to spinnman/transceiver/transceiver.py diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py new file mode 100644 index 000000000..1078b2d58 --- /dev/null +++ b/spinnman/transceiver/transceiver_factory.py @@ -0,0 +1,90 @@ +# Copyright (c) 2023 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 +from spinn_utilities.log import FormatAdapter +from spinnman.utilities.utility_functions import ( + work_out_bmp_from_machine_details) +from spinnman.connections.udp_packet_connections import ( + BMPConnection, BootConnection, SCAMPConnection) +from spinnman.transceiver.transceiver import Transceiver + +logger = FormatAdapter(logging.getLogger(__name__)) + + +def create_transceiver_from_hostname( + hostname, version, bmp_connection_data=None, number_of_boards=None, + auto_detect_bmp=False): + """ + Create a Transceiver by creating a :py:class:`~.UDPConnection` to the + given hostname on port 17893 (the default SCAMP port), and a + :py:class:`~.BootConnection` on port 54321 (the default boot port), + optionally discovering any additional links using the UDPConnection, + and then returning the transceiver created with the conjunction of + the created UDPConnection and the discovered connections. + + :param hostname: The hostname or IP address of the board or `None` if + only the BMP connections are of interest + :type hostname: str or None + :param number_of_boards: a number of boards expected to be supported, or + ``None``, which defaults to a single board + :type number_of_boards: int or None + :param int version: the type of SpiNNaker board used within the SpiNNaker + machine being used. If a Spinn-5 board, then the version will be 5, + Spinn-3 would equal 3 and so on. + :param list(BMPConnectionData) bmp_connection_data: + the details of the BMP connections used to boot multi-board systems + :param bool auto_detect_bmp: + ``True`` if the BMP of version 4 or 5 boards should be + automatically determined from the board IP address + :param scamp_connections: + the list of connections used for SCAMP communications + :return: The created transceiver + :rtype: Transceiver + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + if hostname is not None: + logger.info("Creating transceiver for {}", hostname) + connections = list() + + # if no BMP has been supplied, but the board is a spinn4 or a spinn5 + # machine, then an assumption can be made that the BMP is at -1 on the + # final value of the IP address + if (version >= 4 and auto_detect_bmp is True and + (bmp_connection_data is None or not bmp_connection_data)): + bmp_connection_data = [ + work_out_bmp_from_machine_details(hostname, number_of_boards)] + + # handle BMP connections + if bmp_connection_data is not None: + bmp_ip_list = list() + for conn_data in bmp_connection_data: + bmp_connection = BMPConnection(conn_data) + connections.append(bmp_connection) + bmp_ip_list.append(bmp_connection.remote_ip_address) + logger.info("Transceiver using BMPs: {}", bmp_ip_list) + + connections.append(SCAMPConnection(remote_host=hostname)) + + # handle the boot connection + connections.append(BootConnection(remote_host=hostname)) + + return Transceiver(version, connections=connections) diff --git a/unittests/data/test_data.py b/unittests/data/test_data.py index ab4fa0652..ffa00f898 100644 --- a/unittests/data/test_data.py +++ b/unittests/data/test_data.py @@ -18,7 +18,7 @@ from spinnman.config_setup import unittest_setup from spinnman.data import SpiNNManDataView from spinnman.data.spinnman_data_writer import SpiNNManDataWriter -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver class MockTranceiver(Transceiver): diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 52c31486c..63b65964a 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -19,7 +19,7 @@ from spinnman.config_setup import unittest_setup from spinnman.data.spinnman_data_writer import SpiNNManDataWriter from spinnman.extended.extended_transceiver import ExtendedTransceiver -from spinnman.transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( SystemVariableDefinition) diff --git a/unittests/test_version.py b/unittests/test_version.py index c8f42fc87..eec3cdc8c 100644 --- a/unittests/test_version.py +++ b/unittests/test_version.py @@ -15,7 +15,7 @@ import unittest import spinn_utilities import spinn_machine -from spinnman.transceiver import Transceiver, _SCAMP_VERSION +from spinnman.transceiver.transceiver import Transceiver, _SCAMP_VERSION import spinnman from spinnman.config_setup import unittest_setup From e02a34320d3ae4d4ff80cc4f2073ffb4fd19c86b Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 2 Aug 2023 07:07:02 +0100 Subject: [PATCH 02/39] remove create_transceiver_from_hostname --- spinnman/transceiver/transceiver.py | 65 ----------------------------- 1 file changed, 65 deletions(-) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 5f58a9b7c..b1a62f6a9 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -89,71 +89,6 @@ _EXECUTABLE_ADDRESS = 0x67800000 -def create_transceiver_from_hostname( - hostname, version, bmp_connection_data=None, number_of_boards=None, - auto_detect_bmp=False): - """ - Create a Transceiver by creating a :py:class:`~.UDPConnection` to the - given hostname on port 17893 (the default SCAMP port), and a - :py:class:`~.BootConnection` on port 54321 (the default boot port), - optionally discovering any additional links using the UDPConnection, - and then returning the transceiver created with the conjunction of - the created UDPConnection and the discovered connections. - - :param hostname: The hostname or IP address of the board or `None` if - only the BMP connections are of interest - :type hostname: str or None - :param number_of_boards: a number of boards expected to be supported, or - ``None``, which defaults to a single board - :type number_of_boards: int or None - :param int version: the type of SpiNNaker board used within the SpiNNaker - machine being used. If a Spinn-5 board, then the version will be 5, - Spinn-3 would equal 3 and so on. - :param BMPConnectionData bmp_connection_data: - the details of the BMP connections used to boot multi-board systems - :param bool auto_detect_bmp: - ``True`` if the BMP of version 4 or 5 boards should be - automatically determined from the board IP address - :param scamp_connections: - the list of connections used for SCAMP communications - :return: The created transceiver - :rtype: Transceiver - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - if hostname is not None: - logger.info("Creating transceiver for {}", hostname) - connections = list() - - # if no BMP has been supplied, but the board is a spinn4 or a spinn5 - # machine, then an assumption can be made that the BMP is at -1 on the - # final value of the IP address - if (version >= 4 and auto_detect_bmp is True and - (bmp_connection_data is None or not bmp_connection_data)): - bmp_connection_data = [ - work_out_bmp_from_machine_details(hostname, number_of_boards)] - - # handle BMP connections - if bmp_connection_data is not None: - bmp_connection = BMPConnection(bmp_connection_data) - connections.append(bmp_connection) - logger.info("Transceiver using BMP: {}", - bmp_connection.remote_ip_address) - - connections.append(SCAMPConnection(remote_host=hostname)) - - # handle the boot connection - connections.append(BootConnection(remote_host=hostname)) - - return Transceiver(version, connections=connections) - - class Transceiver(AbstractContextManager): """ An encapsulation of various communications with the SpiNNaker board. From deea76189ba89da9dad2193916a13cb3201cc005 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 2 Aug 2023 10:47:02 +0100 Subject: [PATCH 03/39] Abstract, BAse Version5 and Mockable Transceivers --- spinnman/data/spinnman_data_writer.py | 10 +- spinnman/extended/extended_transceiver.py | 70 +- spinnman/spalloc/spalloc_client.py | 6 +- spinnman/spalloc/spalloc_job.py | 4 +- spinnman/transceiver/__init__.py | 8 +- spinnman/transceiver/abstract_transceiver.py | 877 ++++++++++++++++++ .../{transceiver.py => base_transceiver.py} | 7 +- spinnman/transceiver/mockable_transceiver.py | 217 +++++ spinnman/transceiver/transceiver_factory.py | 4 +- spinnman/transceiver/version5Transceiver.py | 22 + spinnman/transceiver/watchdog_setter.py | 85 ++ unittests/data/test_data.py | 12 +- unittests/test_transceiver.py | 44 +- unittests/test_version.py | 17 +- 14 files changed, 1249 insertions(+), 134 deletions(-) create mode 100644 spinnman/transceiver/abstract_transceiver.py rename spinnman/transceiver/{transceiver.py => base_transceiver.py} (99%) create mode 100644 spinnman/transceiver/mockable_transceiver.py create mode 100644 spinnman/transceiver/version5Transceiver.py create mode 100644 spinnman/transceiver/watchdog_setter.py diff --git a/spinnman/data/spinnman_data_writer.py b/spinnman/data/spinnman_data_writer.py index f526ff051..ef76cfa1d 100644 --- a/spinnman/data/spinnman_data_writer.py +++ b/spinnman/data/spinnman_data_writer.py @@ -16,7 +16,7 @@ from spinn_utilities.log import FormatAdapter from spinn_utilities.overrides import overrides from spinn_machine.data.machine_data_writer import MachineDataWriter -from spinnman.transceiver.transceiver import Transceiver +from spinnman.transceiver import AbstractTransceiver from .spinnman_data_view import _SpiNNManDataModel, SpiNNManDataView logger = FormatAdapter(logging.getLogger(__name__)) @@ -98,11 +98,11 @@ def set_transceiver(self, transceiver): """ Sets the transceiver object. - :param Transceiver transceiver: - :raises TypeError: If the transceiver is not a Transceiver + :param AbstractTransceiver transceiver: + :raises TypeError: If the transceiver is not a AbstractTransceiver """ - if not isinstance(transceiver, Transceiver): - raise TypeError("transceiver should be a Transceiver") + if not isinstance(transceiver, AbstractTransceiver): + raise TypeError("transceiver should be a AbstractTransceiver") if self.__data._transceiver: raise NotImplementedError( "Over writing and existing transceiver not supported") diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index fe7439b8c..5134bb269 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -22,12 +22,9 @@ from spinn_utilities.log import FormatAdapter from spinn_utilities.logger_utils import warn_once from spinn_machine import CoreSubsets -from spinnman.constants import SYSTEM_VARIABLE_BASE_ADDRESS -from spinnman.transceiver.transceiver import Transceiver from spinnman.constants import ( ROUTER_REGISTER_BASE_ADDRESS, ROUTER_FILTER_CONTROLS_OFFSET, ROUTER_DIAGNOSTIC_FILTER_SIZE) -from spinnman.data import SpiNNManDataView from spinnman.exceptions import SpinnmanException from spinnman.extended import ( BMPSetLed, DeAllocSDRAMProcess, ReadADC, SetLED, WriteMemoryFloodProcess) @@ -42,8 +39,8 @@ from spinnman.processes import ( GetHeapProcess, ReadMemoryProcess, SendSingleCommandProcess, WriteMemoryProcess) -from spinnman.transceiver.transceiver import ( - _EXECUTABLE_ADDRESS, _ONE_BYTE, _ONE_WORD) +from spinnman.transceiver.version5Transceiver import Version5Transceiver +from spinnman.transceiver.watchdog_setter import WatchdogSetter from spinnman.utilities.utility_functions import ( work_out_bmp_from_machine_details) @@ -117,7 +114,7 @@ def create_transceiver_from_hostname( return ExtendedTransceiver(version, connections=connections) -class ExtendedTransceiver(Transceiver): +class ExtendedTransceiver(Version5Transceiver, WatchdogSetter): """ An encapsulation of various communications with the SpiNNaker board. @@ -215,59 +212,6 @@ def is_connected(self, connection=None): return connection.is_connected() return any(c.is_connected() for c in self._scamp_connections) - def __set_watch_dog_on_chip(self, x, y, watch_dog): - """ - Enable, disable or set the value of the watch dog timer on a - specific chip. - - .. warning:: - This method is currently deprecated and untested as there is no - known use. Same functionality provided by ybug and bmpc. - Retained in case needed for hardware debugging. - - :param int x: chip X coordinate to write new watchdog parameter to - :param int y: chip Y coordinate to write new watchdog parameter to - :param watch_dog: - Either a boolean indicating whether to enable (True) or - disable (False) the watchdog timer, or an int value to set the - timer count to - :type watch_dog: bool or int - """ - # build what we expect it to be - warn_once(logger, "The set_watch_dog_on_chip method is deprecated " - "and untested due to no known use.") - value_to_set = watch_dog - watchdog = SystemVariableDefinition.software_watchdog_count - if isinstance(watch_dog, bool): - value_to_set = watchdog.default if watch_dog else 0 - - # build data holder - data = _ONE_BYTE.pack(value_to_set) - - # write data - address = SYSTEM_VARIABLE_BASE_ADDRESS + watchdog.offset - self.write_memory(x=x, y=y, base_address=address, data=data) - - def set_watch_dog(self, watch_dog): - """ - Enable, disable or set the value of the watch dog timer. - - .. warning:: - This method is currently deprecated and untested as there is no - known use. Same functionality provided by ybug and bmpc. - Retained in case needed for hardware debugging. - - :param watch_dog: - Either a boolean indicating whether to enable (True) or - disable (False) the watch dog timer, or an int value to set the - timer count to. - :type watch_dog: bool or int - """ - warn_once(logger, "The set_watch_dog method is deprecated and " - "untested due to no known use.") - for x, y in SpiNNManDataView.get_machine().chip_coordinates: - self.__set_watch_dog_on_chip(x, y, watch_dog) - def get_iobuf_from_core(self, x, y, p): """ Get the contents of IOBUF for a given core. @@ -383,7 +327,7 @@ def execute( with self._chip_execute_lock(x, y): # Write the executable self.write_memory( - x, y, _EXECUTABLE_ADDRESS, executable, n_bytes, + x, y, self._EXECUTABLE_ADDRESS, executable, n_bytes, is_filename=is_filename) # Request the start of the executable @@ -530,7 +474,7 @@ def write_neighbour_memory(self, x, y, link, base_address, data, process.write_link_memory_from_reader( x, y, cpu, link, base_address, data, n_bytes) elif isinstance(data, int): - data_to_write = _ONE_WORD.pack(data) + data_to_write = self._ONE_WORD.pack(data) process.write_link_memory_from_bytearray( x, y, cpu, link, base_address, data_to_write, 0, 4) else: @@ -647,7 +591,7 @@ def write_memory_flood( process.write_memory_from_reader( nearest_neighbour_id, base_address, reader, n_bytes) elif isinstance(data, int): - data_to_write = _ONE_WORD.pack(data) + data_to_write = self._ONE_WORD.pack(data) process.write_memory_from_bytearray( nearest_neighbour_id, base_address, data_to_write, 0) else: @@ -767,7 +711,7 @@ def get_router_diagnostic_filter(self, x, y, position): process = SendSingleCommandProcess( self._scamp_connection_selector) response = process.execute(ReadMemory(x, y, memory_position, 4)) - return DiagnosticFilter.read_from_int(_ONE_WORD.unpack_from( + return DiagnosticFilter.read_from_int(self._ONE_WORD.unpack_from( response.data, response.offset)[0]) # pylint: disable=no-member except Exception: diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index 945cb27bd..e7aaf9ae5 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -35,7 +35,7 @@ from spinnman.constants import SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT from spinnman.exceptions import SpinnmanTimeoutException from spinnman.exceptions import SpallocException -from spinnman.transceiver.transceiver import Transceiver +from spinnman.transceiver.version5Transceiver import Version5Transceiver from .spalloc_state import SpallocState from .proxy_protocol import ProxyProtocol from .session import Session, SessionAware @@ -635,14 +635,14 @@ def _keepalive_handle(self, handle): self.__keepalive_handle = handle @overrides(SpallocJob.create_transceiver) - def create_transceiver(self) -> Transceiver: + def create_transceiver(self) -> Version5Transceiver: if self.get_state() != SpallocState.READY: raise SpallocException("job not ready to execute scripts") proxies = [ self.connect_to_board(x, y) for (x, y) in self.get_connections()] # Also need a boot connection proxies.append(self.connect_for_booting()) - return Transceiver(version=5, connections=proxies) + return Version5Transceiver(version=5, connections=proxies) def __repr__(self): return f"SpallocJob({self._url})" diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py index 81035a5ed..9cab74d4b 100644 --- a/spinnman/spalloc/spalloc_job.py +++ b/spinnman/spalloc/spalloc_job.py @@ -17,7 +17,7 @@ from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.constants import SCP_SCAMP_PORT -from spinnman.transceiver.transceiver import Transceiver +from spinnman.transceiver.version5Transceiver import Version5Transceiver from spinnman.connections.udp_packet_connections import UDPConnection from .spalloc_state import SpallocState from .spalloc_boot_connection import SpallocBootConnection @@ -121,7 +121,7 @@ def open_udp_listener_connection(self) -> UDPConnection: """ @abstractmethod - def create_transceiver(self) -> Transceiver: + def create_transceiver(self) -> Version5Transceiver: """ Create a transceiver that will talk to this job. The transceiver will only be configured to talk to the SCP ports of the boards of the job. diff --git a/spinnman/transceiver/__init__.py b/spinnman/transceiver/__init__.py index 899e31121..c5d606a4c 100644 --- a/spinnman/transceiver/__init__.py +++ b/spinnman/transceiver/__init__.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from spinnman.transceiver.transceiver_factory import ( - create_transceiver_from_hostname) +from .abstract_transceiver import AbstractTransceiver +from .mockable_transceiver import MockableTransceiver +from .transceiver_factory import (create_transceiver_from_hostname) -__all__ = ["create_transceiver_from_hostname"] +__all__ = ["AbstractTransceiver", "create_transceiver_from_hostname", + "MockableTransceiver"] diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/abstract_transceiver.py new file mode 100644 index 000000000..b4173dc6d --- /dev/null +++ b/spinnman/transceiver/abstract_transceiver.py @@ -0,0 +1,877 @@ +# Copyright (c) 2014 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. + +# pylint: disable=too-many-arguments + +from spinn_utilities.abstract_base import (AbstractBase, abstractmethod) +from spinn_utilities.abstract_context_manager import AbstractContextManager +from spinnman.model.enums import CPUState + + +class AbstractTransceiver(AbstractContextManager, metaclass=AbstractBase): + """ + An encapsulation of various communications with the SpiNNaker board. + + The methods of this class are designed to be thread-safe (provided they do + not access a BMP, as access to those is never thread-safe); + thus you can make multiple calls to the same (or different) methods + from multiple threads and expect each call to work as if it had been + called sequentially, although the order of returns is not guaranteed. + + .. note:: + With multiple connections to the board, using multiple threads in this + way may result in an increase in the overall speed of operation, since + the multiple calls may be made separately over the set of given + connections. + """ + __slots__ = [] + + @abstractmethod + def send_sdp_message(self, message, connection=None): + """ + Sends an SDP message using one of the connections. + + :param SDPMessage message: The message to send + :param SDPConnection connection: An optional connection to use + """ + + @abstractmethod + def discover_scamp_connections(self): + """ + Find connections to the board and store these for future use. + + .. note:: + An exception will be thrown if no initial connections can be + found to the board. + + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def add_scamp_connections(self, connections): + """ + Check connections to the board and store these for future use. + + .. note:: + An exception will be thrown if no initial connections can be + found to the board. + + :param dict((int,int),str) connections: + Dict of (`x`,`y`) to IP address + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_machine_details(self): + """ + Get the details of the machine made up of chips on a board and how + they are connected to each other. + + :return: A machine description + :rtype: ~spinn_machine.Machine + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): + """ + Ensure that the board is ready to interact with this version of the + transceiver. Boots the board if not already booted and verifies that + the version of SCAMP running is compatible with this transceiver. + + :param number_of_boards: + this parameter is deprecated and will be ignored + :param int n_retries: The number of times to retry booting + :param dict(SystemVariableDefinition,object) extra_boot_values: + Any additional or overwrite values to set during boot. + This should only be used for values which are not standard + based on the board version. + :return: The version identifier + :rtype: VersionInfo + :raise SpinnmanIOException: + * If there is a problem booting the board + * If the version of software on the board is not compatible with + this transceiver + """ + + @abstractmethod + def get_cpu_infos( + self, core_subsets=None, states=None, include=True): + """ + Get information about the processors on the board. + + :param ~spinn_machine.CoreSubsets core_subsets: + A set of chips and cores from which to get the + information. If not specified, the information from all of the + cores on all of the chips on the board are obtained. + :param states: The state or states to filter on (if any) + :type states: None, CPUState or collection(CPUState) + :param bool include: + If True includes only infos in the requested state(s). + If False includes only infos NOT in the requested state(s). + Ignored if states is None. + :return: The CPU information for the selected cores and States, or + all cores/states if core_subsets/states is not specified + :rtype: ~spinnman.model.CPUInfos + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If chip_and_cores contains invalid items + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_clock_drift(self, x, y): + """ + Get the clock drift + :param int x: The x-coordinate of the chip to get drift for + :param int y: The y-coordinate of the chip to get drift for + """ + + @abstractmethod + def read_user(self, x, y, p, user): + """ + Get the contents of the this user register for the given processor. + + .. note:: + Conventionally, user_0 usually holds the address of the table of + memory regions. + + :param int x: X coordinate of the chip + :param int y: Y coordinate of the chip + :param int p: Virtual processor identifier on the chip + :param int user: The user number to read data for + :rtype: int + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If x, y, p does not identify a valid processor + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_cpu_information_from_core(self, x, y, p): + """ + Get information about a specific processor on the board. + + :param int x: The x-coordinate of the chip containing the processor + :param int y: The y-coordinate of the chip containing the processor + :param int p: The ID of the processor to get the information about + :return: The CPU information for the selected core + :rtype: CPUInfo + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y, p is not a valid processor + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_iobuf(self, core_subsets=None): + """ + Get the contents of the IOBUF buffer for a number of processors. + + :param ~spinn_machine.CoreSubsets core_subsets: + A set of chips and cores from which to get the buffers. If not + specified, the buffers from all of the cores on all of the chips + on the board are obtained. + :return: An iterable of the buffers, which may not be in the order + of core_subsets + :rtype: iterable(IOBuffer) + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If chip_and_cores contains invalid items + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_core_state_count(self, app_id, state): + """ + Get a count of the number of cores which have a given state. + + :param int app_id: + The ID of the application from which to get the count. + :param CPUState state: The state count to get + :return: A count of the cores with the given status + :rtype: int + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If state is not a valid status + * If app_id is not a valid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def execute_flood( + self, core_subsets, executable, app_id, n_bytes=None, wait=False, + is_filename=False): + """ + Start an executable running on multiple places on the board. This + will be optimised based on the selected cores, but it may still + require a number of communications with the board to execute. + + :param ~spinn_machine.CoreSubsets core_subsets: + Which cores on which chips to start the executable + :param executable: + The data that is to be executed. Should be one of the following: + + * An instance of RawIOBase + * A bytearray + * A filename of an executable (in which case `is_filename` must be + set to True) + :type executable: + ~io.RawIOBase or bytes or bytearray or str + :param int app_id: + The ID of the application with which to associate the executable + :param int n_bytes: + The size of the executable data in bytes. If not specified: + + * If `executable` is an RawIOBase, an error is raised + * If `executable` is a bytearray, the length of the bytearray will + be used + * If `executable` is an int, 4 will be used + * If `executable` is a str, the length of the file will be used + :param bool wait: + True if the processors should enter a "wait" state on loading + :param bool is_filename: True if the data is a filename + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the executable + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If one of the specified cores is not valid + * If `app_id` is an invalid application ID + * If a packet is received that has invalid parameters + * If `executable` is an RawIOBase but `n_bytes` is not specified + * If `executable` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def power_on(self, boards=0): + """ + Power on a set of boards in the machine. + + :param int boards: The board or boards to power on + """ + + @abstractmethod + def power_off_machine(self): + """ + Power off the whole machine. + + :rtype bool + :return success or failure to power off the machine + """ + + @abstractmethod + def power_off(self, boards=0): + """ + Power off a set of boards in the machine. + + :param int boards: The board or boards to power off + """ + + @abstractmethod + def read_fpga_register( + self, fpga_num, register, board=0): + """ + Read a register on a FPGA of a board. The meaning of the + register's contents will depend on the FPGA's configuration. + + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int register: + Register address to read to (will be rounded down to + the nearest 32-bit word boundary). + :param int board: which board to request the FPGA register from + :return: the register data + :rtype: int + """ + + @abstractmethod + def write_fpga_register(self, fpga_num, register, value, board=0): + """ + Write a register on a FPGA of a board. The meaning of setting the + register's contents will depend on the FPGA's configuration. + + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int register: + Register address to read to (will be rounded down to + the nearest 32-bit word boundary). + :param int value: the value to write into the FPGA register + :param int board: which board to write the FPGA register to + """ + + @abstractmethod + def read_bmp_version(self, board): + """ + Read the BMP version. + + :param int board: which board to request the data from + :return: the sver from the BMP + """ + + @abstractmethod + def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, + cpu=0, is_filename=False, get_sum=False): + """ + Write to the SDRAM on the board. + + :param int x: + The x-coordinate of the chip where the memory is to be written to + :param int y: + The y-coordinate of the chip where the memory is to be written to + :param int base_address: + The address in SDRAM where the region of memory is to be written + :param data: The data to write. Should be one of the following: + + * An instance of RawIOBase + * A bytearray/bytes + * A single integer - will be written in little-endian byte order + * A filename of a data file (in which case `is_filename` must be + set to True) + :type data: + ~io.RawIOBase or bytes or bytearray or int or str + :param int n_bytes: + The amount of data to be written in bytes. If not specified: + + * If `data` is an RawIOBase, an error is raised + * If `data` is a bytearray, the length of the bytearray will be + used + * If `data` is an int, 4 will be used + * If `data` is a str, the length of the file will be used + :param int offset: The offset from which the valid data begins + :param int cpu: The optional CPU to write to + :param bool is_filename: True if `data` is a filename + :param bool get_sum: whether to return a checksum or 0 + :return: The number of bytes written, the checksum (0 if get_sum=False) + :rtype: int, int + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the data + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If `x, y` does not lead to a valid chip + * If a packet is received that has invalid parameters + * If `base_address` is not a positive integer + * If `data` is an RawIOBase but `n_bytes` is not specified + * If `data` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def write_user(self, x, y, p, user, value): + """ + Write to the this user register for the given processor. + + .. note:: + Conventionally, user_0 usually holds the address of the table of + memory regions. + + :param int x: X coordinate of the chip + :param int y: Y coordinate of the chip + :param int p: Virtual processor identifier on the chip + :param int user: The user number of write data for + :param int value: The value to write + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If x, y, p does not identify a valid processor + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def read_memory(self, x, y, base_address, length, cpu=0): + """ + Read some areas of memory (usually SDRAM) from the board. + + :param int x: + The x-coordinate of the chip where the memory is to be read from + :param int y: + The y-coordinate of the chip where the memory is to be read from + :param int base_address: + The address in SDRAM where the region of memory to be read starts + :param int length: The length of the data to be read in bytes + :param int cpu: + the core ID used to read the memory of; should usually be 0 when + reading from SDRAM, but may be other values when reading from DTCM. + :return: A bytearray of data read + :rtype: bytes + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If one of `x`, `y`, `cpu`, `base_address` or `length` is invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def read_word(self, x, y, base_address, cpu=0): + """ + Read a word (usually of SDRAM) from the board. + + :param int x: + The x-coordinate of the chip where the word is to be read from + :param int y: + The y-coordinate of the chip where the word is to be read from + :param int base_address: + The address (usually in SDRAM) where the word to be read starts + :param int cpu: + the core ID used to read the word; should usually be 0 when reading + from SDRAM, but may be other values when reading from DTCM. + :return: The unsigned integer value at ``base_address`` + :rtype: int + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If one of `x`, `y`, `cpu` or `base_address` is invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def stop_application(self, app_id): + """ + Sends a stop request for an app_id. + + :param int app_id: The ID of the application to send to + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If app_id is not a valid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def wait_for_cores_to_be_in_state( + self, all_core_subsets, app_id, cpu_states, timeout=None, + time_between_polls=0.1, + error_states=frozenset({ + CPUState.RUN_TIME_EXCEPTION, CPUState.WATCHDOG}), + counts_between_full_check=100, progress_bar=None): + """ + Waits for the specified cores running the given application to be + in some target state or states. Handles failures. + + :param ~spinn_machine.CoreSubsets all_core_subsets: + the cores to check are in a given sync state + :param int app_id: the application ID that being used by the simulation + :param set(CPUState) cpu_states: + The expected states once the applications are ready; success is + when each application is in one of these states + :param float timeout: + The amount of time to wait in seconds for the cores to reach one + of the states + :param float time_between_polls: Time between checking the state + :param set(CPUState) error_states: + Set of states that the application can be in that indicate an + error, and so should raise an exception + :param int counts_between_full_check: + The number of times to use the count signal before instead using + the full CPU state check + :param progress_bar: Possible progress bar to update. + :type progress_bar: ~spinn_utilities.progress_bar.ProgressBar or None + :raise SpinnmanTimeoutException: + If a timeout is specified and exceeded. + """ + + @abstractmethod + def send_signal(self, app_id, signal): + """ + Send a signal to an application. + + :param int app_id: The ID of the application to send to + :param ~spinnman.messages.scp.enums.Signal signal: The signal to send + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If signal is not a valid signal + * If app_id is not a valid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def set_ip_tag(self, ip_tag, use_sender=False): + """ + Set up an IP tag. + + :param ~spinn_machine.tags.IPTag ip_tag: + The tag to set up. + + .. note:: + `board_address` can be `None`, in which case, the tag will be + assigned to all boards. + :param bool use_sender: + Optionally use the sender host and port instead of + the given host and port in the tag + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If the IP tag fields are incorrect + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def set_reverse_ip_tag(self, reverse_ip_tag): + """ + Set up a reverse IP tag. + + :param ~spinn_machine.tags.ReverseIPTag reverse_ip_tag: + The reverse tag to set up. + + .. note:: + The `board_address` field can be `None`, in which case, the tag + will be assigned to all boards. + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If the reverse IP tag fields are incorrect + * If a packet is received that has invalid parameters + * If the UDP port is one that is already used by SpiNNaker for + system functions + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def clear_ip_tag(self, tag, board_address=None): + """ + Clear the setting of an IP tag. + + :param int tag: The tag ID + :param str board_address: + Board address where the tag should be cleared. + If not specified, all AbstractSCPConnection connections will send + the message to clear the tag + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If the tag is not a valid tag + * If the connection cannot send SDP messages + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_tags(self, connection=None): + """ + Get the current set of tags that have been set on the board. + + :param AbstractSCPConnection connection: + Connection from which the tags should be received. + If not specified, all AbstractSCPConnection connections will be + queried and the response will be combined. + :return: An iterable of tags + :rtype: iterable(~spinn_machine.tags.AbstractTag) + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If the connection cannot send SDP messages + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def malloc_sdram(self, x, y, size, app_id, tag=None): + """ + Allocates a chunk of SDRAM on a chip on the machine. + + :param int x: The x-coordinate of the chip onto which to ask for memory + :param int y: The y-coordinate of the chip onto which to ask for memory + :param int size: the amount of memory to allocate in bytes + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :param int tag: the tag for the SDRAM, a 8-bit (chip-wide) tag that can + be looked up by a SpiNNaker application to discover the address of + the allocated block. If `0` then no tag is applied. + :return: the base address of the allocated memory + :rtype: int + """ + + @abstractmethod + def load_multicast_routes(self, x, y, routes, app_id): + """ + Load a set of multicast routes on to a chip. + + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param iterable(~spinn_machine.MulticastRoutingEntry) routes: + An iterable of multicast routes to load + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If any of the routes are invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def load_fixed_route(self, x, y, fixed_route, app_id): + """ + Loads a fixed route routing table entry onto a chip's router. + + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param ~spinn_machine.FixedRouteEntry fixed_route: + the route for the fixed route entry on this chip + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If any of the routes are invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def read_fixed_route(self, x, y, app_id): + """ + Reads a fixed route routing table entry from a chip's router. + + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param int app_id: + The ID of the application with which to associate the + routes. If not specified, defaults to 0. + :return: the route as a fixed route entry + """ + + @abstractmethod + def get_multicast_routes(self, x, y, app_id=None): + """ + Get the current multicast routes set up on a chip. + + :param int x: + The x-coordinate of the chip from which to get the routes + :param int y: + The y-coordinate of the chip from which to get the routes + :param int app_id: + The ID of the application to filter the routes for. If + not specified, will return all routes + :return: An iterable of multicast routes + :rtype: list(~spinn_machine.MulticastRoutingEntry) + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def clear_multicast_routes(self, x, y): + """ + Remove all the multicast routes on a chip. + + :param int x: The x-coordinate of the chip on which to clear the routes + :param int y: The y-coordinate of the chip on which to clear the routes + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def get_router_diagnostics(self, x, y): + """ + Get router diagnostic information from a chip. + + :param int x: + The x-coordinate of the chip from which to get the information + :param int y: + The y-coordinate of the chip from which to get the information + :return: The router diagnostic information + :rtype: RouterDiagnostics + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): + """ + Sets a router diagnostic filter in a router. + + :param int x: + The X address of the router in which this filter is being set. + :param int y: + The Y address of the router in which this filter is being set. + :param int position: + The position in the list of filters where this filter is to be + added. + :param ~spinnman.model.DiagnosticFilter diagnostic_filter: + The diagnostic filter being set in the placed, between 0 and 15. + + .. note:: + Positions 0 to 11 are used by the default filters, + and setting these positions will result in a warning. + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the data + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y does not lead to a valid chip + * If position is less than 0 or more than 15 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def clear_router_diagnostic_counters(self, x, y): + """ + Clear router diagnostic information on a chip. + + :param int x: The x-coordinate of the chip + :param int y: The y-coordinate of the chip + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters or a counter + ID is out of range + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + + @abstractmethod + def close(self): + """ + Close the transceiver and any threads that are running. + """ + + @abstractmethod + def control_sync(self, do_sync): + """ + Control the synchronisation of the chips. + + :param bool do_sync: Whether to synchronise or not + """ + + @abstractmethod + def update_provenance_and_exit(self, x, y, p): + """ + Sends a command to update prevenance and exit + + :param int x: + The x-coordinate of the core + :param int y: + The y-coordinate of the core + :param int p: + The processor on the core + """ diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/base_transceiver.py similarity index 99% rename from spinnman/transceiver/transceiver.py rename to spinnman/transceiver/base_transceiver.py index b1a62f6a9..30a461e3e 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -67,8 +67,8 @@ LoadMultiCastRoutesProcess, GetTagsProcess, GetMultiCastRoutesProcess, SendSingleCommandProcess, ReadRouterDiagnosticsProcess, MostDirectConnectionSelector, ApplicationCopyRunProcess) -from spinnman.utilities.utility_functions import ( - get_vcpu_address, work_out_bmp_from_machine_details) +from spinnman.utilities.utility_functions import get_vcpu_address +from spinnman.transceiver.abstract_transceiver import AbstractTransceiver logger = FormatAdapter(logging.getLogger(__name__)) @@ -81,7 +81,6 @@ _CONNECTION_CHECK_RETRIES = 3 INITIAL_FIND_SCAMP_RETRIES_COUNT = 3 -_ONE_BYTE = struct.Struct("B") _TWO_BYTES = struct.Struct(" Date: Wed, 2 Aug 2023 14:09:46 +0100 Subject: [PATCH 04/39] create_transceiver_from_hostname uses data version --- spinnman/get_cores_in_run_state.py | 4 +--- spinnman/transceiver/transceiver_factory.py | 20 ++++++++++++------- spinnman/transceiver/version3Transceiver.py | 22 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 spinnman/transceiver/version3Transceiver.py diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index 5b4374029..052dfff1b 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -82,7 +82,6 @@ def _make_transceiver(host, version, bmp_names): config = BoardTestConfiguration() config.set_up_remote_board() host = config.remotehost - version = config.board_version bmp_names = config.bmp_names auto_detect_bmp = config.auto_detect_bmp else: @@ -95,8 +94,7 @@ def _make_transceiver(host, version, bmp_names): print(f"talking to SpiNNaker system at {host}") return create_transceiver_from_hostname( - host, version, - bmp_connection_data=bmp_names, + host, bmp_connection_data=bmp_names, auto_detect_bmp=auto_detect_bmp) diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index d1c6e5093..438335253 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -14,17 +14,21 @@ import logging from spinn_utilities.log import FormatAdapter +from spinn_machine.version.version_3 import Version3 +from spinn_machine.version.version_5 import Version5 +from spinnman.data import SpiNNManDataView from spinnman.utilities.utility_functions import ( work_out_bmp_from_machine_details) from spinnman.connections.udp_packet_connections import ( BMPConnection, BootConnection, SCAMPConnection) -from spinnman.transceiver.base_transceiver import BaseTransceiver +from spinnman.transceiver.version3Transceiver import Version3Transceiver +from spinnman.transceiver.version5Transceiver import Version5Transceiver logger = FormatAdapter(logging.getLogger(__name__)) def create_transceiver_from_hostname( - hostname, version, bmp_connection_data=None, number_of_boards=None, + hostname, bmp_connection_data=None, number_of_boards=None, auto_detect_bmp=False): """ Create a Transceiver by creating a :py:class:`~.UDPConnection` to the @@ -40,9 +44,6 @@ def create_transceiver_from_hostname( :param number_of_boards: a number of boards expected to be supported, or ``None``, which defaults to a single board :type number_of_boards: int or None - :param int version: the type of SpiNNaker board used within the SpiNNaker - machine being used. If a Spinn-5 board, then the version will be 5, - Spinn-3 would equal 3 and so on. :param list(BMPConnectionData) bmp_connection_data: the details of the BMP connections used to boot multi-board systems :param bool auto_detect_bmp: @@ -68,7 +69,8 @@ def create_transceiver_from_hostname( # if no BMP has been supplied, but the board is a spinn4 or a spinn5 # machine, then an assumption can be made that the BMP is at -1 on the # final value of the IP address - if (version >= 4 and auto_detect_bmp is True and + version = SpiNNManDataView.get_machine_version() + if (isinstance(version, Version5) and auto_detect_bmp is True and (bmp_connection_data is None or not bmp_connection_data)): bmp_connection_data = [ work_out_bmp_from_machine_details(hostname, number_of_boards)] @@ -87,4 +89,8 @@ def create_transceiver_from_hostname( # handle the boot connection connections.append(BootConnection(remote_host=hostname)) - return BaseTransceiver(version, connections=connections) + if isinstance(version, Version3): + return Version3Transceiver(3, connections=connections) + if isinstance(version, Version5): + return Version5Transceiver(5, connections=connections) + raise NotImplementedError(f"No Transceiver for {version=}") \ No newline at end of file diff --git a/spinnman/transceiver/version3Transceiver.py b/spinnman/transceiver/version3Transceiver.py new file mode 100644 index 000000000..50a8cf3a8 --- /dev/null +++ b/spinnman/transceiver/version3Transceiver.py @@ -0,0 +1,22 @@ +# Copyright (c) 2024 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 spinnman.transceiver.base_transceiver import BaseTransceiver + + +class Version3Transceiver(BaseTransceiver): + """ + Implementation of the Transceiver classes for Version 5 boards + """ + pass From ec9ab467679b28b57505fb1743cc628890b0703c Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 3 Aug 2023 10:01:12 +0100 Subject: [PATCH 05/39] create_transceiver_from_hostname uses data Version --- spinnman/get_cores_in_run_state.py | 2 ++ spinnman/transceiver/transceiver_factory.py | 8 ++++---- unittests/test_transceiver.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index 052dfff1b..e018ebda8 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -18,6 +18,7 @@ import sys import argparse +from spinn_utilities.config_holder import set_config from spinnman.transceiver import create_transceiver_from_hostname from spinn_machine import CoreSubsets, CoreSubset from spinnman.board_test_configuration import BoardTestConfiguration @@ -91,6 +92,7 @@ def _make_transceiver(host, version, bmp_names): else: version = 5 auto_detect_bmp = False + set_config("Machine", "version", version) print(f"talking to SpiNNaker system at {host}") return create_transceiver_from_hostname( diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index 438335253..fbef035c1 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -44,7 +44,7 @@ def create_transceiver_from_hostname( :param number_of_boards: a number of boards expected to be supported, or ``None``, which defaults to a single board :type number_of_boards: int or None - :param list(BMPConnectionData) bmp_connection_data: + :param BMPConnectionData bmp_connection_data: the details of the BMP connections used to boot multi-board systems :param bool auto_detect_bmp: ``True`` if the BMP of version 4 or 5 boards should be @@ -71,9 +71,9 @@ def create_transceiver_from_hostname( # final value of the IP address version = SpiNNManDataView.get_machine_version() if (isinstance(version, Version5) and auto_detect_bmp is True and - (bmp_connection_data is None or not bmp_connection_data)): - bmp_connection_data = [ - work_out_bmp_from_machine_details(hostname, number_of_boards)] + (bmp_connection_data is None)): + bmp_connection_data = \ + work_out_bmp_from_machine_details(hostname, number_of_boards) # handle BMP connections if bmp_connection_data is not None: diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 90eb5faad..0b2f559d1 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -102,7 +102,7 @@ def test_retrieving_machine_details(self): def test_boot_board(self): board_config.set_up_remote_board() with create_transceiver_from_hostname( - board_config.remotehost, board_config.board_version) as trans: + board_config.remotehost) as trans: # self.assertFalse(trans.is_connected()) trans._boot_board() From e3b30cbcb1ecf0b825199a851d7a47b1086f005d Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 3 Aug 2023 16:27:54 +0100 Subject: [PATCH 06/39] more use of version --- spinnman/extended/extended_transceiver.py | 5 +- .../spinnaker_boot/spinnaker_boot_messages.py | 22 ++++---- .../system_variable_boot_values.py | 25 +++------ spinnman/spalloc/spalloc_client.py | 2 +- spinnman/transceiver/__init__.py | 7 +-- spinnman/transceiver/abstract_transceiver.py | 11 +++- spinnman/transceiver/base_transceiver.py | 8 +-- spinnman/transceiver/mockable_transceiver.py | 7 ++- spinnman/transceiver/transceiver_factory.py | 30 +++++++++-- spinnman/transceiver/version3Transceiver.py | 13 ++++- spinnman/transceiver/version5Transceiver.py | 12 +++++ unittests/test_transceiver.py | 54 ++++++++----------- 12 files changed, 116 insertions(+), 80 deletions(-) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 5134bb269..5615bbe68 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -133,9 +133,8 @@ class ExtendedTransceiver(Version5Transceiver, WatchdogSetter): __slots__ = ["_flood_write_lock", "_nearest_neighbour_id", "_nearest_neighbour_lock"] - def __init__(self, version, connections=None): + def __init__(self, connections=None): """ - :param int version: The version of the board being connected to :param list(Connection) connections: An iterable of connections to the board. If not specified, no communication will be possible until connections are found. @@ -149,7 +148,7 @@ def __init__(self, version, connections=None): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - super().__init__(version, connections) + super().__init__(connections) # A lock against multiple flood fill writes - needed as SCAMP cannot # cope with this diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py index f1a364177..97905e7d0 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py @@ -16,8 +16,9 @@ import math import time import array +from spinnman.data import SpiNNManDataView from .system_variable_boot_values import ( - SystemVariableBootValues, spinnaker_boot_values, SystemVariableDefinition) + SystemVariableBootValues, SystemVariableDefinition) from .spinnaker_boot_message import SpinnakerBootMessage from .spinnaker_boot_op_code import SpinnakerBootOpCode from spinnman.exceptions import ( @@ -41,9 +42,9 @@ class SpinnakerBootMessages(object): "_n_bytes_to_read", "_no_data_packets"] - def __init__(self, board_version=None, extra_boot_values=None): + def __init__(self, led_0, extra_boot_values=None): """ - :param int board_version: The version of the board to be booted + :param int led_0: The balue for the led_0 field :param extra_boot_values: Any additional or overwrite values to set during boot. This should only be used for values which are not standard @@ -54,16 +55,15 @@ def __init__(self, board_version=None, extra_boot_values=None): :raise SpinnmanIOException: If there is an error assembling the packets """ - if (board_version is not None and - board_version not in spinnaker_boot_values): - raise SpinnmanInvalidParameterException( - "board_version", str(board_version), "Unknown board version") + version = SpiNNManDataView.get_machine_version() # Get the boot packet values - if board_version is not None: - spinnaker_boot_value = spinnaker_boot_values[board_version] - else: - spinnaker_boot_value = SystemVariableBootValues() + spinnaker_boot_value = SystemVariableBootValues() + + spinnaker_boot_value.set_value( + SystemVariableDefinition.hardware_version, version.number) + spinnaker_boot_value.set_value( + SystemVariableDefinition.led_0, led_0) current_time = int(time.time()) spinnaker_boot_value.set_value( diff --git a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py index b3fb9d35c..7444808e0 100644 --- a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py +++ b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py @@ -356,17 +356,12 @@ class SystemVariableBootValues(object): __slot__ = [ "_values"] - def __init__(self, hardware_version=None, led_0=None): + def __init__(self): # Create a dict of variable values self._values = dict() for variable in SystemVariableDefinition: self._values[variable] = variable.default - if hardware_version is not None: - self._values[SystemVariableDefinition.hardware_version] =\ - hardware_version - if led_0 is not None: - self._values[SystemVariableDefinition.led_0] = led_0 def set_value(self, system_variable_definition, value): self._values[system_variable_definition] = value @@ -380,14 +375,10 @@ def bytestring(self): return data -spinnaker_boot_values = { - 1: SystemVariableBootValues( - hardware_version=1, led_0=0x00076104), - 2: SystemVariableBootValues( - hardware_version=2, led_0=0x00006103), - 3: SystemVariableBootValues( - hardware_version=3, led_0=0x00000502), - 4: SystemVariableBootValues( - hardware_version=4, led_0=0x00000001), - 5: SystemVariableBootValues( - hardware_version=5, led_0=0x00000001)} + """ + hardware_version=1, led_0=0x00076104), + hardware_version=2, led_0=0x00006103), + hardware_version=3, led_0=0x00000502), + hardware_version=4, led_0=0x00000001), + hardware_version=5, led_0=0x00000001)} + """ \ No newline at end of file diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index e7aaf9ae5..84c5bedbe 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -642,7 +642,7 @@ def create_transceiver(self) -> Version5Transceiver: self.connect_to_board(x, y) for (x, y) in self.get_connections()] # Also need a boot connection proxies.append(self.connect_for_booting()) - return Version5Transceiver(version=5, connections=proxies) + return Version5Transceiver(connections=proxies) def __repr__(self): return f"SpallocJob({self._url})" diff --git a/spinnman/transceiver/__init__.py b/spinnman/transceiver/__init__.py index c5d606a4c..5279a28a4 100644 --- a/spinnman/transceiver/__init__.py +++ b/spinnman/transceiver/__init__.py @@ -14,7 +14,8 @@ from .abstract_transceiver import AbstractTransceiver from .mockable_transceiver import MockableTransceiver -from .transceiver_factory import (create_transceiver_from_hostname) +from .transceiver_factory import ( + create_transceiver_from_connections, create_transceiver_from_hostname) -__all__ = ["AbstractTransceiver", "create_transceiver_from_hostname", - "MockableTransceiver"] +__all__ = ["AbstractTransceiver", "create_transceiver_from_connections", + "create_transceiver_from_hostname", "MockableTransceiver"] diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/abstract_transceiver.py index b4173dc6d..ba1146522 100644 --- a/spinnman/transceiver/abstract_transceiver.py +++ b/spinnman/transceiver/abstract_transceiver.py @@ -14,7 +14,8 @@ # pylint: disable=too-many-arguments -from spinn_utilities.abstract_base import (AbstractBase, abstractmethod) +from spinn_utilities.abstract_base import ( + AbstractBase, abstractmethod, abstractproperty) from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.model.enums import CPUState @@ -875,3 +876,11 @@ def update_provenance_and_exit(self, x, y, p): :param int p: The processor on the core """ + + @abstractproperty + def boot_led_0_value(self): + """ + The Values to be set in SpinnakerBootMessages for led_0 + + :rtype int: + """ \ No newline at end of file diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 30a461e3e..480954d16 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -118,13 +118,10 @@ class BaseTransceiver(AbstractTransceiver): "_scamp_connection_selector", "_scamp_connections", "_udp_scamp_connections", - "_version", "_width"] - def __init__( - self, version, connections=None): + def __init__(self, connections=None): """ - :param int version: The version of the board being connected to :param list(Connection) connections: An iterable of connections to the board. If not specified, no communication will be possible until connections are found. @@ -139,7 +136,6 @@ def __init__( If a response indicates an error during the exchange """ # Place to keep the current machine - self._version = version self._width = None self._height = None self._iobuf_size = None @@ -548,7 +544,7 @@ def _boot_board(self, extra_boot_values=None): # No can do. Can't boot without a boot connection. raise SpinnmanIOException("no boot connection available") boot_messages = SpinnakerBootMessages( - board_version=self._version, extra_boot_values=extra_boot_values) + led_0=self.boot_led_0_value, extra_boot_values=extra_boot_values) for boot_message in boot_messages.messages: self._boot_send_connection.send_boot_message(boot_message) time.sleep(2.0) diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 5c69945c1..5edfe58ca 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -214,4 +214,9 @@ def control_sync(self, do_sync): @overrides(AbstractTransceiver.update_provenance_and_exit) def update_provenance_and_exit(self, x, y, p): - pass \ No newline at end of file + pass + + @property + @overrides(AbstractTransceiver.boot_led_0_value) + def boot_led_0_value(self): + return 0 diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index fbef035c1..5f8f4be75 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -52,7 +52,7 @@ def create_transceiver_from_hostname( :param scamp_connections: the list of connections used for SCAMP communications :return: The created transceiver - :rtype: Transceiver + :rtype: spinnman.transceiver.AbstractTransceiver :raise SpinnmanIOException: If there is an error communicating with the board :raise SpinnmanInvalidPacketException: @@ -89,8 +89,30 @@ def create_transceiver_from_hostname( # handle the boot connection connections.append(BootConnection(remote_host=hostname)) + return create_transceiver_from_connections(connections) + + +def create_transceiver_from_connections(connections): + """ + Create a Transceiver with these connections + + :param list(Connection) connections: + An iterable of connections to the board. If not specified, no + communication will be possible until connections are found. + :return: The created transceiver + :rtype: spinnman.transceiver.AbstractTransceiver + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + version = SpiNNManDataView.get_machine_version() if isinstance(version, Version3): - return Version3Transceiver(3, connections=connections) + return Version3Transceiver(connections=connections) if isinstance(version, Version5): - return Version5Transceiver(5, connections=connections) - raise NotImplementedError(f"No Transceiver for {version=}") \ No newline at end of file + return Version5Transceiver(connections=connections) + raise NotImplementedError(f"No Transceiver for {version=}") diff --git a/spinnman/transceiver/version3Transceiver.py b/spinnman/transceiver/version3Transceiver.py index 50a8cf3a8..46e0a8a4e 100644 --- a/spinnman/transceiver/version3Transceiver.py +++ b/spinnman/transceiver/version3Transceiver.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from spinn_utilities.overrides import overrides +from spinnman.data import SpiNNManDataView from spinnman.transceiver.base_transceiver import BaseTransceiver @@ -19,4 +21,13 @@ class Version3Transceiver(BaseTransceiver): """ Implementation of the Transceiver classes for Version 5 boards """ - pass + + @overrides(BaseTransceiver.__init__) + def __init__(self, connections=None): + super().__init__(connections) + assert SpiNNManDataView.get_machine_version().number == 3 + + @property + @overrides(BaseTransceiver.boot_led_0_value) + def boot_led_0_value(self): + return 0x00000502 diff --git a/spinnman/transceiver/version5Transceiver.py b/spinnman/transceiver/version5Transceiver.py index f2fee0234..726f2cdb4 100644 --- a/spinnman/transceiver/version5Transceiver.py +++ b/spinnman/transceiver/version5Transceiver.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from spinn_utilities.overrides import overrides +from spinnman.data import SpiNNManDataView from spinnman.transceiver.base_transceiver import BaseTransceiver @@ -20,3 +22,13 @@ class Version5Transceiver(BaseTransceiver): Implementation of the Transceiver classes for Version 5 boards """ pass + + @overrides(BaseTransceiver.__init__) + def __init__(self, connections=None): + super().__init__(connections) + assert SpiNNManDataView.get_machine_version().number == 5 + + @property + @overrides(BaseTransceiver.boot_led_0_value) + def boot_led_0_value(self): + return 0x00000001 diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 0c6830d38..ea4e85f4c 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -19,9 +19,9 @@ from spinnman.data import SpiNNManDataView from spinnman.data.spinnman_data_writer import SpiNNManDataWriter from spinnman.transceiver import ( - create_transceiver_from_hostname, MockableTransceiver) + create_transceiver_from_connections, create_transceiver_from_hostname, + MockableTransceiver) from spinnman.transceiver.watchdog_setter import WatchdogSetter -from spinnman.transceiver.version5Transceiver import Version5Transceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( SystemVariableDefinition) @@ -30,8 +30,6 @@ import spinnman.extended.extended_transceiver as extended from spinnman.board_test_configuration import BoardTestConfiguration -ver = 5 # Guess? - class MockExtendedTransceiver(MockableTransceiver, WatchdogSetter): pass @@ -48,7 +46,7 @@ def test_create_new_transceiver_to_board(self): connections = list() connections.append(SCAMPConnection( remote_host=self.board_config.remotehost)) - trans = Version5Transceiver(ver, connections=connections) + trans = create_transceiver_from_connections(connections=connections) trans.close() def test_create_new_transceiver_one_connection(self): @@ -56,9 +54,9 @@ def test_create_new_transceiver_one_connection(self): connections = set() connections.add(SCAMPConnection( remote_host=self.board_config.remotehost)) - with extended.ExtendedTransceiver( - ver, connections=connections) as trans: - assert trans._all_connections == connections + if self.board_config.board_version == 5: + with extended.ExtendedTransceiver(connections=connections) as trans: + assert trans._all_connections == connections def test_create_new_transceiver_from_list_connections(self): self.board_config.set_up_remote_board() @@ -66,33 +64,25 @@ def test_create_new_transceiver_from_list_connections(self): connections.append(SCAMPConnection( remote_host=self.board_config.remotehost)) connections.append(BootConnection(remote_host="127.0.0.1")) - with Version5Transceiver(ver, connections=connections) as trans: - instantiated_connections = trans._all_connections - for connection in connections: - assert connection in instantiated_connections - # assert trans.get_connections() == connections + trans = create_transceiver_from_connections(connections=connections) + instantiated_connections = trans._all_connections + for connection in connections: + assert connection in instantiated_connections + #assert trans.get_connections() == connections def test_retrieving_machine_details(self): self.board_config.set_up_remote_board() - connections = list() - connections.append(SCAMPConnection( - remote_host=self.board_config.remotehost)) - connections.append(BootConnection(remote_host="127.0.0.1")) - with Version5Transceiver(ver, connections=connections) as trans: - SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) - if self.board_config.board_version in (2, 3): - assert trans._get_machine_dimensions().width == 2 - assert trans._get_machine_dimensions().height == 2 - elif self.board_config.board_version in (4, 5): - assert trans._get_machine_dimensions().width == 8 - assert trans._get_machine_dimensions().height == 8 - else: - size = trans._get_machine_dimensions() - print(f"Unknown board with size {size.width} x {size.height}") - - assert any(c.is_connected() for c in trans._scamp_connections) - print(trans._get_scamp_version()) - print(trans.get_cpu_infos()) + trans = create_transceiver_from_hostname(self.board_config.remotehost) + SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) + version = SpiNNManDataView.get_machine_version() + self.assertEqual( + version.board_shape, + (trans._get_machine_dimensions().width, + trans._get_machine_dimensions().height)) + + assert any(c.is_connected() for c in trans._scamp_connections) + print(trans._get_scamp_version()) + print(trans.get_cpu_infos()) def test_boot_board(self): self.board_config.set_up_remote_board() From 9571a89e3bd051b4a1c59a51435ffa0a28488654 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 3 Aug 2023 16:29:52 +0100 Subject: [PATCH 07/39] use create_transceiver_from_connections --- spinnman/spalloc/spalloc_client.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index 84c5bedbe..bcff0f9f4 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -35,7 +35,8 @@ from spinnman.constants import SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT from spinnman.exceptions import SpinnmanTimeoutException from spinnman.exceptions import SpallocException -from spinnman.transceiver.version5Transceiver import Version5Transceiver +from spinnman.transceiver import ( + AbstractTransceiver, create_transceiver_from_connections) from .spalloc_state import SpallocState from .proxy_protocol import ProxyProtocol from .session import Session, SessionAware @@ -635,14 +636,14 @@ def _keepalive_handle(self, handle): self.__keepalive_handle = handle @overrides(SpallocJob.create_transceiver) - def create_transceiver(self) -> Version5Transceiver: + def create_transceiver(self) -> AbstractTransceiver: if self.get_state() != SpallocState.READY: raise SpallocException("job not ready to execute scripts") proxies = [ self.connect_to_board(x, y) for (x, y) in self.get_connections()] # Also need a boot connection proxies.append(self.connect_for_booting()) - return Version5Transceiver(connections=proxies) + return create_transceiver_from_connections(connections=proxies) def __repr__(self): return f"SpallocJob({self._url})" From 3fb73de7f7201b2d1eb8ff27fa281ecaa85123a7 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 07:31:08 +0100 Subject: [PATCH 08/39] board speficic boot values trom Transciever --- .../spinnaker_boot/spinnaker_boot_messages.py | 7 ++----- .../system_variable_boot_values.py | 15 +++++---------- spinnman/transceiver/abstract_transceiver.py | 8 -------- spinnman/transceiver/base_transceiver.py | 18 +++++++++++++++--- spinnman/transceiver/mockable_transceiver.py | 5 ----- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py index 97905e7d0..30e327c84 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py @@ -21,8 +21,7 @@ SystemVariableBootValues, SystemVariableDefinition) from .spinnaker_boot_message import SpinnakerBootMessage from .spinnaker_boot_op_code import SpinnakerBootOpCode -from spinnman.exceptions import ( - SpinnmanInvalidParameterException, SpinnmanIOException) +from spinnman.exceptions import SpinnmanIOException _BOOT_MESSAGE_DATA_WORDS = 256 _BOOT_MESSAGE_DATA_BYTES = _BOOT_MESSAGE_DATA_WORDS * 4 @@ -44,11 +43,11 @@ class SpinnakerBootMessages(object): def __init__(self, led_0, extra_boot_values=None): """ - :param int led_0: The balue for the led_0 field :param extra_boot_values: Any additional or overwrite values to set during boot. This should only be used for values which are not standard based on the board version. + for example this may include an Led_0 value. :type extra_boot_values: dict(SystemVariableDefinition, object) :raise SpinnmanInvalidParameterException: If the board version is not supported @@ -62,8 +61,6 @@ def __init__(self, led_0, extra_boot_values=None): spinnaker_boot_value.set_value( SystemVariableDefinition.hardware_version, version.number) - spinnaker_boot_value.set_value( - SystemVariableDefinition.led_0, led_0) current_time = int(time.time()) spinnaker_boot_value.set_value( diff --git a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py index 7444808e0..e2fc3451a 100644 --- a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py +++ b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py @@ -162,6 +162,11 @@ class SystemVariableDefinition(Enum): led_0 = _Definition( _DataType.INT, offset=0x30, default=0x1, doc="The first part of the LED definitions") + # hardware_version=1, led_0=0x00076104), + # hardware_version=2, led_0=0x00006103), + # hardware_version=3, led_0=0x00000502), + # hardware_version=4, led_0=0x00000001), + # hardware_version=5, led_0=0x00000001)} led_1 = _Definition( _DataType.INT, offset=0x34, doc="The last part of the LED definitions") @@ -362,7 +367,6 @@ def __init__(self): for variable in SystemVariableDefinition: self._values[variable] = variable.default - def set_value(self, system_variable_definition, value): self._values[system_variable_definition] = value @@ -373,12 +377,3 @@ def bytestring(self): data += struct.pack(sys_var.data_type.struct_code, self._values[sys_var]) return data - - - """ - hardware_version=1, led_0=0x00076104), - hardware_version=2, led_0=0x00006103), - hardware_version=3, led_0=0x00000502), - hardware_version=4, led_0=0x00000001), - hardware_version=5, led_0=0x00000001)} - """ \ No newline at end of file diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/abstract_transceiver.py index ba1146522..e010b5a14 100644 --- a/spinnman/transceiver/abstract_transceiver.py +++ b/spinnman/transceiver/abstract_transceiver.py @@ -876,11 +876,3 @@ def update_provenance_and_exit(self, x, y, p): :param int p: The processor on the core """ - - @abstractproperty - def boot_led_0_value(self): - """ - The Values to be set in SpinnakerBootMessages for led_0 - - :rtype int: - """ \ No newline at end of file diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 480954d16..8f91b64e7 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -23,8 +23,9 @@ import logging import socket import time +from spinn_utilities.abstract_base import ( + AbstractBase, abstractproperty) from spinn_utilities.config_holder import get_config_bool -from spinn_utilities.abstract_context_manager import AbstractContextManager from spinn_utilities.log import FormatAdapter from spinn_machine import CoreSubsets from spinnman.constants import ( @@ -88,7 +89,7 @@ _EXECUTABLE_ADDRESS = 0x67800000 -class BaseTransceiver(AbstractTransceiver): +class BaseTransceiver(AbstractTransceiver, metaclass=AbstractBase): """ An encapsulation of various communications with the SpiNNaker board. @@ -525,6 +526,14 @@ def _get_scamp_version( process = GetVersionProcess(connection_selector, n_retries) return process.get_version(x=chip_x, y=chip_y, p=0) + @abstractproperty + def boot_led_0_value(self): + """ + The Values to be set in SpinnakerBootMessages for led_0 + + :rtype int: + """ + def _boot_board(self, extra_boot_values=None): """ Attempt to boot the board. No check is performed to see if the @@ -543,8 +552,11 @@ def _boot_board(self, extra_boot_values=None): if not self._boot_send_connection: # No can do. Can't boot without a boot connection. raise SpinnmanIOException("no boot connection available") + if SystemVariableDefinition.led_0 not in extra_boot_values: + extra_boot_values.set_value( + SystemVariableDefinition.led_0, self.boot_led_0_valu) boot_messages = SpinnakerBootMessages( - led_0=self.boot_led_0_value, extra_boot_values=extra_boot_values) + extra_boot_values=extra_boot_values) for boot_message in boot_messages.messages: self._boot_send_connection.send_boot_message(boot_message) time.sleep(2.0) diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 5edfe58ca..8fc9d6cfa 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -215,8 +215,3 @@ def control_sync(self, do_sync): @overrides(AbstractTransceiver.update_provenance_and_exit) def update_provenance_and_exit(self, x, y, p): pass - - @property - @overrides(AbstractTransceiver.boot_led_0_value) - def boot_led_0_value(self): - return 0 From d053d760d0c8e51c970c11927e26afbe3a9e0dfd Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 12:21:32 +0100 Subject: [PATCH 09/39] Extendenable tranceiver --- spinnman/extended/extended_transceiver.py | 196 ++++++++---------- spinnman/extended/version3transceiver.py | 19 ++ spinnman/extended/version5transceiver.py | 19 ++ .../spinnaker_boot/spinnaker_boot_messages.py | 2 +- spinnman/transceiver/abstract_transceiver.py | 13 +- spinnman/transceiver/base_transceiver.py | 94 +++------ .../transceiver/extendable_transceiver.py | 119 +++++++++++ spinnman/transceiver/mockable_transceiver.py | 23 +- spinnman/transceiver/transceiver_factory.py | 19 +- ...3Transceiver.py => version3transceiver.py} | 4 +- ...5Transceiver.py => version5transceiver.py} | 2 + spinnman/transceiver/watchdog_setter.py | 85 -------- unittests/test_transceiver.py | 22 +- 13 files changed, 328 insertions(+), 289 deletions(-) create mode 100644 spinnman/extended/version3transceiver.py create mode 100644 spinnman/extended/version5transceiver.py create mode 100644 spinnman/transceiver/extendable_transceiver.py rename spinnman/transceiver/{version3Transceiver.py => version3transceiver.py} (89%) rename spinnman/transceiver/{version5Transceiver.py => version5transceiver.py} (94%) delete mode 100644 spinnman/transceiver/watchdog_setter.py diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 5615bbe68..c032f7b6b 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -17,10 +17,12 @@ import io import os import logging -from threading import Condition, RLock +import struct import time +from spinn_utilities.abstract_base import AbstractBase from spinn_utilities.log import FormatAdapter from spinn_utilities.logger_utils import warn_once +from spinn_utilities.require_subclass import require_subclass from spinn_machine import CoreSubsets from spinnman.constants import ( ROUTER_REGISTER_BASE_ADDRESS, ROUTER_FILTER_CONTROLS_OFFSET, @@ -33,88 +35,22 @@ from spinnman.messages.scp.enums import Signal from spinnman.messages.scp.impl import ( ReadMemory, ApplicationRun) +from spinnman.connections.udp_packet_connections import SCAMPConnection +from spinnman.constants import SYSTEM_VARIABLE_BASE_ADDRESS +from spinnman.data import SpiNNManDataView from spinnman.messages.spinnaker_boot import SystemVariableDefinition -from spinnman.connections.udp_packet_connections import ( - BMPConnection, BootConnection, SCAMPConnection) from spinnman.processes import ( GetHeapProcess, ReadMemoryProcess, SendSingleCommandProcess, WriteMemoryProcess) -from spinnman.transceiver.version5Transceiver import Version5Transceiver -from spinnman.transceiver.watchdog_setter import WatchdogSetter -from spinnman.utilities.utility_functions import ( - work_out_bmp_from_machine_details) +from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver -logger = FormatAdapter(logging.getLogger(__name__)) - - -def create_transceiver_from_hostname( - hostname, version, bmp_connection_data=None, number_of_boards=None, - auto_detect_bmp=False): - """ - Create a Transceiver by creating a :py:class:`~.UDPConnection` to the - given hostname on port 17893 (the default SCAMP port), and a - :py:class:`~.BootConnection` on port 54321 (the default boot port), - optionally discovering any additional links using the UDPConnection, - and then returning the transceiver created with the conjunction of - the created UDPConnection and the discovered connections. - - :param hostname: The hostname or IP address of the board or `None` if - only the BMP connections are of interest - :type hostname: str or None - :param number_of_boards: a number of boards expected to be supported, or - ``None``, which defaults to a single board - :type number_of_boards: int or None - :param int version: the type of SpiNNaker board used within the SpiNNaker - machine being used. If a Spinn-5 board, then the version will be 5, - Spinn-3 would equal 3 and so on. - :param list(BMPConnectionData) bmp_connection_data: - the details of the BMP connections used to boot multi-board systems - :param bool auto_detect_bmp: - ``True`` if the BMP of version 4 or 5 boards should be - automatically determined from the board IP address - :param scamp_connections: - the list of connections used for SCAMP communications - :return: The created transceiver - :rtype: Transceiver - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - if hostname is not None: - logger.info("Creating transceiver for {}", hostname) - connections = list() - - # if no BMP has been supplied, but the board is a spinn4 or a spinn5 - # machine, then an assumption can be made that the BMP is at -1 on the - # final value of the IP address - if (version >= 4 and auto_detect_bmp is True and - (bmp_connection_data is None or not bmp_connection_data)): - bmp_connection_data = [ - work_out_bmp_from_machine_details(hostname, number_of_boards)] - - # handle BMP connections - if bmp_connection_data is not None: - bmp_ip_list = list() - for conn_data in bmp_connection_data: - bmp_connection = BMPConnection(conn_data) - connections.append(bmp_connection) - bmp_ip_list.append(bmp_connection.remote_ip_address) - logger.info("Transceiver using BMPs: {}", bmp_ip_list) - - connections.append(SCAMPConnection(remote_host=hostname)) - - # handle the boot connection - connections.append(BootConnection(remote_host=hostname)) +_ONE_BYTE = struct.Struct("B") - return ExtendedTransceiver(version, connections=connections) +logger = FormatAdapter(logging.getLogger(__name__)) -class ExtendedTransceiver(Version5Transceiver, WatchdogSetter): +@require_subclass(ExtendableTransceiver) +class ExtendedTransceiver(object, metaclass=AbstractBase): """ An encapsulation of various communications with the SpiNNaker board. @@ -130,8 +66,7 @@ class ExtendedTransceiver(Version5Transceiver, WatchdogSetter): the multiple calls may be made separately over the set of given connections. """ - __slots__ = ["_flood_write_lock", "_nearest_neighbour_id", - "_nearest_neighbour_lock"] + __slots__ = [] def __init__(self, connections=None): """ @@ -150,13 +85,6 @@ def __init__(self, connections=None): """ super().__init__(connections) - # A lock against multiple flood fill writes - needed as SCAMP cannot - # cope with this - self._flood_write_lock = Condition() - - # The nearest neighbour start ID and lock - self._nearest_neighbour_id = 1 - self._nearest_neighbour_lock = RLock() def send_scp_message(self, message, connection=None): """ @@ -185,31 +113,22 @@ def send_scp_message(self, message, connection=None): connection = self._get_random_connection(self._scamp_connections) connection.send_scp_request(message) - def get_connections(self): - """ - Get the currently known connections to the board, made up of those - passed in to the transceiver and those that are discovered during - calls to discover_connections. No further discovery is done here. - - :return: An iterable of connections known to the transceiver - :rtype: list(Connection) - """ - return self._all_connections - def is_connected(self, connection=None): """ - Determines if the board can be contacted. + Determines if the board can be contacted via SCAMP :param Connection connection: The connection which is to be tested. If `None`, - all connections will be tested, and the board will be considered + all Scamp connections will be tested, + and the board will be considered to be connected if any one connection works. :return: True if the board can be contacted, False otherwise :rtype: bool """ if connection is not None: return connection.is_connected() - return any(c.is_connected() for c in self._scamp_connections) + return any(c.is_connected() and isinstance(c, SCAMPConnection) + for c in self._scamp_connections) def get_iobuf_from_core(self, x, y, p): """ @@ -392,7 +311,7 @@ def set_led(self, led, action, board): """ warn_once(logger, "The set_led method is deprecated and " "untested due to no known use.") - process = SendSingleCommandProcess(self._bmp_selector) + process = SendSingleCommandProcess(self.bmp_selector) process.execute(BMPSetLed(led, action, board)) def read_adc_data(self, board): @@ -410,7 +329,7 @@ def read_adc_data(self, board): """ warn_once(logger, "The read_adc_data method is deprecated and " "untested due to no known use.") - process = SendSingleCommandProcess(self._bmp_selector) + process = SendSingleCommandProcess(self.bmp_selector) response = process.execute(ReadADC(board)) return response.adc_info # pylint: disable=no-member @@ -468,7 +387,7 @@ def write_neighbour_memory(self, x, y, link, base_address, data, """ warn_once(logger, "The write_neighbour_memory method is deprecated " "and untested due to no known use.") - process = WriteMemoryProcess(self._scamp_connection_selector) + process = WriteMemoryProcess(self.scamp_connection_selector) if isinstance(data, io.RawIOBase): process.write_link_memory_from_reader( x, y, cpu, link, base_address, data, n_bytes) @@ -519,11 +438,11 @@ def read_neighbour_memory(self, x, y, link, base_address, length, cpu=0): try: warn_once(logger, "The read_neighbour_memory method is deprecated " "and untested due to no known use.") - process = ReadMemoryProcess(self._scamp_connection_selector) + process = ReadMemoryProcess(self.scamp_connection_selector) return process.read_link_memory( x, y, cpu, link, base_address, length) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def _get_next_nearest_neighbour_id(self): @@ -627,7 +546,7 @@ def set_leds(self, x, y, cpu, led_states): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(SetLED(x, y, cpu, led_states)) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def free_sdram(self, x, y, base_address, app_id): @@ -648,7 +567,7 @@ def free_sdram(self, x, y, base_address, app_id): process = DeAllocSDRAMProcess(self._scamp_connection_selector) process.de_alloc_sdram(x, y, app_id, base_address) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def free_sdram_by_app_id(self, x, y, app_id): @@ -673,7 +592,7 @@ def free_sdram_by_app_id(self, x, y, app_id): process.de_alloc_sdram(x, y, app_id) return process.no_blocks_freed except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def get_router_diagnostic_filter(self, x, y, position): @@ -708,13 +627,13 @@ def get_router_diagnostic_filter(self, x, y, position): position * ROUTER_DIAGNOSTIC_FILTER_SIZE) process = SendSingleCommandProcess( - self._scamp_connection_selector) + self.scamp_connection_selector) response = process.execute(ReadMemory(x, y, memory_position, 4)) return DiagnosticFilter.read_from_int(self._ONE_WORD.unpack_from( response.data, response.offset)[0]) # pylint: disable=no-member except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @property @@ -729,8 +648,8 @@ def number_of_boards_located(self): """ warn_once(logger, "The number_of_boards_located method is deprecated " "and likely to be removed.") - if self._bmp_connection is not None: - return max(1, len(self._bmp_connection.boards)) + if self.bmp_connection is not None: + return max(1, len(self.bmp_connection.boards)) else: # if no BMPs are available, then there's still at least one board return 1 @@ -746,8 +665,61 @@ def get_heap(self, x, y, heap=SystemVariableDefinition.sdram_heap_address): :rtype: list(HeapElement) """ try: - process = GetHeapProcess(self._scamp_connection_selector) + process = GetHeapProcess(self.scamp_connection_selector) return process.get_heap((x, y), heap) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise + + def __set_watch_dog_on_chip(self, x, y, watch_dog): + """ + Enable, disable or set the value of the watch dog timer on a + specific chip. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param int x: chip X coordinate to write new watchdog parameter to + :param int y: chip Y coordinate to write new watchdog parameter to + :param watch_dog: + Either a boolean indicating whether to enable (True) or + disable (False) the watchdog timer, or an int value to set the + timer count to + :type watch_dog: bool or int + """ + # build what we expect it to be + warn_once(logger, "The set_watch_dog_on_chip method is deprecated " + "and untested due to no known use.") + value_to_set = watch_dog + watchdog = SystemVariableDefinition.software_watchdog_count + if isinstance(watch_dog, bool): + value_to_set = watchdog.default if watch_dog else 0 + + # build data holder + data = _ONE_BYTE.pack(value_to_set) + + # write data + address = SYSTEM_VARIABLE_BASE_ADDRESS + watchdog.offset + self.write_memory(x=x, y=y, base_address=address, data=data) + + def set_watch_dog(self, watch_dog): + """ + Enable, disable or set the value of the watch dog timer. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param watch_dog: + Either a boolean indicating whether to enable (True) or + disable (False) the watch dog timer, or an int value to set the + timer count to. + :type watch_dog: bool or int + """ + warn_once(logger, "The set_watch_dog method is deprecated and " + "untested due to no known use.") + for x, y in SpiNNManDataView.get_machine().chip_coordinates: + self.__set_watch_dog_on_chip(x, y, watch_dog) diff --git a/spinnman/extended/version3transceiver.py b/spinnman/extended/version3transceiver.py new file mode 100644 index 000000000..c2b8e6603 --- /dev/null +++ b/spinnman/extended/version3transceiver.py @@ -0,0 +1,19 @@ +# Copyright (c) 2023 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 .extended_transceiver import ExtendedTransceiver +from spinnman.transceiver.version3transceiver import Version3Transceiver + + +class ExtendedVersion3Transceiver(Version3Transceiver, ExtendedTransceiver): + pass diff --git a/spinnman/extended/version5transceiver.py b/spinnman/extended/version5transceiver.py new file mode 100644 index 000000000..24e1a7718 --- /dev/null +++ b/spinnman/extended/version5transceiver.py @@ -0,0 +1,19 @@ +# Copyright (c) 2023 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 .extended_transceiver import ExtendedTransceiver +from spinnman.transceiver.version5transceiver import Version5Transceiver + + +class ExtendedVersion5Transceiver(Version5Transceiver, ExtendedTransceiver): + pass diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py index 30e327c84..e7fad9022 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py @@ -41,7 +41,7 @@ class SpinnakerBootMessages(object): "_n_bytes_to_read", "_no_data_packets"] - def __init__(self, led_0, extra_boot_values=None): + def __init__(self, extra_boot_values=None): """ :param extra_boot_values: Any additional or overwrite values to set during boot. diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/abstract_transceiver.py index e010b5a14..f64fdf893 100644 --- a/spinnman/transceiver/abstract_transceiver.py +++ b/spinnman/transceiver/abstract_transceiver.py @@ -15,7 +15,7 @@ # pylint: disable=too-many-arguments from spinn_utilities.abstract_base import ( - AbstractBase, abstractmethod, abstractproperty) + AbstractBase, abstractmethod) from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.model.enums import CPUState @@ -87,6 +87,17 @@ def add_scamp_connections(self, connections): If a response indicates an error during the exchange """ + @abstractmethod + def get_connections(self): + """ + Get the currently known connections to the board, made up of those + passed in to the transceiver and those that are discovered during + calls to discover_connections. No further discovery is done here. + + :return: An iterable of connections known to the transceiver + :rtype: set(Connection) + """ + @abstractmethod def get_machine_details(self): """ diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 8f91b64e7..bf89d8dfb 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -15,10 +15,7 @@ # pylint: disable=too-many-arguments import io import os -import random import struct -from threading import Condition -from collections import defaultdict from contextlib import contextmanager, suppress import logging import socket @@ -27,6 +24,7 @@ AbstractBase, abstractproperty) from spinn_utilities.config_holder import get_config_bool from spinn_utilities.log import FormatAdapter +from spinn_utilities.overrides import overrides from spinn_machine import CoreSubsets from spinnman.constants import ( BMP_POST_POWER_ON_SLEEP_TIME, BMP_POWER_ON_TIMEOUT, BMP_TIMEOUT, @@ -70,6 +68,7 @@ MostDirectConnectionSelector, ApplicationCopyRunProcess) from spinnman.utilities.utility_functions import get_vcpu_address from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver logger = FormatAdapter(logging.getLogger(__name__)) @@ -89,33 +88,17 @@ _EXECUTABLE_ADDRESS = 0x67800000 -class BaseTransceiver(AbstractTransceiver, metaclass=AbstractBase): +class BaseTransceiver(ExtendableTransceiver, metaclass=AbstractBase): """ - An encapsulation of various communications with the SpiNNaker board. - - The methods of this class are designed to be thread-safe (provided they do - not access a BMP, as access to those is never thread-safe); - thus you can make multiple calls to the same (or different) methods - from multiple threads and expect each call to work as if it had been - called sequentially, although the order of returns is not guaranteed. - - .. note:: - With multiple connections to the board, using multiple threads in this - way may result in an increase in the overall speed of operation, since - the multiple calls may be made separately over the set of given - connections. """ __slots__ = [ "_all_connections", "_bmp_selector", "_bmp_connection", "_boot_send_connection", - "_chip_execute_lock_condition", - "_chip_execute_locks", "_height", "_iobuf_size", "_machine_off", - "_n_chip_execute_locks", "_scamp_connection_selector", "_scamp_connections", "_udp_scamp_connections", @@ -136,6 +119,8 @@ def __init__(self, connections=None): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + super().__init__() + # Place to keep the current machine self._width = None self._height = None @@ -170,30 +155,27 @@ def __init__(self, connections=None): self._scamp_connection_selector = \ self.__identify_connections(connections) - # A lock against single chip executions (entry is (x, y)) - # The condition should be acquired before the locks are - # checked or updated - # The write lock condition should also be acquired to avoid a flood - # fill during an individual chip execute - self._chip_execute_locks = defaultdict(Condition) - self._chip_execute_lock_condition = Condition() - self._n_chip_execute_locks = 0 - # Check that the BMP connections are valid self.__check_bmp_connection() self._machine_off = False - def _where_is_xy(self, x, y): - """ - Attempts to get where_is_x_y info from the machine + @property + @overrides(ExtendableTransceiver.bmp_selector) + def bmp_selector(self): + return self._bmp_selector - If no machine will do its best. + @property + @overrides(ExtendableTransceiver.scamp_connection_selector) + def scamp_connection_selector(self): + return self._scamp_connection_selector - :param int x: - :param int y: - :rtype: str - """ + @property + @overrides(ExtendableTransceiver.bmp_connection) + def bmp_connection(self): + return self._bmp_connection + + def where_is_xy(self, x, y): try: if SpiNNManDataView.has_machine(): return SpiNNManDataView.get_machine().where_is_xy(x, y) @@ -298,32 +280,6 @@ def _check_connection( break return None - @contextmanager - def __flood_execute_lock(self): - """ - Get a lock for executing a flood fill of an executable. - """ - # Get the execute lock all together, so nothing can access it - with self._chip_execute_lock_condition: - # Wait until nothing is executing - self._chip_execute_lock_condition.wait_for( - lambda: self._n_chip_execute_locks < 1) - yield self._chip_execute_lock_condition - - @staticmethod - def _get_random_connection(connections): - """ - Returns the given connection, or else picks one at random. - - :param list(Connection) connections: - the list of connections to locate a random one from - :return: a connection object - :rtype: Connection or None - """ - if not connections: - return None - return connections[random.randint(0, len(connections) - 1)] - def send_sdp_message(self, message, connection=None): """ Sends an SDP message using one of the connections. @@ -434,6 +390,10 @@ def add_scamp_connections(self, connections): self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) + @overrides(AbstractTransceiver.get_connections) + def get_connections(self): + return self._all_connections + def _get_machine_dimensions(self): """ Get the maximum chip X-coordinate and maximum chip Y-coordinate of @@ -552,9 +512,11 @@ def _boot_board(self, extra_boot_values=None): if not self._boot_send_connection: # No can do. Can't boot without a boot connection. raise SpinnmanIOException("no boot connection available") + if extra_boot_values is None: + extra_boot_values = dict() if SystemVariableDefinition.led_0 not in extra_boot_values: - extra_boot_values.set_value( - SystemVariableDefinition.led_0, self.boot_led_0_valu) + extra_boot_values[SystemVariableDefinition.led_0] = \ + self.boot_led_0_value boot_messages = SpinnakerBootMessages( extra_boot_values=extra_boot_values) for boot_message in boot_messages.messages: @@ -969,7 +931,7 @@ def execute_flood( If a response indicates an error during the exchange """ # Lock against other executable's - with self.__flood_execute_lock(): + with self._flood_execute_lock(): # Flood fill the system with the binary n_bytes, chksum = self.write_memory( 0, 0, _EXECUTABLE_ADDRESS, executable, n_bytes, diff --git a/spinnman/transceiver/extendable_transceiver.py b/spinnman/transceiver/extendable_transceiver.py new file mode 100644 index 000000000..b024aba83 --- /dev/null +++ b/spinnman/transceiver/extendable_transceiver.py @@ -0,0 +1,119 @@ +# Copyright (c) 2014 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. + +# pylint: disable=too-many-arguments +from contextlib import contextmanager, suppress + +from collections import defaultdict +import logging +import random +from threading import Condition, RLock +from spinn_utilities.abstract_base import ( + AbstractBase, abstractproperty, abstractmethod) +from spinn_utilities.log import FormatAdapter +from spinnman.transceiver.abstract_transceiver import AbstractTransceiver + +logger = FormatAdapter(logging.getLogger(__name__)) + + +class ExtendableTransceiver(AbstractTransceiver, metaclass=AbstractBase): + """ + + """ + __slots__ = [ + "_chip_execute_lock_condition", + "_chip_execute_locks", + "_flood_write_lock", + "_n_chip_execute_locks", + "_nearest_neighbour_id", + "_nearest_neighbour_lock" + ] + + def __init__(self): + # A lock against single chip executions (entry is (x, y)) + # The condition should be acquired before the locks are + # checked or updated + # The write lock condition should also be acquired to avoid a flood + # fill during an individual chip execute + self._chip_execute_locks = defaultdict(Condition) + self._chip_execute_lock_condition = Condition() + self._n_chip_execute_locks = 0 + # A lock against multiple flood fill writes - needed as SCAMP cannot + # cope with this + self._flood_write_lock = Condition() + + # The nearest neighbour start ID and lock + self._nearest_neighbour_id = 1 + self._nearest_neighbour_lock = RLock() + + @abstractproperty + def bmp_connection(self): + """ + Returns the BMP connection if there is one + :rtype: BMPConnection or None + """ + + @abstractproperty + def bmp_selector(self): + """ + Returns the bmp selector + + :rtype: AbstractMultiConnectionProcessConnectionSelector + """ + + @abstractproperty + def scamp_connection_selector(self): + """ + Returns the scamp selector + + :rtype: AbstractMultiConnectionProcessConnectionSelector + """ + + @abstractmethod + def where_is_xy(self, x, y): + """ + Attempts to get where_is_x_y info from the machine + + If no machine will do its best. + + :param int x: + :param int y: + :rtype: str + """ + + @contextmanager + def _flood_execute_lock(self): + """ + Get a lock for executing a flood fill of an executable. + """ + # Get the execute lock all together, so nothing can access it + with self._chip_execute_lock_condition: + # Wait until nothing is executing + self._chip_execute_lock_condition.wait_for( + lambda: self._n_chip_execute_locks < 1) + yield self._chip_execute_lock_condition + + @staticmethod + def _get_random_connection(connections): + """ + Returns the given connection, or else picks one at random. + + :param list(Connection) connections: + the list of connections to locate a random one from + :return: a connection object + :rtype: Connection or None + """ + if not connections: + return None + return connections[random.randint(0, len(connections) - 1)] diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 8fc9d6cfa..4d5b992ea 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -18,9 +18,10 @@ from spinnman.data import SpiNNManDataView from spinnman.model.enums import CPUState from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver -class MockableTransceiver(AbstractTransceiver): +class MockableTransceiver(ExtendableTransceiver): """ A based for Mock Transceivers """ @@ -45,6 +46,10 @@ def add_scamp_connections(self, connections): def get_machine_details(self): return SpiNNManDataView.get_machine() + @overrides(AbstractTransceiver.get_connections) + def get_connections(self): + raise NotImplementedError("Needs to be mocked") + @overrides(AbstractTransceiver.ensure_board_is_ready) def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): raise NotImplementedError("Needs to be mocked") @@ -215,3 +220,19 @@ def control_sync(self, do_sync): @overrides(AbstractTransceiver.update_provenance_and_exit) def update_provenance_and_exit(self, x, y, p): pass + + @overrides(ExtendableTransceiver.bmp_connection) + def bmp_connection(self): + raise NotImplementedError("Needs to be mocked") + + @overrides(ExtendableTransceiver.bmp_selector) + def bmp_selector(self): + raise NotImplementedError("Needs to be mocked") + + @overrides(ExtendableTransceiver.scamp_connection_selector) + def scamp_connection_selector(self): + raise NotImplementedError("Needs to be mocked") + + @overrides(ExtendableTransceiver.where_is_xy) + def where_is_xy(self, x, y): + return f"Mocked {x=} {y=}" diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index 5f8f4be75..455f0bbd5 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -17,19 +17,21 @@ from spinn_machine.version.version_3 import Version3 from spinn_machine.version.version_5 import Version5 from spinnman.data import SpiNNManDataView +from spinnman.extended.version3transceiver import ExtendedVersion3Transceiver +from spinnman.extended.version5transceiver import ExtendedVersion5Transceiver from spinnman.utilities.utility_functions import ( work_out_bmp_from_machine_details) from spinnman.connections.udp_packet_connections import ( BMPConnection, BootConnection, SCAMPConnection) -from spinnman.transceiver.version3Transceiver import Version3Transceiver -from spinnman.transceiver.version5Transceiver import Version5Transceiver +from spinnman.transceiver.version3transceiver import Version3Transceiver +from spinnman.transceiver.version5transceiver import Version5Transceiver logger = FormatAdapter(logging.getLogger(__name__)) def create_transceiver_from_hostname( hostname, bmp_connection_data=None, number_of_boards=None, - auto_detect_bmp=False): + auto_detect_bmp=False, extended=False): """ Create a Transceiver by creating a :py:class:`~.UDPConnection` to the given hostname on port 17893 (the default SCAMP port), and a @@ -51,6 +53,8 @@ def create_transceiver_from_hostname( automatically determined from the board IP address :param scamp_connections: the list of connections used for SCAMP communications + :param bool extended: + If True will return an Extended version of the Transceiver :return: The created transceiver :rtype: spinnman.transceiver.AbstractTransceiver :raise SpinnmanIOException: @@ -89,16 +93,17 @@ def create_transceiver_from_hostname( # handle the boot connection connections.append(BootConnection(remote_host=hostname)) - return create_transceiver_from_connections(connections) + return create_transceiver_from_connections(connections, extended) -def create_transceiver_from_connections(connections): +def create_transceiver_from_connections(connections, extended=False): """ Create a Transceiver with these connections :param list(Connection) connections: An iterable of connections to the board. If not specified, no communication will be possible until connections are found. + :param bool extended: :return: The created transceiver :rtype: spinnman.transceiver.AbstractTransceiver :raise SpinnmanIOException: @@ -112,7 +117,11 @@ def create_transceiver_from_connections(connections): """ version = SpiNNManDataView.get_machine_version() if isinstance(version, Version3): + if extended: + return ExtendedVersion3Transceiver(connections=connections) return Version3Transceiver(connections=connections) if isinstance(version, Version5): + if extended: + return ExtendedVersion5Transceiver(connections=connections) return Version5Transceiver(connections=connections) raise NotImplementedError(f"No Transceiver for {version=}") diff --git a/spinnman/transceiver/version3Transceiver.py b/spinnman/transceiver/version3transceiver.py similarity index 89% rename from spinnman/transceiver/version3Transceiver.py rename to spinnman/transceiver/version3transceiver.py index 46e0a8a4e..e0f883101 100644 --- a/spinnman/transceiver/version3Transceiver.py +++ b/spinnman/transceiver/version3transceiver.py @@ -19,7 +19,9 @@ class Version3Transceiver(BaseTransceiver): """ - Implementation of the Transceiver classes for Version 5 boards + Implementation of the Transceiver classes for Version 3 boards + + This class should ONLY be created via by transceiver_factory.py """ @overrides(BaseTransceiver.__init__) diff --git a/spinnman/transceiver/version5Transceiver.py b/spinnman/transceiver/version5transceiver.py similarity index 94% rename from spinnman/transceiver/version5Transceiver.py rename to spinnman/transceiver/version5transceiver.py index 726f2cdb4..29c92532b 100644 --- a/spinnman/transceiver/version5Transceiver.py +++ b/spinnman/transceiver/version5transceiver.py @@ -20,6 +20,8 @@ class Version5Transceiver(BaseTransceiver): """ Implementation of the Transceiver classes for Version 5 boards + + This class should ONLY be created via by transceiver_factory.py """ pass diff --git a/spinnman/transceiver/watchdog_setter.py b/spinnman/transceiver/watchdog_setter.py deleted file mode 100644 index 254f1aff7..000000000 --- a/spinnman/transceiver/watchdog_setter.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2024 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 spinn_utilities.abstract_base import AbstractBase, abstractmethod -from spinn_utilities.log import FormatAdapter -from spinn_utilities.logger_utils import warn_once -from spinn_utilities.require_subclass import require_subclass -from spinnman.constants import SYSTEM_VARIABLE_BASE_ADDRESS -from spinnman.data import SpiNNManDataView -from spinnman.messages.spinnaker_boot import SystemVariableDefinition -from spinnman.transceiver.abstract_transceiver import AbstractTransceiver - -_ONE_BYTE = struct.Struct("B") - -logger = FormatAdapter(logging.getLogger(__name__)) - - -@require_subclass(AbstractTransceiver) -class WatchdogSetter(object, metaclass=AbstractBase): - - def __set_watch_dog_on_chip(self, x, y, watch_dog): - """ - Enable, disable or set the value of the watch dog timer on a - specific chip. - - .. warning:: - This method is currently deprecated and untested as there is no - known use. Same functionality provided by ybug and bmpc. - Retained in case needed for hardware debugging. - - :param int x: chip X coordinate to write new watchdog parameter to - :param int y: chip Y coordinate to write new watchdog parameter to - :param watch_dog: - Either a boolean indicating whether to enable (True) or - disable (False) the watchdog timer, or an int value to set the - timer count to - :type watch_dog: bool or int - """ - # build what we expect it to be - warn_once(logger, "The set_watch_dog_on_chip method is deprecated " - "and untested due to no known use.") - value_to_set = watch_dog - watchdog = SystemVariableDefinition.software_watchdog_count - if isinstance(watch_dog, bool): - value_to_set = watchdog.default if watch_dog else 0 - - # build data holder - data = _ONE_BYTE.pack(value_to_set) - - # write data - address = SYSTEM_VARIABLE_BASE_ADDRESS + watchdog.offset - self.write_memory(x=x, y=y, base_address=address, data=data) - - def set_watch_dog(self, watch_dog): - """ - Enable, disable or set the value of the watch dog timer. - - .. warning:: - This method is currently deprecated and untested as there is no - known use. Same functionality provided by ybug and bmpc. - Retained in case needed for hardware debugging. - - :param watch_dog: - Either a boolean indicating whether to enable (True) or - disable (False) the watch dog timer, or an int value to set the - timer count to. - :type watch_dog: bool or int - """ - warn_once(logger, "The set_watch_dog method is deprecated and " - "untested due to no known use.") - for x, y in SpiNNManDataView.get_machine().chip_coordinates: - self.__set_watch_dog_on_chip(x, y, watch_dog) diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index ea4e85f4c..b73c89fd8 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -21,17 +21,16 @@ from spinnman.transceiver import ( create_transceiver_from_connections, create_transceiver_from_hostname, MockableTransceiver) -from spinnman.transceiver.watchdog_setter import WatchdogSetter +from spinnman.extended.extended_transceiver import ExtendedTransceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( SystemVariableDefinition) from spinnman.connections.udp_packet_connections import ( BootConnection, SCAMPConnection) -import spinnman.extended.extended_transceiver as extended from spinnman.board_test_configuration import BoardTestConfiguration -class MockExtendedTransceiver(MockableTransceiver, WatchdogSetter): +class MockExtendedTransceiver(MockableTransceiver, ExtendedTransceiver): pass @@ -47,6 +46,7 @@ def test_create_new_transceiver_to_board(self): connections.append(SCAMPConnection( remote_host=self.board_config.remotehost)) trans = create_transceiver_from_connections(connections=connections) + trans.get_connections() == connections trans.close() def test_create_new_transceiver_one_connection(self): @@ -54,21 +54,9 @@ def test_create_new_transceiver_one_connection(self): connections = set() connections.add(SCAMPConnection( remote_host=self.board_config.remotehost)) - if self.board_config.board_version == 5: - with extended.ExtendedTransceiver(connections=connections) as trans: - assert trans._all_connections == connections - - def test_create_new_transceiver_from_list_connections(self): - self.board_config.set_up_remote_board() - connections = list() - connections.append(SCAMPConnection( - remote_host=self.board_config.remotehost)) - connections.append(BootConnection(remote_host="127.0.0.1")) trans = create_transceiver_from_connections(connections=connections) - instantiated_connections = trans._all_connections - for connection in connections: - assert connection in instantiated_connections - #assert trans.get_connections() == connections + self.assertSetEqual(connections, trans.get_connections()) + trans.close() def test_retrieving_machine_details(self): self.board_config.set_up_remote_board() From 4f0ef554decf38f5dd9d29de4ddaa46e0ae30924 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 13:13:33 +0100 Subject: [PATCH 10/39] allow spalloc to support any version in principle --- spinnman/spalloc/spalloc_job.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py index 9cab74d4b..c268dcb96 100644 --- a/spinnman/spalloc/spalloc_job.py +++ b/spinnman/spalloc/spalloc_job.py @@ -17,7 +17,7 @@ from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.constants import SCP_SCAMP_PORT -from spinnman.transceiver.version5Transceiver import Version5Transceiver +from spinnman.transceiver.abstract_transceiver import AbstractTransceiver from spinnman.connections.udp_packet_connections import UDPConnection from .spalloc_state import SpallocState from .spalloc_boot_connection import SpallocBootConnection @@ -121,12 +121,12 @@ def open_udp_listener_connection(self) -> UDPConnection: """ @abstractmethod - def create_transceiver(self) -> Version5Transceiver: + def create_transceiver(self) -> AbstractTransceiver: """ Create a transceiver that will talk to this job. The transceiver will only be configured to talk to the SCP ports of the boards of the job. - :rtype: Transceiver + :rtype: AbstractTransceiver """ @abstractmethod From bc633141ca38324daeae3c3ead5b21516c9210f0 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 14:49:23 +0100 Subject: [PATCH 11/39] cleanup --- spinnman/extended/extended_transceiver.py | 43 +- spinnman/transceiver/abstract_transceiver.py | 12 + spinnman/transceiver/base_transceiver.py | 747 ++---------------- .../transceiver/extendable_transceiver.py | 55 +- spinnman/transceiver/mockable_transceiver.py | 9 +- 5 files changed, 102 insertions(+), 764 deletions(-) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index c032f7b6b..eab7e90be 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -17,6 +17,7 @@ import io import os import logging +import random import struct import time from spinn_utilities.abstract_base import AbstractBase @@ -52,39 +53,16 @@ @require_subclass(ExtendableTransceiver) class ExtendedTransceiver(object, metaclass=AbstractBase): """ - An encapsulation of various communications with the SpiNNaker board. - - The methods of this class are designed to be thread-safe (provided they do - not access a BMP, as access to those is never thread-safe); - thus you can make multiple calls to the same (or different) methods - from multiple threads and expect each call to work as if it had been - called sequentially, although the order of returns is not guaranteed. - - .. note:: - With multiple connections to the board, using multiple threads in this - way may result in an increase in the overall speed of operation, since - the multiple calls may be made separately over the set of given - connections. - """ - __slots__ = [] + Allows a Transceiver to support extra method not currently needed. - def __init__(self, connections=None): - """ - :param list(Connection) connections: - An iterable of connections to the board. If not specified, no - communication will be possible until connections are found. - :raise SpinnmanIOException: - If there is an error communicating with the board, or if no - connections to the board can be found (if connections is ``None``) - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - super().__init__(connections) + All methods here are in danger of being removed if they become too hard + to support and many are untested so use at your own risk. + It is undetermined if these will work with Spin2 boards. + If any method here is considered important to keep please move it to + AbstractTransceiver + """ + __slots__ = [] def send_scp_message(self, message, connection=None): """ @@ -110,7 +88,8 @@ def send_scp_message(self, message, connection=None): If the response is not one of the expected codes """ if connection is None: - connection = self._get_random_connection(self._scamp_connections) + connection = self._scamp_connectio[random.randint( + 0, len(self._scamp_connectio) - 1)] connection.send_scp_request(message) def is_connected(self, connection=None): diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/abstract_transceiver.py index f64fdf893..77a15a684 100644 --- a/spinnman/transceiver/abstract_transceiver.py +++ b/spinnman/transceiver/abstract_transceiver.py @@ -887,3 +887,15 @@ def update_provenance_and_exit(self, x, y, p): :param int p: The processor on the core """ + + @abstractmethod + def where_is_xy(self, x, y): + """ + Attempts to get where_is_x_y info from the machine + + If no machine will do its best. + + :param int x: + :param int y: + :rtype: str + """ diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index bf89d8dfb..7086691a0 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -13,12 +13,15 @@ # limitations under the License. # pylint: disable=too-many-arguments +from collections import defaultdict import io import os +import random import struct from contextlib import contextmanager, suppress import logging import socket +from threading import Condition import time from spinn_utilities.abstract_base import ( AbstractBase, abstractproperty) @@ -90,15 +93,20 @@ class BaseTransceiver(ExtendableTransceiver, metaclass=AbstractBase): """ + A base for all the code shared by all Version of the Transciever. + """ __slots__ = [ "_all_connections", "_bmp_selector", "_bmp_connection", "_boot_send_connection", + "_chip_execute_lock_condition", + "_chip_execute_locks", "_height", "_iobuf_size", "_machine_off", + "_n_chip_execute_locks", "_scamp_connection_selector", "_scamp_connections", "_udp_scamp_connections", @@ -150,6 +158,15 @@ def __init__(self, connections=None): # The BMP connections self._bmp_connection = None + # A lock against single chip executions (entry is (x, y)) + # The condition should be acquired before the locks are + # checked or updated + # The write lock condition should also be acquired to avoid a flood + # fill during an individual chip execute + self._chip_execute_locks = defaultdict(Condition) + self._chip_execute_lock_condition = Condition() + self._n_chip_execute_locks = 0 + # build connection selectors for the processes. self._bmp_selector = None self._scamp_connection_selector = \ @@ -175,6 +192,7 @@ def scamp_connection_selector(self): def bmp_connection(self): return self._bmp_connection + @overrides(AbstractTransceiver.where_is_xy) def where_is_xy(self, x, y): try: if SpiNNManDataView.has_machine(): @@ -280,16 +298,11 @@ def _check_connection( break return None + @overrides(AbstractTransceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): - """ - Sends an SDP message using one of the connections. - - :param SDPMessage message: The message to send - :param SDPConnection connection: An optional connection to use - """ if connection is None: - connection_to_use = self._get_random_connection( - self._scamp_connections) + self._scamp_connectio[random.randint( + 0, len(self._scamp_connectio) - 1)] else: connection_to_use = connection connection_to_use.send_sdp_message(message) @@ -322,23 +335,8 @@ def _check_and_add_scamp_connections(self, x, y, ip_address): "Additional Ethernet connection on {} at chip {}, {} " "cannot be contacted", ip_address, x, y) + @overrides(AbstractTransceiver.discover_scamp_connections) def discover_scamp_connections(self): - """ - Find connections to the board and store these for future use. - - .. note:: - An exception will be thrown if no initial connections can be - found to the board. - - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # Currently, this only finds other UDP connections given a connection # that supports SCP - this is done via the machine if not self._scamp_connections: @@ -366,25 +364,8 @@ def discover_scamp_connections(self): self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) + @overrides(AbstractTransceiver.add_scamp_connections) def add_scamp_connections(self, connections): - """ - Check connections to the board and store these for future use. - - .. note:: - An exception will be thrown if no initial connections can be - found to the board. - - :param dict((int,int),str) connections: - Dict of (`x`,`y`) to IP address - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ for ((x, y), ip_address) in connections.items(): self._check_and_add_scamp_connections(x, y, ip_address) self._scamp_connection_selector = MostDirectConnectionSelector( @@ -420,22 +401,8 @@ def _get_machine_dimensions(self): 2)) return MachineDimensions(self._width, self._height) + @overrides(AbstractTransceiver.get_machine_details) def get_machine_details(self): - """ - Get the details of the machine made up of chips on a board and how - they are connected to each other. - - :return: A machine description - :rtype: ~spinn_machine.Machine - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # Get the width and height of the machine self._get_machine_dimensions() @@ -545,26 +512,8 @@ def _is_scamp_version_compabible(version): # version is irrelevant return version[1] > _SCAMP_VERSION[1] + @overrides(AbstractTransceiver.ensure_board_is_ready) def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): - """ - Ensure that the board is ready to interact with this version of the - transceiver. Boots the board if not already booted and verifies that - the version of SCAMP running is compatible with this transceiver. - - :param number_of_boards: - this parameter is deprecated and will be ignored - :param int n_retries: The number of times to retry booting - :param dict(SystemVariableDefinition,object) extra_boot_values: - Any additional or overwrite values to set during boot. - This should only be used for values which are not standard - based on the board version. - :return: The version identifier - :rtype: VersionInfo - :raise SpinnmanIOException: - * If there is a problem booting the board - * If the version of software on the board is not compatible with - this transceiver - """ # try to get a SCAMP version once logger.info("Working out if machine is booted") if self._machine_off: @@ -678,34 +627,9 @@ def _try_to_find_scamp_and_boot(self, tries_to_go, extra_boot_values): logger.info("Found board with version {}", version_info) return version_info + @overrides(AbstractTransceiver.get_cpu_infos) def get_cpu_infos( self, core_subsets=None, states=None, include=True): - """ - Get information about the processors on the board. - - :param ~spinn_machine.CoreSubsets core_subsets: - A set of chips and cores from which to get the - information. If not specified, the information from all of the - cores on all of the chips on the board are obtained. - :param states: The state or states to filter on (if any) - :type states: None, CPUState or collection(CPUState) - :param bool include: - If True includes only infos in the requested state(s). - If False includes only infos NOT in the requested state(s). - Ignored if states is None. - :return: The CPU information for the selected cores and States, or - all cores/states if core_subsets/states is not specified - :rtype: ~spinnman.model.CPUInfos - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If chip_and_cores contains invalid items - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # Get all the cores if the subsets are not given if core_subsets is None: core_subsets = CoreSubsets() @@ -731,12 +655,8 @@ def get_cpu_infos( cpu_info = process.get_cpu_info(core_subsets) return cpu_info + @overrides(AbstractTransceiver.get_clock_drift) def get_clock_drift(self, x, y): - """ - Get the clock drift - :param int x: The x-coordinate of the chip to get drift for - :param int y: The y-coordinate of the chip to get drift for - """ DRIFT_FP = 1 << 17 drift = self._get_sv_data(x, y, SystemVariableDefinition.clock_drift) @@ -778,76 +698,20 @@ def __get_user_register_address_from_core(p, user): return (get_vcpu_address(p) + CPU_USER_START_ADDRESS + CPU_USER_OFFSET * user) + @overrides(AbstractTransceiver.read_user) def read_user(self, x, y, p, user): - """ - Get the contents of the this user register for the given processor. - - .. note:: - Conventionally, user_0 usually holds the address of the table of - memory regions. - - :param int x: X coordinate of the chip - :param int y: Y coordinate of the chip - :param int p: Virtual processor identifier on the chip - :param int user: The user number to read data for - :rtype: int - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If x, y, p does not identify a valid processor - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ addr = self.__get_user_register_address_from_core(p, user) return self.read_word(x, y, addr) + @overrides(AbstractTransceiver.get_cpu_information_from_core) def get_cpu_information_from_core(self, x, y, p): - """ - Get information about a specific processor on the board. - - :param int x: The x-coordinate of the chip containing the processor - :param int y: The y-coordinate of the chip containing the processor - :param int p: The ID of the processor to get the information about - :return: The CPU information for the selected core - :rtype: CPUInfo - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If x, y, p is not a valid processor - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ core_subsets = CoreSubsets() core_subsets.add_processor(x, y, p) cpu_infos = self.get_cpu_infos(core_subsets) return cpu_infos.get_cpu_info(x, y, p) + @overrides(AbstractTransceiver.get_iobuf) def get_iobuf(self, core_subsets=None): - """ - Get the contents of the IOBUF buffer for a number of processors. - - :param ~spinn_machine.CoreSubsets core_subsets: - A set of chips and cores from which to get the buffers. If not - specified, the buffers from all of the cores on all of the chips - on the board are obtained. - :return: An iterable of the buffers, which may not be in the order - of core_subsets - :rtype: iterable(IOBuffer) - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If chip_and_cores contains invalid items - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # making the assumption that all chips have the same iobuf size. if self._iobuf_size is None: self._iobuf_size = self._get_sv_data( @@ -859,77 +723,28 @@ def get_iobuf(self, core_subsets=None): process = ReadIOBufProcess(self._scamp_connection_selector) return process.read_iobuf(self._iobuf_size, core_subsets) + @overrides(AbstractTransceiver.get_core_state_count) def get_core_state_count(self, app_id, state): - """ - Get a count of the number of cores which have a given state. - - :param int app_id: - The ID of the application from which to get the count. - :param CPUState state: The state count to get - :return: A count of the cores with the given status - :rtype: int - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If state is not a valid status - * If app_id is not a valid application ID - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ process = SendSingleCommandProcess(self._scamp_connection_selector) response = process.execute(CountState(app_id, state)) return response.count # pylint: disable=no-member + @contextmanager + def _flood_execute_lock(self): + """ + Get a lock for executing a flood fill of an executable. + """ + # Get the execute lock all together, so nothing can access it + with self._chip_execute_lock_condition: + # Wait until nothing is executing + self._chip_execute_lock_condition.wait_for( + lambda: self._n_chip_execute_locks < 1) + yield self._chip_execute_lock_condition + + @overrides(AbstractTransceiver.execute_flood) def execute_flood( self, core_subsets, executable, app_id, n_bytes=None, wait=False, is_filename=False): - """ - Start an executable running on multiple places on the board. This - will be optimised based on the selected cores, but it may still - require a number of communications with the board to execute. - - :param ~spinn_machine.CoreSubsets core_subsets: - Which cores on which chips to start the executable - :param executable: - The data that is to be executed. Should be one of the following: - - * An instance of RawIOBase - * A bytearray - * A filename of an executable (in which case `is_filename` must be - set to True) - :type executable: - ~io.RawIOBase or bytes or bytearray or str - :param int app_id: - The ID of the application with which to associate the executable - :param int n_bytes: - The size of the executable data in bytes. If not specified: - - * If `executable` is an RawIOBase, an error is raised - * If `executable` is a bytearray, the length of the bytearray will - be used - * If `executable` is an int, 4 will be used - * If `executable` is a str, the length of the file will be used - :param bool wait: - True if the processors should enter a "wait" state on loading - :param bool is_filename: True if the data is a filename - :raise SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the executable - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If one of the specified cores is not valid - * If `app_id` is an invalid application ID - * If a packet is received that has invalid parameters - * If `executable` is an RawIOBase but `n_bytes` is not specified - * If `executable` is an int and `n_bytes` is more than 4 - * If `n_bytes` is less than 0 - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # Lock against other executable's with self._flood_execute_lock(): # Flood fill the system with the binary @@ -963,21 +778,12 @@ def _power_on_machine(self): self.power_on(self._bmp_connection) return True + @overrides(AbstractTransceiver.power_on) def power_on(self, boards=0): - """ - Power on a set of boards in the machine. - - :param int boards: The board or boards to power on - """ self._power(PowerCommand.POWER_ON, boards) + @overrides(AbstractTransceiver.power_off_machine) def power_off_machine(self): - """ - Power off the whole machine. - - :rtype bool - :return success or failure to power off the machine - """ if self._bmp_connection is None: logger.warning("No BMP connections, so can't power off") return False @@ -985,12 +791,8 @@ def power_off_machine(self): self.power_off(self._bmp_connection) return True + @overrides(AbstractTransceiver.power_off) def power_off(self, boards=0): - """ - Power off a set of boards in the machine. - - :param int boards: The board or boards to power off - """ self._power(PowerCommand.POWER_OFF, boards) def _power(self, power_command, boards=0): @@ -1014,101 +816,29 @@ def _power(self, power_command, boards=0): if not self._machine_off: time.sleep(BMP_POST_POWER_ON_SLEEP_TIME) + @overrides(AbstractTransceiver.read_fpga_register) def read_fpga_register( self, fpga_num, register, board=0): - """ - Read a register on a FPGA of a board. The meaning of the - register's contents will depend on the FPGA's configuration. - - :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. - :param int register: - Register address to read to (will be rounded down to - the nearest 32-bit word boundary). - :param int board: which board to request the FPGA register from - :return: the register data - :rtype: int - """ process = SendSingleCommandProcess(self._bmp_selector, timeout=1.0) response = process.execute( ReadFPGARegister(fpga_num, register, board)) return response.fpga_register # pylint: disable=no-member + @overrides(AbstractTransceiver.write_fpga_register) def write_fpga_register(self, fpga_num, register, value, board=0): - """ - Write a register on a FPGA of a board. The meaning of setting the - register's contents will depend on the FPGA's configuration. - - :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. - :param int register: - Register address to read to (will be rounded down to - the nearest 32-bit word boundary). - :param int value: the value to write into the FPGA register - :param int board: which board to write the FPGA register to - """ process = SendSingleCommandProcess(self._bmp_selector) process.execute( WriteFPGARegister(fpga_num, register, value, board)) + @overrides(AbstractTransceiver.read_bmp_version) def read_bmp_version(self, board): - """ - Read the BMP version. - - :param int board: which board to request the data from - :return: the sver from the BMP - """ process = SendSingleCommandProcess(self._bmp_selector) response = process.execute(BMPGetVersion(board)) return response.version_info # pylint: disable=no-member + @overrides(AbstractTransceiver.write_memory) def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, cpu=0, is_filename=False, get_sum=False): - """ - Write to the SDRAM on the board. - - :param int x: - The x-coordinate of the chip where the memory is to be written to - :param int y: - The y-coordinate of the chip where the memory is to be written to - :param int base_address: - The address in SDRAM where the region of memory is to be written - :param data: The data to write. Should be one of the following: - - * An instance of RawIOBase - * A bytearray/bytes - * A single integer - will be written in little-endian byte order - * A filename of a data file (in which case `is_filename` must be - set to True) - :type data: - ~io.RawIOBase or bytes or bytearray or int or str - :param int n_bytes: - The amount of data to be written in bytes. If not specified: - - * If `data` is an RawIOBase, an error is raised - * If `data` is a bytearray, the length of the bytearray will be - used - * If `data` is an int, 4 will be used - * If `data` is a str, the length of the file will be used - :param int offset: The offset from which the valid data begins - :param int cpu: The optional CPU to write to - :param bool is_filename: True if `data` is a filename - :param bool get_sum: whether to return a checksum or 0 - :return: The number of bytes written, the checksum (0 if get_sum=False) - :rtype: int, int - :raise SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the data - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If `x, y` does not lead to a valid chip - * If a packet is received that has invalid parameters - * If `base_address` is not a positive integer - * If `data` is an RawIOBase but `n_bytes` is not specified - * If `data` is an int and `n_bytes` is more than 4 - * If `n_bytes` is less than 0 - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ process = WriteMemoryProcess(self._scamp_connection_selector) if isinstance(data, io.RawIOBase): chksum = process.write_memory_from_reader( @@ -1131,89 +861,22 @@ def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, x, y, cpu, base_address, data, offset, n_bytes, get_sum) return n_bytes, chksum + @overrides(AbstractTransceiver.write_user) def write_user(self, x, y, p, user, value): - """ - Write to the this user register for the given processor. - - .. note:: - Conventionally, user_0 usually holds the address of the table of - memory regions. - - :param int x: X coordinate of the chip - :param int y: Y coordinate of the chip - :param int p: Virtual processor identifier on the chip - :param int user: The user number of write data for - :param int value: The value to write - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If x, y, p does not identify a valid processor - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ addr = self.__get_user_register_address_from_core(p, user) self.write_memory(x, y, addr, int(value)) + @overrides(AbstractTransceiver.read_memory) def read_memory(self, x, y, base_address, length, cpu=0): - """ - Read some areas of memory (usually SDRAM) from the board. - - :param int x: - The x-coordinate of the chip where the memory is to be read from - :param int y: - The y-coordinate of the chip where the memory is to be read from - :param int base_address: - The address in SDRAM where the region of memory to be read starts - :param int length: The length of the data to be read in bytes - :param int cpu: - the core ID used to read the memory of; should usually be 0 when - reading from SDRAM, but may be other values when reading from DTCM. - :return: A bytearray of data read - :rtype: bytes - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If one of `x`, `y`, `cpu`, `base_address` or `length` is invalid - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = ReadMemoryProcess(self._scamp_connection_selector) return process.read_memory(x, y, cpu, base_address, length) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.read_word) def read_word(self, x, y, base_address, cpu=0): - """ - Read a word (usually of SDRAM) from the board. - - :param int x: - The x-coordinate of the chip where the word is to be read from - :param int y: - The y-coordinate of the chip where the word is to be read from - :param int base_address: - The address (usually in SDRAM) where the word to be read starts - :param int cpu: - the core ID used to read the word; should usually be 0 when reading - from SDRAM, but may be other values when reading from DTCM. - :return: The unsigned integer value at ``base_address`` - :rtype: int - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If one of `x`, `y`, `cpu` or `base_address` is invalid - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = ReadMemoryProcess(self._scamp_connection_selector) data = process.read_memory(x, y, cpu, base_address, _ONE_WORD.size) @@ -1223,22 +886,8 @@ def read_word(self, x, y, base_address, cpu=0): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.stop_application) def stop_application(self, app_id): - """ - Sends a stop request for an app_id. - - :param int app_id: The ID of the application to send to - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If app_id is not a valid application ID - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - if not self._machine_off: process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(AppStop(app_id)) @@ -1262,37 +911,13 @@ def __log_where_is_info(self, cpu_infos): for (x, y) in xys: logger.info(self._where_is_xy(x, y)) + @overrides(AbstractTransceiver.wait_for_cores_to_be_in_state) def wait_for_cores_to_be_in_state( self, all_core_subsets, app_id, cpu_states, timeout=None, time_between_polls=0.1, error_states=frozenset({ CPUState.RUN_TIME_EXCEPTION, CPUState.WATCHDOG}), counts_between_full_check=100, progress_bar=None): - """ - Waits for the specified cores running the given application to be - in some target state or states. Handles failures. - - :param ~spinn_machine.CoreSubsets all_core_subsets: - the cores to check are in a given sync state - :param int app_id: the application ID that being used by the simulation - :param set(CPUState) cpu_states: - The expected states once the applications are ready; success is - when each application is in one of these states - :param float timeout: - The amount of time to wait in seconds for the cores to reach one - of the states - :param float time_between_polls: Time between checking the state - :param set(CPUState) error_states: - Set of states that the application can be in that indicate an - error, and so should raise an exception - :param int counts_between_full_check: - The number of times to use the count signal before instead using - the full CPU state check - :param progress_bar: Possible progress bar to update. - :type progress_bar: ~spinn_utilities.progress_bar.ProgressBar or None - :raise SpinnmanTimeoutException: - If a timeout is specified and exceeded. - """ # check that the right number of processors are in the states processors_ready = 0 max_processors_ready = 0 @@ -1362,23 +987,8 @@ def wait_for_cores_to_be_in_state( raise SpiNNManCoresNotInStateException( timeout, cpu_states, cores_not_in_state) + @overrides(AbstractTransceiver.send_signal) def send_signal(self, app_id, signal): - """ - Send a signal to an application. - - :param int app_id: The ID of the application to send to - :param ~spinnman.messages.scp.enums.Signal signal: The signal to send - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If signal is not a valid signal - * If app_id is not a valid application ID - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(SendSignal(app_id, signal)) @@ -1394,29 +1004,8 @@ def _locate_spinnaker_connection_for_board_address(self, board_address): """ return self._udp_scamp_connections.get(board_address, None) + @overrides(AbstractTransceiver.set_ip_tag) def set_ip_tag(self, ip_tag, use_sender=False): - """ - Set up an IP tag. - - :param ~spinn_machine.tags.IPTag ip_tag: - The tag to set up. - - .. note:: - `board_address` can be `None`, in which case, the tag will be - assigned to all boards. - :param bool use_sender: - Optionally use the sender host and port instead of - the given host and port in the tag - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If the IP tag fields are incorrect - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ # Check that the tag has a port assigned if ip_tag.port is None: raise SpinnmanInvalidParameterException( @@ -1466,28 +1055,8 @@ def __get_connection_list(self, connection=None, board_address=None): return [] return [connection] + @overrides(AbstractTransceiver.set_reverse_ip_tag) def set_reverse_ip_tag(self, reverse_ip_tag): - """ - Set up a reverse IP tag. - - :param ~spinn_machine.tags.ReverseIPTag reverse_ip_tag: - The reverse tag to set up. - - .. note:: - The `board_address` field can be `None`, in which case, the tag - will be assigned to all boards. - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If the reverse IP tag fields are incorrect - * If a packet is received that has invalid parameters - * If the UDP port is one that is already used by SpiNNaker for - system functions - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ if reverse_ip_tag.port is None: raise SpinnmanInvalidParameterException( "reverse_ip_tag.port", "None", @@ -1519,71 +1088,22 @@ def set_reverse_ip_tag(self, reverse_ip_tag): reverse_ip_tag.port, reverse_ip_tag.tag, reverse_ip_tag.sdp_port)) + @overrides(AbstractTransceiver.clear_ip_tag) def clear_ip_tag(self, tag, board_address=None): - """ - Clear the setting of an IP tag. - - :param int tag: The tag ID - :param str board_address: - Board address where the tag should be cleared. - If not specified, all AbstractSCPConnection connections will send - the message to clear the tag - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If the tag is not a valid tag - * If the connection cannot send SDP messages - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ for conn in self.__get_connection_list(board_address=board_address): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(IPTagClear(conn.chip_x, conn.chip_y, tag)) + @overrides(AbstractTransceiver.get_tags) def get_tags(self, connection=None): - """ - Get the current set of tags that have been set on the board. - - :param AbstractSCPConnection connection: - Connection from which the tags should be received. - If not specified, all AbstractSCPConnection connections will be - queried and the response will be combined. - :return: An iterable of tags - :rtype: iterable(~spinn_machine.tags.AbstractTag) - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If the connection cannot send SDP messages - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ all_tags = list() for conn in self.__get_connection_list(connection): process = GetTagsProcess(self._scamp_connection_selector) all_tags.extend(process.get_tags(conn)) return all_tags + @overrides(AbstractTransceiver.malloc_sdram) def malloc_sdram(self, x, y, size, app_id, tag=None): - """ - Allocates a chunk of SDRAM on a chip on the machine. - - :param int x: The x-coordinate of the chip onto which to ask for memory - :param int y: The y-coordinate of the chip onto which to ask for memory - :param int size: the amount of memory to allocate in bytes - :param int app_id: The ID of the application with which to associate - the routes. If not specified, defaults to 0. - :param int tag: the tag for the SDRAM, a 8-bit (chip-wide) tag that can - be looked up by a SpiNNaker application to discover the address of - the allocated block. If `0` then no tag is applied. - :return: the base address of the allocated memory - :rtype: int - """ try: process = MallocSDRAMProcess(self._scamp_connection_selector) process.malloc_sdram(x, y, size, app_id, tag) @@ -1592,28 +1112,8 @@ def malloc_sdram(self, x, y, size, app_id, tag=None): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.load_multicast_routes) def load_multicast_routes(self, x, y, routes, app_id): - """ - Load a set of multicast routes on to a chip. - - :param int x: - The x-coordinate of the chip onto which to load the routes - :param int y: - The y-coordinate of the chip onto which to load the routes - :param iterable(~spinn_machine.MulticastRoutingEntry) routes: - An iterable of multicast routes to load - :param int app_id: The ID of the application with which to associate - the routes. If not specified, defaults to 0. - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If any of the routes are invalid - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = LoadMultiCastRoutesProcess( self._scamp_connection_selector) @@ -1622,28 +1122,8 @@ def load_multicast_routes(self, x, y, routes, app_id): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.load_fixed_route) def load_fixed_route(self, x, y, fixed_route, app_id): - """ - Loads a fixed route routing table entry onto a chip's router. - - :param int x: - The x-coordinate of the chip onto which to load the routes - :param int y: - The y-coordinate of the chip onto which to load the routes - :param ~spinn_machine.FixedRouteEntry fixed_route: - the route for the fixed route entry on this chip - :param int app_id: The ID of the application with which to associate - the routes. If not specified, defaults to 0. - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If any of the routes are invalid - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = LoadFixedRouteRoutingEntryProcess( self._scamp_connection_selector) @@ -1652,19 +1132,8 @@ def load_fixed_route(self, x, y, fixed_route, app_id): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.read_fixed_route) def read_fixed_route(self, x, y, app_id): - """ - Reads a fixed route routing table entry from a chip's router. - - :param int x: - The x-coordinate of the chip onto which to load the routes - :param int y: - The y-coordinate of the chip onto which to load the routes - :param int app_id: - The ID of the application with which to associate the - routes. If not specified, defaults to 0. - :return: the route as a fixed route entry - """ try: process = ReadFixedRouteRoutingEntryProcess( self._scamp_connection_selector) @@ -1673,28 +1142,8 @@ def read_fixed_route(self, x, y, app_id): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.get_multicast_routes) def get_multicast_routes(self, x, y, app_id=None): - """ - Get the current multicast routes set up on a chip. - - :param int x: - The x-coordinate of the chip from which to get the routes - :param int y: - The y-coordinate of the chip from which to get the routes - :param int app_id: - The ID of the application to filter the routes for. If - not specified, will return all routes - :return: An iterable of multicast routes - :rtype: list(~spinn_machine.MulticastRoutingEntry) - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: base_address = self._get_sv_data( x, y, SystemVariableDefinition.router_table_copy_address) @@ -1705,21 +1154,8 @@ def get_multicast_routes(self, x, y, app_id=None): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.clear_multicast_routes) def clear_multicast_routes(self, x, y): - """ - Remove all the multicast routes on a chip. - - :param int x: The x-coordinate of the chip on which to clear the routes - :param int y: The y-coordinate of the chip on which to clear the routes - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(RouterClear(x, y)) @@ -1727,25 +1163,8 @@ def clear_multicast_routes(self, x, y): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.get_router_diagnostics) def get_router_diagnostics(self, x, y): - """ - Get router diagnostic information from a chip. - - :param int x: - The x-coordinate of the chip from which to get the information - :param int y: - The y-coordinate of the chip from which to get the information - :return: The router diagnostic information - :rtype: RouterDiagnostics - :raise SpinnmanIOException: - If there is an error communicating with the board - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: process = ReadRouterDiagnosticsProcess( self._scamp_connection_selector) @@ -1754,34 +1173,8 @@ def get_router_diagnostics(self, x, y): logger.info(self._where_is_xy(x, y)) raise + @overrides(AbstractTransceiver.set_router_diagnostic_filter) def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): - """ - Sets a router diagnostic filter in a router. - - :param int x: - The X address of the router in which this filter is being set. - :param int y: - The Y address of the router in which this filter is being set. - :param int position: - The position in the list of filters where this filter is to be - added. - :param ~spinnman.model.DiagnosticFilter diagnostic_filter: - The diagnostic filter being set in the placed, between 0 and 15. - - .. note:: - Positions 0 to 11 are used by the default filters, - and setting these positions will result in a warning. - :raise SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the data - :raise SpinnmanInvalidPacketException: - If a packet is received that is not in the valid format - :raise SpinnmanInvalidParameterException: - * If x, y does not lead to a valid chip - * If position is less than 0 or more than 15 - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ try: self.__set_router_diagnostic_filter( x, y, position, diagnostic_filter) diff --git a/spinnman/transceiver/extendable_transceiver.py b/spinnman/transceiver/extendable_transceiver.py index b024aba83..217ecb5d8 100644 --- a/spinnman/transceiver/extendable_transceiver.py +++ b/spinnman/transceiver/extendable_transceiver.py @@ -15,9 +15,7 @@ # pylint: disable=too-many-arguments from contextlib import contextmanager, suppress -from collections import defaultdict import logging -import random from threading import Condition, RLock from spinn_utilities.abstract_base import ( AbstractBase, abstractproperty, abstractmethod) @@ -29,26 +27,19 @@ class ExtendableTransceiver(AbstractTransceiver, metaclass=AbstractBase): """ + Support Functions to allow a Transceiver to also be an ExtendedTransceiver + If supporting these is too difficult the + methods that require these may be removed. """ __slots__ = [ - "_chip_execute_lock_condition", - "_chip_execute_locks", "_flood_write_lock", - "_n_chip_execute_locks", "_nearest_neighbour_id", "_nearest_neighbour_lock" ] def __init__(self): - # A lock against single chip executions (entry is (x, y)) - # The condition should be acquired before the locks are - # checked or updated - # The write lock condition should also be acquired to avoid a flood - # fill during an individual chip execute - self._chip_execute_locks = defaultdict(Condition) - self._chip_execute_lock_condition = Condition() - self._n_chip_execute_locks = 0 + # A lock against multiple flood fill writes - needed as SCAMP cannot # cope with this self._flood_write_lock = Condition() @@ -79,41 +70,3 @@ def scamp_connection_selector(self): :rtype: AbstractMultiConnectionProcessConnectionSelector """ - - @abstractmethod - def where_is_xy(self, x, y): - """ - Attempts to get where_is_x_y info from the machine - - If no machine will do its best. - - :param int x: - :param int y: - :rtype: str - """ - - @contextmanager - def _flood_execute_lock(self): - """ - Get a lock for executing a flood fill of an executable. - """ - # Get the execute lock all together, so nothing can access it - with self._chip_execute_lock_condition: - # Wait until nothing is executing - self._chip_execute_lock_condition.wait_for( - lambda: self._n_chip_execute_locks < 1) - yield self._chip_execute_lock_condition - - @staticmethod - def _get_random_connection(connections): - """ - Returns the given connection, or else picks one at random. - - :param list(Connection) connections: - the list of connections to locate a random one from - :return: a connection object - :rtype: Connection or None - """ - if not connections: - return None - return connections[random.randint(0, len(connections) - 1)] diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 4d5b992ea..f165c75d1 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -28,6 +28,7 @@ class MockableTransceiver(ExtendableTransceiver): __slots__ = ["written_memory"] def __init__(self): + super().__init__() self.written_memory = list() @overrides(AbstractTransceiver.send_sdp_message) @@ -221,6 +222,10 @@ def control_sync(self, do_sync): def update_provenance_and_exit(self, x, y, p): pass + @overrides(AbstractTransceiver.where_is_xy) + def where_is_xy(self, x, y): + return f"Mocked {x=} {y=}" + @overrides(ExtendableTransceiver.bmp_connection) def bmp_connection(self): raise NotImplementedError("Needs to be mocked") @@ -232,7 +237,3 @@ def bmp_selector(self): @overrides(ExtendableTransceiver.scamp_connection_selector) def scamp_connection_selector(self): raise NotImplementedError("Needs to be mocked") - - @overrides(ExtendableTransceiver.where_is_xy) - def where_is_xy(self, x, y): - return f"Mocked {x=} {y=}" From 9b055cca690907e063ed4e440b7f441a079cfe18 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 14:55:06 +0100 Subject: [PATCH 12/39] flake8 --- spinnman/transceiver/extendable_transceiver.py | 4 +--- unittests/test_transceiver.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/spinnman/transceiver/extendable_transceiver.py b/spinnman/transceiver/extendable_transceiver.py index 217ecb5d8..e0189519e 100644 --- a/spinnman/transceiver/extendable_transceiver.py +++ b/spinnman/transceiver/extendable_transceiver.py @@ -13,12 +13,10 @@ # limitations under the License. # pylint: disable=too-many-arguments -from contextlib import contextmanager, suppress - import logging from threading import Condition, RLock from spinn_utilities.abstract_base import ( - AbstractBase, abstractproperty, abstractmethod) + AbstractBase, abstractproperty) from spinn_utilities.log import FormatAdapter from spinnman.transceiver.abstract_transceiver import AbstractTransceiver diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index b73c89fd8..b444bbe46 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -25,8 +25,7 @@ from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( SystemVariableDefinition) -from spinnman.connections.udp_packet_connections import ( - BootConnection, SCAMPConnection) +from spinnman.connections.udp_packet_connections import SCAMPConnection from spinnman.board_test_configuration import BoardTestConfiguration From 33b44e6cc2f71f0f7ce893d03ea0ad0e76e6bccb Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 16:35:11 +0100 Subject: [PATCH 13/39] fixes found by pylint --- spinnman/transceiver/base_transceiver.py | 26 ++++++++++---------- spinnman/transceiver/mockable_transceiver.py | 3 +++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 7086691a0..7a9bebac8 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -301,8 +301,8 @@ def _check_connection( @overrides(AbstractTransceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): if connection is None: - self._scamp_connectio[random.randint( - 0, len(self._scamp_connectio) - 1)] + self._scamp_connection[random.randint( + 0, len(self._scamp_connection) - 1)] else: connection_to_use = connection connection_to_use.send_sdp_message(message) @@ -883,7 +883,7 @@ def read_word(self, x, y, base_address, cpu=0): (value, ) = _ONE_WORD.unpack(data) return value except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.stop_application) @@ -909,7 +909,7 @@ def __log_where_is_info(self, cpu_infos): else: xys.add((cpu_info.x, cpu_info.y)) for (x, y) in xys: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) @overrides(AbstractTransceiver.wait_for_cores_to_be_in_state) def wait_for_cores_to_be_in_state( @@ -1109,7 +1109,7 @@ def malloc_sdram(self, x, y, size, app_id, tag=None): process.malloc_sdram(x, y, size, app_id, tag) return process.base_address except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.load_multicast_routes) @@ -1119,7 +1119,7 @@ def load_multicast_routes(self, x, y, routes, app_id): self._scamp_connection_selector) process.load_routes(x, y, routes, app_id) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.load_fixed_route) @@ -1129,7 +1129,7 @@ def load_fixed_route(self, x, y, fixed_route, app_id): self._scamp_connection_selector) process.load_fixed_route(x, y, fixed_route, app_id) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.read_fixed_route) @@ -1139,7 +1139,7 @@ def read_fixed_route(self, x, y, app_id): self._scamp_connection_selector) return process.read_fixed_route(x, y, app_id) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.get_multicast_routes) @@ -1151,7 +1151,7 @@ def get_multicast_routes(self, x, y, app_id=None): self._scamp_connection_selector, app_id) return process.get_routes(x, y, base_address) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.clear_multicast_routes) @@ -1160,7 +1160,7 @@ def clear_multicast_routes(self, x, y): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(RouterClear(x, y)) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.get_router_diagnostics) @@ -1170,7 +1170,7 @@ def get_router_diagnostics(self, x, y): self._scamp_connection_selector) return process.get_router_diagnostics(x, y) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise @overrides(AbstractTransceiver.set_router_diagnostic_filter) @@ -1179,7 +1179,7 @@ def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): self.__set_router_diagnostic_filter( x, y, position, diagnostic_filter) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def __set_router_diagnostic_filter( @@ -1227,7 +1227,7 @@ def clear_router_diagnostic_counters(self, x, y): process.execute(WriteMemory( x, y, 0xf100002c, _ONE_WORD.pack(0xFFFFFFFF))) except Exception: - logger.info(self._where_is_xy(x, y)) + logger.info(self.where_is_xy(x, y)) raise def close(self): diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index f165c75d1..1dc4e3543 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -226,14 +226,17 @@ def update_provenance_and_exit(self, x, y, p): def where_is_xy(self, x, y): return f"Mocked {x=} {y=}" + @property @overrides(ExtendableTransceiver.bmp_connection) def bmp_connection(self): raise NotImplementedError("Needs to be mocked") + @property @overrides(ExtendableTransceiver.bmp_selector) def bmp_selector(self): raise NotImplementedError("Needs to be mocked") + @property @overrides(ExtendableTransceiver.scamp_connection_selector) def scamp_connection_selector(self): raise NotImplementedError("Needs to be mocked") From ac116bf631f75213a57b4f33037069438b57886c Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 16:41:55 +0100 Subject: [PATCH 14/39] more error fixing --- spinnman/transceiver/base_transceiver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 7a9bebac8..4a53d9df9 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -301,8 +301,8 @@ def _check_connection( @overrides(AbstractTransceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): if connection is None: - self._scamp_connection[random.randint( - 0, len(self._scamp_connection) - 1)] + self._scamp_connections[random.randint( + 0, len(self._scamp_connections) - 1)] else: connection_to_use = connection connection_to_use.send_sdp_message(message) From 7b19de4838bd047b7b43b8334f0912a21730b925 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 16:49:36 +0100 Subject: [PATCH 15/39] another point to pylint --- spinnman/transceiver/base_transceiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 4a53d9df9..ee6334feb 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -301,7 +301,7 @@ def _check_connection( @overrides(AbstractTransceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): if connection is None: - self._scamp_connections[random.randint( + sconnection_to_use = self._scamp_connections[random.randint( 0, len(self._scamp_connections) - 1)] else: connection_to_use = connection From 1b60f228e90622b8c61a7104625a795df40b97fb Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 16:53:08 +0100 Subject: [PATCH 16/39] fix sillyness --- spinnman/transceiver/base_transceiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index ee6334feb..1995d744c 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -301,7 +301,7 @@ def _check_connection( @overrides(AbstractTransceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): if connection is None: - sconnection_to_use = self._scamp_connections[random.randint( + connection_to_use = self._scamp_connections[random.randint( 0, len(self._scamp_connections) - 1)] else: connection_to_use = connection From f3373aae5f4347cbc5b7dba7a785265c036472a1 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 16:58:56 +0100 Subject: [PATCH 17/39] no need to pass --- spinnman/transceiver/version5transceiver.py | 1 - 1 file changed, 1 deletion(-) diff --git a/spinnman/transceiver/version5transceiver.py b/spinnman/transceiver/version5transceiver.py index 29c92532b..e1fc82455 100644 --- a/spinnman/transceiver/version5transceiver.py +++ b/spinnman/transceiver/version5transceiver.py @@ -23,7 +23,6 @@ class Version5Transceiver(BaseTransceiver): This class should ONLY be created via by transceiver_factory.py """ - pass @overrides(BaseTransceiver.__init__) def __init__(self, connections=None): From 2fa395f9e01f1f52a6c0593a0bb3dbd71237407e Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 17:08:48 +0100 Subject: [PATCH 18/39] pylint: disable=no_member --- spinnman/extended/extended_transceiver.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index eab7e90be..6738c6d4d 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -64,6 +64,9 @@ class ExtendedTransceiver(object, metaclass=AbstractBase): """ __slots__ = [] + # calls many methods only reachable do to require_subclass + # pylint: disable=no_member + def send_scp_message(self, message, connection=None): """ Sends an SCP message, without expecting a response. @@ -88,8 +91,8 @@ def send_scp_message(self, message, connection=None): If the response is not one of the expected codes """ if connection is None: - connection = self._scamp_connectio[random.randint( - 0, len(self._scamp_connectio) - 1)] + connection = self.scamp_connections[random.randint( + 0, len(self.scamp_connections) - 1)] connection.send_scp_request(message) def is_connected(self, connection=None): @@ -107,7 +110,7 @@ def is_connected(self, connection=None): if connection is not None: return connection.is_connected() return any(c.is_connected() and isinstance(c, SCAMPConnection) - for c in self._scamp_connections) + for c in self.scamp_connections) def get_iobuf_from_core(self, x, y, p): """ From 8ba7d547cd09f23f113a875d179e9339befe6fbc Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 17:19:06 +0100 Subject: [PATCH 19/39] pylint: disable=no-member --- spinnman/extended/extended_transceiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 6738c6d4d..3450527ed 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -65,7 +65,7 @@ class ExtendedTransceiver(object, metaclass=AbstractBase): __slots__ = [] # calls many methods only reachable do to require_subclass - # pylint: disable=no_member + # pylint: disable=no-member def send_scp_message(self, message, connection=None): """ From e45356705e156ae0f1145b58915330bfb69e45f4 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 17:25:28 +0100 Subject: [PATCH 20/39] more pylint disables --- spinnman/extended/extended_transceiver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 3450527ed..14de00f44 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -65,7 +65,8 @@ class ExtendedTransceiver(object, metaclass=AbstractBase): __slots__ = [] # calls many methods only reachable do to require_subclass - # pylint: disable=no-member + # pylint: disable=no-member,assigning-non-slot + # pylint: disable=access-member-before-definition def send_scp_message(self, message, connection=None): """ From 87c14d1a1e12849c93c0eaae330d2ea76e56f45c Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 4 Aug 2023 17:38:53 +0100 Subject: [PATCH 21/39] still more disables --- spinnman/extended/extended_transceiver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 14de00f44..453499169 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -67,6 +67,7 @@ class ExtendedTransceiver(object, metaclass=AbstractBase): # calls many methods only reachable do to require_subclass # pylint: disable=no-member,assigning-non-slot # pylint: disable=access-member-before-definition + # pylint: disable=attribute-defined-outside-init def send_scp_message(self, message, connection=None): """ From 56f4ddf7594b20c070be861c332a1b8682e4d054 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 10:13:47 +0100 Subject: [PATCH 22/39] remane AbstractTransceiver to just Transceiver --- spinnman/data/spinnman_data_writer.py | 10 +-- spinnman/extended/extended_transceiver.py | 2 +- spinnman/spalloc/spalloc_client.py | 4 +- spinnman/spalloc/spalloc_job.py | 6 +- spinnman/transceiver/__init__.py | 4 +- spinnman/transceiver/base_transceiver.py | 80 ++++++++--------- .../transceiver/extendable_transceiver.py | 4 +- spinnman/transceiver/mockable_transceiver.py | 88 +++++++++---------- ...abstract_transceiver.py => transceiver.py} | 2 +- spinnman/transceiver/transceiver_factory.py | 4 +- 10 files changed, 102 insertions(+), 102 deletions(-) rename spinnman/transceiver/{abstract_transceiver.py => transceiver.py} (99%) diff --git a/spinnman/data/spinnman_data_writer.py b/spinnman/data/spinnman_data_writer.py index ef76cfa1d..bfd4ad35f 100644 --- a/spinnman/data/spinnman_data_writer.py +++ b/spinnman/data/spinnman_data_writer.py @@ -16,7 +16,7 @@ from spinn_utilities.log import FormatAdapter from spinn_utilities.overrides import overrides from spinn_machine.data.machine_data_writer import MachineDataWriter -from spinnman.transceiver import AbstractTransceiver +from spinnman.transceiver import Transceiver from .spinnman_data_view import _SpiNNManDataModel, SpiNNManDataView logger = FormatAdapter(logging.getLogger(__name__)) @@ -98,11 +98,11 @@ def set_transceiver(self, transceiver): """ Sets the transceiver object. - :param AbstractTransceiver transceiver: - :raises TypeError: If the transceiver is not a AbstractTransceiver + :param Transceiver transceiver: + :raises TypeError: If the transceiver is not a Transceiver subclass """ - if not isinstance(transceiver, AbstractTransceiver): - raise TypeError("transceiver should be a AbstractTransceiver") + if not isinstance(transceiver, Transceiver): + raise TypeError("transceiver should be a Transceiver subclass") if self.__data._transceiver: raise NotImplementedError( "Over writing and existing transceiver not supported") diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py index 453499169..06e31653b 100644 --- a/spinnman/extended/extended_transceiver.py +++ b/spinnman/extended/extended_transceiver.py @@ -60,7 +60,7 @@ class ExtendedTransceiver(object, metaclass=AbstractBase): It is undetermined if these will work with Spin2 boards. If any method here is considered important to keep please move it to - AbstractTransceiver + Transceiver and its implementations """ __slots__ = [] diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index bcff0f9f4..5307b7e2d 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -36,7 +36,7 @@ from spinnman.exceptions import SpinnmanTimeoutException from spinnman.exceptions import SpallocException from spinnman.transceiver import ( - AbstractTransceiver, create_transceiver_from_connections) + Transceiver, create_transceiver_from_connections) from .spalloc_state import SpallocState from .proxy_protocol import ProxyProtocol from .session import Session, SessionAware @@ -636,7 +636,7 @@ def _keepalive_handle(self, handle): self.__keepalive_handle = handle @overrides(SpallocJob.create_transceiver) - def create_transceiver(self) -> AbstractTransceiver: + def create_transceiver(self) -> Transceiver: if self.get_state() != SpallocState.READY: raise SpallocException("job not ready to execute scripts") proxies = [ diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py index c268dcb96..8685f1c2f 100644 --- a/spinnman/spalloc/spalloc_job.py +++ b/spinnman/spalloc/spalloc_job.py @@ -17,7 +17,7 @@ from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.constants import SCP_SCAMP_PORT -from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.abstract_transceiver import Transceiver from spinnman.connections.udp_packet_connections import UDPConnection from .spalloc_state import SpallocState from .spalloc_boot_connection import SpallocBootConnection @@ -121,12 +121,12 @@ def open_udp_listener_connection(self) -> UDPConnection: """ @abstractmethod - def create_transceiver(self) -> AbstractTransceiver: + def create_transceiver(self) -> Transceiver: """ Create a transceiver that will talk to this job. The transceiver will only be configured to talk to the SCP ports of the boards of the job. - :rtype: AbstractTransceiver + :rtype: Transceiver """ @abstractmethod diff --git a/spinnman/transceiver/__init__.py b/spinnman/transceiver/__init__.py index 5279a28a4..3f96f166a 100644 --- a/spinnman/transceiver/__init__.py +++ b/spinnman/transceiver/__init__.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .abstract_transceiver import AbstractTransceiver +from .transceiver import Transceiver from .mockable_transceiver import MockableTransceiver from .transceiver_factory import ( create_transceiver_from_connections, create_transceiver_from_hostname) -__all__ = ["AbstractTransceiver", "create_transceiver_from_connections", +__all__ = ["Transceiver", "create_transceiver_from_connections", "create_transceiver_from_hostname", "MockableTransceiver"] diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 1995d744c..7ab57f672 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -70,7 +70,7 @@ SendSingleCommandProcess, ReadRouterDiagnosticsProcess, MostDirectConnectionSelector, ApplicationCopyRunProcess) from spinnman.utilities.utility_functions import get_vcpu_address -from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.abstract_transceiver import Transceiver from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver logger = FormatAdapter(logging.getLogger(__name__)) @@ -192,7 +192,7 @@ def scamp_connection_selector(self): def bmp_connection(self): return self._bmp_connection - @overrides(AbstractTransceiver.where_is_xy) + @overrides(Transceiver.where_is_xy) def where_is_xy(self, x, y): try: if SpiNNManDataView.has_machine(): @@ -298,7 +298,7 @@ def _check_connection( break return None - @overrides(AbstractTransceiver.send_sdp_message) + @overrides(Transceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): if connection is None: connection_to_use = self._scamp_connections[random.randint( @@ -335,7 +335,7 @@ def _check_and_add_scamp_connections(self, x, y, ip_address): "Additional Ethernet connection on {} at chip {}, {} " "cannot be contacted", ip_address, x, y) - @overrides(AbstractTransceiver.discover_scamp_connections) + @overrides(Transceiver.discover_scamp_connections) def discover_scamp_connections(self): # Currently, this only finds other UDP connections given a connection # that supports SCP - this is done via the machine @@ -364,14 +364,14 @@ def discover_scamp_connections(self): self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) - @overrides(AbstractTransceiver.add_scamp_connections) + @overrides(Transceiver.add_scamp_connections) def add_scamp_connections(self, connections): for ((x, y), ip_address) in connections.items(): self._check_and_add_scamp_connections(x, y, ip_address) self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) - @overrides(AbstractTransceiver.get_connections) + @overrides(Transceiver.get_connections) def get_connections(self): return self._all_connections @@ -401,7 +401,7 @@ def _get_machine_dimensions(self): 2)) return MachineDimensions(self._width, self._height) - @overrides(AbstractTransceiver.get_machine_details) + @overrides(Transceiver.get_machine_details) def get_machine_details(self): # Get the width and height of the machine self._get_machine_dimensions() @@ -512,7 +512,7 @@ def _is_scamp_version_compabible(version): # version is irrelevant return version[1] > _SCAMP_VERSION[1] - @overrides(AbstractTransceiver.ensure_board_is_ready) + @overrides(Transceiver.ensure_board_is_ready) def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): # try to get a SCAMP version once logger.info("Working out if machine is booted") @@ -627,7 +627,7 @@ def _try_to_find_scamp_and_boot(self, tries_to_go, extra_boot_values): logger.info("Found board with version {}", version_info) return version_info - @overrides(AbstractTransceiver.get_cpu_infos) + @overrides(Transceiver.get_cpu_infos) def get_cpu_infos( self, core_subsets=None, states=None, include=True): # Get all the cores if the subsets are not given @@ -655,7 +655,7 @@ def get_cpu_infos( cpu_info = process.get_cpu_info(core_subsets) return cpu_info - @overrides(AbstractTransceiver.get_clock_drift) + @overrides(Transceiver.get_clock_drift) def get_clock_drift(self, x, y): DRIFT_FP = 1 << 17 @@ -698,19 +698,19 @@ def __get_user_register_address_from_core(p, user): return (get_vcpu_address(p) + CPU_USER_START_ADDRESS + CPU_USER_OFFSET * user) - @overrides(AbstractTransceiver.read_user) + @overrides(Transceiver.read_user) def read_user(self, x, y, p, user): addr = self.__get_user_register_address_from_core(p, user) return self.read_word(x, y, addr) - @overrides(AbstractTransceiver.get_cpu_information_from_core) + @overrides(Transceiver.get_cpu_information_from_core) def get_cpu_information_from_core(self, x, y, p): core_subsets = CoreSubsets() core_subsets.add_processor(x, y, p) cpu_infos = self.get_cpu_infos(core_subsets) return cpu_infos.get_cpu_info(x, y, p) - @overrides(AbstractTransceiver.get_iobuf) + @overrides(Transceiver.get_iobuf) def get_iobuf(self, core_subsets=None): # making the assumption that all chips have the same iobuf size. if self._iobuf_size is None: @@ -723,7 +723,7 @@ def get_iobuf(self, core_subsets=None): process = ReadIOBufProcess(self._scamp_connection_selector) return process.read_iobuf(self._iobuf_size, core_subsets) - @overrides(AbstractTransceiver.get_core_state_count) + @overrides(Transceiver.get_core_state_count) def get_core_state_count(self, app_id, state): process = SendSingleCommandProcess(self._scamp_connection_selector) response = process.execute(CountState(app_id, state)) @@ -741,7 +741,7 @@ def _flood_execute_lock(self): lambda: self._n_chip_execute_locks < 1) yield self._chip_execute_lock_condition - @overrides(AbstractTransceiver.execute_flood) + @overrides(Transceiver.execute_flood) def execute_flood( self, core_subsets, executable, app_id, n_bytes=None, wait=False, is_filename=False): @@ -778,11 +778,11 @@ def _power_on_machine(self): self.power_on(self._bmp_connection) return True - @overrides(AbstractTransceiver.power_on) + @overrides(Transceiver.power_on) def power_on(self, boards=0): self._power(PowerCommand.POWER_ON, boards) - @overrides(AbstractTransceiver.power_off_machine) + @overrides(Transceiver.power_off_machine) def power_off_machine(self): if self._bmp_connection is None: logger.warning("No BMP connections, so can't power off") @@ -791,7 +791,7 @@ def power_off_machine(self): self.power_off(self._bmp_connection) return True - @overrides(AbstractTransceiver.power_off) + @overrides(Transceiver.power_off) def power_off(self, boards=0): self._power(PowerCommand.POWER_OFF, boards) @@ -816,7 +816,7 @@ def _power(self, power_command, boards=0): if not self._machine_off: time.sleep(BMP_POST_POWER_ON_SLEEP_TIME) - @overrides(AbstractTransceiver.read_fpga_register) + @overrides(Transceiver.read_fpga_register) def read_fpga_register( self, fpga_num, register, board=0): process = SendSingleCommandProcess(self._bmp_selector, timeout=1.0) @@ -824,19 +824,19 @@ def read_fpga_register( ReadFPGARegister(fpga_num, register, board)) return response.fpga_register # pylint: disable=no-member - @overrides(AbstractTransceiver.write_fpga_register) + @overrides(Transceiver.write_fpga_register) def write_fpga_register(self, fpga_num, register, value, board=0): process = SendSingleCommandProcess(self._bmp_selector) process.execute( WriteFPGARegister(fpga_num, register, value, board)) - @overrides(AbstractTransceiver.read_bmp_version) + @overrides(Transceiver.read_bmp_version) def read_bmp_version(self, board): process = SendSingleCommandProcess(self._bmp_selector) response = process.execute(BMPGetVersion(board)) return response.version_info # pylint: disable=no-member - @overrides(AbstractTransceiver.write_memory) + @overrides(Transceiver.write_memory) def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, cpu=0, is_filename=False, get_sum=False): process = WriteMemoryProcess(self._scamp_connection_selector) @@ -861,12 +861,12 @@ def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, x, y, cpu, base_address, data, offset, n_bytes, get_sum) return n_bytes, chksum - @overrides(AbstractTransceiver.write_user) + @overrides(Transceiver.write_user) def write_user(self, x, y, p, user, value): addr = self.__get_user_register_address_from_core(p, user) self.write_memory(x, y, addr, int(value)) - @overrides(AbstractTransceiver.read_memory) + @overrides(Transceiver.read_memory) def read_memory(self, x, y, base_address, length, cpu=0): try: process = ReadMemoryProcess(self._scamp_connection_selector) @@ -875,7 +875,7 @@ def read_memory(self, x, y, base_address, length, cpu=0): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.read_word) + @overrides(Transceiver.read_word) def read_word(self, x, y, base_address, cpu=0): try: process = ReadMemoryProcess(self._scamp_connection_selector) @@ -886,7 +886,7 @@ def read_word(self, x, y, base_address, cpu=0): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.stop_application) + @overrides(Transceiver.stop_application) def stop_application(self, app_id): if not self._machine_off: process = SendSingleCommandProcess(self._scamp_connection_selector) @@ -911,7 +911,7 @@ def __log_where_is_info(self, cpu_infos): for (x, y) in xys: logger.info(self.where_is_xy(x, y)) - @overrides(AbstractTransceiver.wait_for_cores_to_be_in_state) + @overrides(Transceiver.wait_for_cores_to_be_in_state) def wait_for_cores_to_be_in_state( self, all_core_subsets, app_id, cpu_states, timeout=None, time_between_polls=0.1, @@ -987,7 +987,7 @@ def wait_for_cores_to_be_in_state( raise SpiNNManCoresNotInStateException( timeout, cpu_states, cores_not_in_state) - @overrides(AbstractTransceiver.send_signal) + @overrides(Transceiver.send_signal) def send_signal(self, app_id, signal): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(SendSignal(app_id, signal)) @@ -1004,7 +1004,7 @@ def _locate_spinnaker_connection_for_board_address(self, board_address): """ return self._udp_scamp_connections.get(board_address, None) - @overrides(AbstractTransceiver.set_ip_tag) + @overrides(Transceiver.set_ip_tag) def set_ip_tag(self, ip_tag, use_sender=False): # Check that the tag has a port assigned if ip_tag.port is None: @@ -1055,7 +1055,7 @@ def __get_connection_list(self, connection=None, board_address=None): return [] return [connection] - @overrides(AbstractTransceiver.set_reverse_ip_tag) + @overrides(Transceiver.set_reverse_ip_tag) def set_reverse_ip_tag(self, reverse_ip_tag): if reverse_ip_tag.port is None: raise SpinnmanInvalidParameterException( @@ -1088,13 +1088,13 @@ def set_reverse_ip_tag(self, reverse_ip_tag): reverse_ip_tag.port, reverse_ip_tag.tag, reverse_ip_tag.sdp_port)) - @overrides(AbstractTransceiver.clear_ip_tag) + @overrides(Transceiver.clear_ip_tag) def clear_ip_tag(self, tag, board_address=None): for conn in self.__get_connection_list(board_address=board_address): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(IPTagClear(conn.chip_x, conn.chip_y, tag)) - @overrides(AbstractTransceiver.get_tags) + @overrides(Transceiver.get_tags) def get_tags(self, connection=None): all_tags = list() for conn in self.__get_connection_list(connection): @@ -1102,7 +1102,7 @@ def get_tags(self, connection=None): all_tags.extend(process.get_tags(conn)) return all_tags - @overrides(AbstractTransceiver.malloc_sdram) + @overrides(Transceiver.malloc_sdram) def malloc_sdram(self, x, y, size, app_id, tag=None): try: process = MallocSDRAMProcess(self._scamp_connection_selector) @@ -1112,7 +1112,7 @@ def malloc_sdram(self, x, y, size, app_id, tag=None): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.load_multicast_routes) + @overrides(Transceiver.load_multicast_routes) def load_multicast_routes(self, x, y, routes, app_id): try: process = LoadMultiCastRoutesProcess( @@ -1122,7 +1122,7 @@ def load_multicast_routes(self, x, y, routes, app_id): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.load_fixed_route) + @overrides(Transceiver.load_fixed_route) def load_fixed_route(self, x, y, fixed_route, app_id): try: process = LoadFixedRouteRoutingEntryProcess( @@ -1132,7 +1132,7 @@ def load_fixed_route(self, x, y, fixed_route, app_id): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.read_fixed_route) + @overrides(Transceiver.read_fixed_route) def read_fixed_route(self, x, y, app_id): try: process = ReadFixedRouteRoutingEntryProcess( @@ -1142,7 +1142,7 @@ def read_fixed_route(self, x, y, app_id): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.get_multicast_routes) + @overrides(Transceiver.get_multicast_routes) def get_multicast_routes(self, x, y, app_id=None): try: base_address = self._get_sv_data( @@ -1154,7 +1154,7 @@ def get_multicast_routes(self, x, y, app_id=None): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.clear_multicast_routes) + @overrides(Transceiver.clear_multicast_routes) def clear_multicast_routes(self, x, y): try: process = SendSingleCommandProcess(self._scamp_connection_selector) @@ -1163,7 +1163,7 @@ def clear_multicast_routes(self, x, y): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.get_router_diagnostics) + @overrides(Transceiver.get_router_diagnostics) def get_router_diagnostics(self, x, y): try: process = ReadRouterDiagnosticsProcess( @@ -1173,7 +1173,7 @@ def get_router_diagnostics(self, x, y): logger.info(self.where_is_xy(x, y)) raise - @overrides(AbstractTransceiver.set_router_diagnostic_filter) + @overrides(Transceiver.set_router_diagnostic_filter) def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): try: self.__set_router_diagnostic_filter( diff --git a/spinnman/transceiver/extendable_transceiver.py b/spinnman/transceiver/extendable_transceiver.py index e0189519e..36d2a7841 100644 --- a/spinnman/transceiver/extendable_transceiver.py +++ b/spinnman/transceiver/extendable_transceiver.py @@ -18,12 +18,12 @@ from spinn_utilities.abstract_base import ( AbstractBase, abstractproperty) from spinn_utilities.log import FormatAdapter -from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.abstract_transceiver import Transceiver logger = FormatAdapter(logging.getLogger(__name__)) -class ExtendableTransceiver(AbstractTransceiver, metaclass=AbstractBase): +class ExtendableTransceiver(Transceiver, metaclass=AbstractBase): """ Support Functions to allow a Transceiver to also be an ExtendedTransceiver diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 1dc4e3543..a66ebc690 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -17,7 +17,7 @@ from spinn_utilities.overrides import overrides from spinnman.data import SpiNNManDataView from spinnman.model.enums import CPUState -from spinnman.transceiver.abstract_transceiver import AbstractTransceiver +from spinnman.transceiver.abstract_transceiver import Transceiver from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver @@ -31,62 +31,62 @@ def __init__(self): super().__init__() self.written_memory = list() - @overrides(AbstractTransceiver.send_sdp_message) + @overrides(Transceiver.send_sdp_message) def send_sdp_message(self, message, connection=None): pass - @overrides(AbstractTransceiver.discover_scamp_connections) + @overrides(Transceiver.discover_scamp_connections) def discover_scamp_connections(self): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.add_scamp_connections) + @overrides(Transceiver.add_scamp_connections) def add_scamp_connections(self, connections): pass - @overrides(AbstractTransceiver.get_machine_details) + @overrides(Transceiver.get_machine_details) def get_machine_details(self): return SpiNNManDataView.get_machine() - @overrides(AbstractTransceiver.get_connections) + @overrides(Transceiver.get_connections) def get_connections(self): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.ensure_board_is_ready) + @overrides(Transceiver.ensure_board_is_ready) def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_cpu_infos) + @overrides(Transceiver.get_cpu_infos) def get_cpu_infos( self, core_subsets=None, states=None, include=True): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_clock_drift) + @overrides(Transceiver.get_clock_drift) def get_clock_drift(self, x, y): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.read_user) + @overrides(Transceiver.read_user) def read_user(self, x, y, p, user): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_cpu_information_from_core) + @overrides(Transceiver.get_cpu_information_from_core) def get_cpu_information_from_core(self, x, y, p): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_iobuf) + @overrides(Transceiver.get_iobuf) def get_iobuf(self, core_subsets=None): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_core_state_count) + @overrides(Transceiver.get_core_state_count) def get_core_state_count(self, app_id, state): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.execute_flood) + @overrides(Transceiver.execute_flood) def execute_flood( self, core_subsets, executable, app_id, n_bytes=None, wait=False, is_filename=False): pass - @overrides(AbstractTransceiver.power_on) + @overrides(Transceiver.power_on) def power_on(self, boards=0): """ Power on a set of boards in the machine. @@ -95,24 +95,24 @@ def power_on(self, boards=0): """ return True - @overrides(AbstractTransceiver.power_off_machine) + @overrides(Transceiver.power_off_machine) def power_off_machine(self): return True - @overrides(AbstractTransceiver.power_off) + @overrides(Transceiver.power_off) def power_off(self, boards=0): return True - @overrides(AbstractTransceiver.read_fpga_register) + @overrides(Transceiver.read_fpga_register) def read_fpga_register( self, fpga_num, register, board=0): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.write_fpga_register) + @overrides(Transceiver.write_fpga_register) def write_fpga_register(self, fpga_num, register, value, board=0): pass - @overrides(AbstractTransceiver.read_bmp_version) + @overrides(Transceiver.read_bmp_version) def read_bmp_version(self, board): """ Read the BMP version. @@ -122,30 +122,30 @@ def read_bmp_version(self, board): """ raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.write_memory) + @overrides(Transceiver.write_memory) def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, cpu=0, is_filename=False, get_sum=False): print("Doing write to", x, y) self.written_memory.append( (x, y, base_address, data, n_bytes, offset, cpu, is_filename)) - @overrides(AbstractTransceiver.write_user) + @overrides(Transceiver.write_user) def write_user(self, x, y, p, user, value): pass - @overrides(AbstractTransceiver.read_memory) + @overrides(Transceiver.read_memory) def read_memory(self, x, y, base_address, length, cpu=0): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.read_word) + @overrides(Transceiver.read_word) def read_word(self, x, y, base_address, cpu=0): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.stop_application) + @overrides(Transceiver.stop_application) def stop_application(self, app_id): pass - @overrides(AbstractTransceiver.wait_for_cores_to_be_in_state) + @overrides(Transceiver.wait_for_cores_to_be_in_state) def wait_for_cores_to_be_in_state( self, all_core_subsets, app_id, cpu_states, timeout=None, time_between_polls=0.1, @@ -154,75 +154,75 @@ def wait_for_cores_to_be_in_state( counts_between_full_check=100, progress_bar=None): pass - @overrides(AbstractTransceiver.send_signal) + @overrides(Transceiver.send_signal) def send_signal(self, app_id, signal): pass - @overrides(AbstractTransceiver.set_ip_tag) + @overrides(Transceiver.set_ip_tag) def set_ip_tag(self, ip_tag, use_sender=False): pass - @overrides(AbstractTransceiver.set_reverse_ip_tag) + @overrides(Transceiver.set_reverse_ip_tag) def set_reverse_ip_tag(self, reverse_ip_tag): pass - @overrides(AbstractTransceiver.clear_ip_tag) + @overrides(Transceiver.clear_ip_tag) def clear_ip_tag(self, tag, board_address=None): pass - @overrides(AbstractTransceiver.get_tags) + @overrides(Transceiver.get_tags) def get_tags(self, connection=None): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.malloc_sdram) + @overrides(Transceiver.malloc_sdram) def malloc_sdram(self, x, y, size, app_id, tag=None): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.load_multicast_routes) + @overrides(Transceiver.load_multicast_routes) def load_multicast_routes(self, x, y, routes, app_id): pass - @overrides(AbstractTransceiver.load_fixed_route) + @overrides(Transceiver.load_fixed_route) def load_fixed_route(self, x, y, fixed_route, app_id): pass - @overrides(AbstractTransceiver.read_fixed_route) + @overrides(Transceiver.read_fixed_route) def read_fixed_route(self, x, y, app_id): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.get_multicast_routes) + @overrides(Transceiver.get_multicast_routes) def get_multicast_routes(self, x, y, app_id=None): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.clear_multicast_routes) + @overrides(Transceiver.clear_multicast_routes) def clear_multicast_routes(self, x, y): pass - @overrides(AbstractTransceiver.get_router_diagnostics) + @overrides(Transceiver.get_router_diagnostics) def get_router_diagnostics(self, x, y): raise NotImplementedError("Needs to be mocked") - @overrides(AbstractTransceiver.set_router_diagnostic_filter) + @overrides(Transceiver.set_router_diagnostic_filter) def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): pass - @overrides(AbstractTransceiver.clear_router_diagnostic_counters) + @overrides(Transceiver.clear_router_diagnostic_counters) def clear_router_diagnostic_counters(self, x, y): pass - @overrides(AbstractTransceiver.close) + @overrides(Transceiver.close) def close(self): pass - @overrides(AbstractTransceiver.control_sync) + @overrides(Transceiver.control_sync) def control_sync(self, do_sync): pass - @overrides(AbstractTransceiver.update_provenance_and_exit) + @overrides(Transceiver.update_provenance_and_exit) def update_provenance_and_exit(self, x, y, p): pass - @overrides(AbstractTransceiver.where_is_xy) + @overrides(Transceiver.where_is_xy) def where_is_xy(self, x, y): return f"Mocked {x=} {y=}" diff --git a/spinnman/transceiver/abstract_transceiver.py b/spinnman/transceiver/transceiver.py similarity index 99% rename from spinnman/transceiver/abstract_transceiver.py rename to spinnman/transceiver/transceiver.py index 77a15a684..812ef0236 100644 --- a/spinnman/transceiver/abstract_transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -20,7 +20,7 @@ from spinnman.model.enums import CPUState -class AbstractTransceiver(AbstractContextManager, metaclass=AbstractBase): +class Transceiver(AbstractContextManager, metaclass=AbstractBase): """ An encapsulation of various communications with the SpiNNaker board. diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index 455f0bbd5..8d889f7d9 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -56,7 +56,7 @@ def create_transceiver_from_hostname( :param bool extended: If True will return an Extended version of the Transceiver :return: The created transceiver - :rtype: spinnman.transceiver.AbstractTransceiver + :rtype: spinnman.transceiver.Transceiver :raise SpinnmanIOException: If there is an error communicating with the board :raise SpinnmanInvalidPacketException: @@ -105,7 +105,7 @@ def create_transceiver_from_connections(connections, extended=False): communication will be possible until connections are found. :param bool extended: :return: The created transceiver - :rtype: spinnman.transceiver.AbstractTransceiver + :rtype: spinnman.transceiver.Transceiver :raise SpinnmanIOException: If there is an error communicating with the board :raise SpinnmanInvalidPacketException: From e5bcb9b13ad555862d38c982bfa850676ae0e70a Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 10:22:31 +0100 Subject: [PATCH 23/39] abstract_transceiver.py is now transceiver.py --- spinnman/spalloc/spalloc_job.py | 2 +- spinnman/transceiver/base_transceiver.py | 2 +- spinnman/transceiver/extendable_transceiver.py | 2 +- spinnman/transceiver/mockable_transceiver.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py index 8685f1c2f..81035a5ed 100644 --- a/spinnman/spalloc/spalloc_job.py +++ b/spinnman/spalloc/spalloc_job.py @@ -17,7 +17,7 @@ from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.abstract_context_manager import AbstractContextManager from spinnman.constants import SCP_SCAMP_PORT -from spinnman.transceiver.abstract_transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman.connections.udp_packet_connections import UDPConnection from .spalloc_state import SpallocState from .spalloc_boot_connection import SpallocBootConnection diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index 7ab57f672..f2e3d21bc 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -70,7 +70,7 @@ SendSingleCommandProcess, ReadRouterDiagnosticsProcess, MostDirectConnectionSelector, ApplicationCopyRunProcess) from spinnman.utilities.utility_functions import get_vcpu_address -from spinnman.transceiver.abstract_transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver logger = FormatAdapter(logging.getLogger(__name__)) diff --git a/spinnman/transceiver/extendable_transceiver.py b/spinnman/transceiver/extendable_transceiver.py index 36d2a7841..54592d2cc 100644 --- a/spinnman/transceiver/extendable_transceiver.py +++ b/spinnman/transceiver/extendable_transceiver.py @@ -18,7 +18,7 @@ from spinn_utilities.abstract_base import ( AbstractBase, abstractproperty) from spinn_utilities.log import FormatAdapter -from spinnman.transceiver.abstract_transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver logger = FormatAdapter(logging.getLogger(__name__)) diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index a66ebc690..82eea7ff5 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -17,7 +17,7 @@ from spinn_utilities.overrides import overrides from spinnman.data import SpiNNManDataView from spinnman.model.enums import CPUState -from spinnman.transceiver.abstract_transceiver import Transceiver +from spinnman.transceiver.transceiver import Transceiver from spinnman.transceiver.extendable_transceiver import ExtendableTransceiver From 89ae8f444554ca2e6d4fcb2f07e0d4f757c88b18 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 11:17:59 +0100 Subject: [PATCH 24/39] simplify BoardTestConfiguration --- quick_tests/quick_tests.py | 6 ++-- spinnman/board_test_configuration.py | 32 +++---------------- spinnman/constants.py | 2 ++ .../connection_tests/test_udp_connection.py | 7 ++-- 4 files changed, 13 insertions(+), 34 deletions(-) diff --git a/quick_tests/quick_tests.py b/quick_tests/quick_tests.py index f28772a71..1e8b7b35e 100644 --- a/quick_tests/quick_tests.py +++ b/quick_tests/quick_tests.py @@ -29,8 +29,7 @@ from spinn_machine.tags import IPTag, ReverseIPTag from spinnman.data import SpiNNManDataView from spinnman.config_setup import unittest_setup -from spinnman.extended.extended_transceiver import ( - create_transceiver_from_hostname) +from spinnman.transceiver import create_transceiver_from_hostname from spinnman.model.enums import CPUState from spinnman.messages.scp.enums import Signal from spinnman.model import DiagnosticFilter @@ -354,7 +353,6 @@ def print_transceiver_tests(transceiver): with create_transceiver_from_hostname( - board_config.remotehost, board_config.board_version, - bmp_connection_data=board_config.bmp_names, + hostname=board_config.remotehost, auto_detect_bmp=board_config.auto_detect_bmp) as _transceiver: print_transceiver_tests(_transceiver) diff --git a/spinnman/board_test_configuration.py b/spinnman/board_test_configuration.py index 329c625fd..0c5c46604 100644 --- a/spinnman/board_test_configuration.py +++ b/spinnman/board_test_configuration.py @@ -15,50 +15,28 @@ import unittest from spinn_utilities.config_holder import set_config from spinn_utilities.ping import Ping -# from spinnman.model import BMPConnectionData - -_LOCALHOST = "127.0.0.1" -# Microsoft invalid IP address. For more details see: -# https://answers.microsoft.com/en-us/windows/forum/windows_vista-networking/invalid-ip-address-169254xx/ce096728-e2b7-4d54-80cc-52a4ed342870 -_NOHOST = "169.254.254.254" -_PORT = 54321 +from spinnman.constants import LOCAL_HOST class BoardTestConfiguration(object): def __init__(self): - self.localport = None self.remotehost = None - self.board_version = None - self.bmp_names = None self.auto_detect_bmp = None - def set_up_local_virtual_board(self): - self.localport = _PORT - self.remotehost = _LOCALHOST - self.board_version = 5 - - def set_up_remote_board(self): + def set_up_remote_board(self, version=None): if Ping.host_is_reachable("192.168.240.253"): self.remotehost = "192.168.240.253" - self.board_version = 3 set_config("Machine", "version", 3) self.auto_detect_bmp = False elif Ping.host_is_reachable("spinn-4.cs.man.ac.uk"): self.remotehost = "spinn-4.cs.man.ac.uk" - self.board_version = 5 set_config("Machine", "version", 5) elif Ping.host_is_reachable("192.168.240.1"): self.remotehost = "192.168.240.1" - self.board_version = 5 set_config("Machine", "version", 5) + elif version is not None: + self.remotehost = LOCAL_HOST + set_config("Machine", "version", version) else: raise unittest.SkipTest("None of the test boards reachable") - - # it always was None but this is what to do if not - # self.bmp_names = BMPConnectionData(0, 0, self.bmp_names, [0], None) - - def set_up_nonexistent_board(self): - self.localport = _PORT - self.remotehost = _NOHOST - self.board_version = None diff --git a/spinnman/constants.py b/spinnman/constants.py index 1f890c144..1ccd0185c 100644 --- a/spinnman/constants.py +++ b/spinnman/constants.py @@ -14,6 +14,8 @@ from enum import Enum +LOCAL_HOST = "127.0.0.1" + #: the amount of time to wait in seconds between powering off and powering # on a SpiNNaker board. POWER_CYCLE_WAIT_TIME_IN_SECONDS = 30 diff --git a/unittests/connection_tests/test_udp_connection.py b/unittests/connection_tests/test_udp_connection.py index 3aedd39b4..322f9601e 100644 --- a/unittests/connection_tests/test_udp_connection.py +++ b/unittests/connection_tests/test_udp_connection.py @@ -60,10 +60,11 @@ def test_scp_read_memory_request_and_response_board(self): self.assertEqual(result, SCPResult.RC_OK) def test_send_scp_request_to_nonexistent_host(self): + # Microsoft invalid IP address. For more details see: + # https://answers.microsoft.com/en-us/windows/forum/windows_vista-networking/invalid-ip-address-169254xx/ce096728-e2b7-4d54-80cc-52a4ed342870 + _NOHOST = "169.254.254.254" with self.assertRaises(SpinnmanTimeoutException): - self.board_config.set_up_nonexistent_board() - connection = SCAMPConnection( - remote_host=self.board_config.remotehost) + connection = SCAMPConnection(remote_host=_NOHOST) scp = ReadMemory(0, 0, 0, 256) connection.send_scp_request(scp) _, _, _, _ = connection.receive_scp_response(2) From 2526e54348fb9f15367e64cfbffbdd686234b755 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 14:17:41 +0100 Subject: [PATCH 25/39] Virtual5Transceiver --- spinnman/transceiver/transceiver_factory.py | 17 +++- spinnman/transceiver/version5transceiver.py | 1 + spinnman/transceiver/virtual5Transceiver.py | 77 +++++++++++++++++++ .../connection_tests/test_udp_connection.py | 16 +++- unittests/model_tests/test_iptag_model.py | 2 +- unittests/test_transceiver.py | 8 +- 6 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 spinnman/transceiver/virtual5Transceiver.py diff --git a/spinnman/transceiver/transceiver_factory.py b/spinnman/transceiver/transceiver_factory.py index 8d889f7d9..b30ba9551 100644 --- a/spinnman/transceiver/transceiver_factory.py +++ b/spinnman/transceiver/transceiver_factory.py @@ -25,6 +25,8 @@ BMPConnection, BootConnection, SCAMPConnection) from spinnman.transceiver.version3transceiver import Version3Transceiver from spinnman.transceiver.version5transceiver import Version5Transceiver +from spinnman.transceiver.virtual5Transceiver import Virtual5Transceiver +from spinnman.constants import LOCAL_HOST logger = FormatAdapter(logging.getLogger(__name__)) @@ -93,16 +95,23 @@ def create_transceiver_from_hostname( # handle the boot connection connections.append(BootConnection(remote_host=hostname)) - return create_transceiver_from_connections(connections, extended) + if hostname == LOCAL_HOST: + return create_transceiver_from_connections( + connections=connections, virtual=True, extended=extended) + else: + return create_transceiver_from_connections( + connections=connections, virtual=False, extended=extended) -def create_transceiver_from_connections(connections, extended=False): +def create_transceiver_from_connections( + connections, virtual=False, extended=False): """ Create a Transceiver with these connections :param list(Connection) connections: An iterable of connections to the board. If not specified, no communication will be possible until connections are found. + :param bool virtual: If True will return a virtual Transceiver :param bool extended: :return: The created transceiver :rtype: spinnman.transceiver.Transceiver @@ -117,10 +126,14 @@ def create_transceiver_from_connections(connections, extended=False): """ version = SpiNNManDataView.get_machine_version() if isinstance(version, Version3): + if virtual: + raise NotImplementedError(f"No Virtual Transceiver for {version=}") if extended: return ExtendedVersion3Transceiver(connections=connections) return Version3Transceiver(connections=connections) if isinstance(version, Version5): + if virtual: + return Virtual5Transceiver(connections=connections) if extended: return ExtendedVersion5Transceiver(connections=connections) return Version5Transceiver(connections=connections) diff --git a/spinnman/transceiver/version5transceiver.py b/spinnman/transceiver/version5transceiver.py index e1fc82455..78f95ef67 100644 --- a/spinnman/transceiver/version5transceiver.py +++ b/spinnman/transceiver/version5transceiver.py @@ -33,3 +33,4 @@ def __init__(self, connections=None): @overrides(BaseTransceiver.boot_led_0_value) def boot_led_0_value(self): return 0x00000001 + diff --git a/spinnman/transceiver/virtual5Transceiver.py b/spinnman/transceiver/virtual5Transceiver.py new file mode 100644 index 000000000..aebca383b --- /dev/null +++ b/spinnman/transceiver/virtual5Transceiver.py @@ -0,0 +1,77 @@ +# Copyright (c) 2024 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 +# return 0the License. + +from spinn_utilities.overrides import overrides +from spinn_machine import virtual_machine +from spinnman.constants import N_RETRIES +from spinnman.exceptions import SpinnmanIOException +from spinnman.messages.scp.abstract_messages import AbstractSCPRequest +from spinnman.model import VersionInfo +from .version5transceiver import Version5Transceiver + + +class Virtual5Transceiver(Version5Transceiver): + """ + Overwirtes the standard Version 5 Transceiver + to intercept the sending of messages. + + This is just enough different to pass know tests. + Extend for new tests! + """ + + @overrides(Version5Transceiver._boot_board) + def _boot_board(self, extra_boot_values=None): + try: + super()._boot_board(extra_boot_values) + except SpinnmanIOException: + pass + + @overrides(Version5Transceiver.read_memory) + def read_memory(self, x, y, base_address, length, cpu=0): + try: + return super().read_memory(x, y, base_address, length, cpu) + except SpinnmanIOException: + if (x==y==255 and base_address==4110450434): + return bytearray(b'\x08\x08') + raise NotImplementedError( + f"Unexpected {x=} {y=} {base_address=}, {length=} {cpu=}") + + @overrides(Version5Transceiver._get_scamp_version) + def _get_scamp_version( + self, chip_x=AbstractSCPRequest.DEFAULT_DEST_X_COORD, + chip_y=AbstractSCPRequest.DEFAULT_DEST_Y_COORD, + connection_selector=None, n_retries=N_RETRIES): + try: + return super()._get_scamp_version(chip_x, chip_y, connection_selector, n_retries) + except SpinnmanIOException: + version = VersionInfo( + b'@\x00\x07\x08\xff\x00\x00\x00\x00\x00\x80\x00\x02\x00\x00\n' + b'\x00\x00\x00\x01\xff\xffoa\xa3bSC&MP/SpiNNaker\x003.4.2\x00', + offset=14) + return version + + @overrides(Version5Transceiver.get_machine_details) + def get_machine_details(self): + try: + return super().get_machine_details() + except SpinnmanIOException: + return virtual_machine(8, 8) + + @overrides(Version5Transceiver.get_cpu_infos) + def get_cpu_infos(self, core_subsets=None, states=None, include=True): + try: + return super().get_cpu_infos(core_subsets, states, include) + except SpinnmanIOException: + return None \ No newline at end of file diff --git a/unittests/connection_tests/test_udp_connection.py b/unittests/connection_tests/test_udp_connection.py index 322f9601e..72afbbf7d 100644 --- a/unittests/connection_tests/test_udp_connection.py +++ b/unittests/connection_tests/test_udp_connection.py @@ -15,6 +15,7 @@ import unittest from spinnman.connections.udp_packet_connections import SCAMPConnection from spinnman.config_setup import unittest_setup +from spinnman.constants import LOCAL_HOST from spinnman.exceptions import SpinnmanTimeoutException from spinnman.messages.scp.impl import GetVersion, ReadLink, ReadMemory from spinnman.messages.scp.enums import SCPResult @@ -29,12 +30,15 @@ def setUp(self): self.board_config = BoardTestConfiguration() def test_scp_version_request_and_response_board(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) connection = SCAMPConnection( remote_host=self.board_config.remotehost) scp_req = GetVersion(0, 0, 0) scp_response = GetVersionResponse() connection.send_scp_request(scp_req) + if self.board_config.remotehost == LOCAL_HOST: + raise unittest.SkipTest( + "Can not test the rest without a ral board") _, _, data, offset = connection.receive_scp_response() scp_response.read_bytestring(data, offset) print(scp_response.version_info) @@ -42,20 +46,26 @@ def test_scp_version_request_and_response_board(self): scp_response._scp_response_header._result, SCPResult.RC_OK) def test_scp_read_link_request_and_response_board(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) connection = SCAMPConnection( remote_host=self.board_config.remotehost) scp_link = ReadLink(0, 0, 0, 0x70000000, 250) connection.send_scp_request(scp_link) + if self.board_config.remotehost == LOCAL_HOST: + raise unittest.SkipTest( + "Can not test the rest without a ral board") result, _, _, _ = connection.receive_scp_response() self.assertEqual(result, SCPResult.RC_OK) def test_scp_read_memory_request_and_response_board(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) connection = SCAMPConnection( remote_host=self.board_config.remotehost) scp_link = ReadMemory(0, 0, 0x70000000, 256) connection.send_scp_request(scp_link) + if self.board_config.remotehost == LOCAL_HOST: + raise unittest.SkipTest( + "Can not test the rest without a ral board") result, _, _, _ = connection.receive_scp_response() self.assertEqual(result, SCPResult.RC_OK) diff --git a/unittests/model_tests/test_iptag_model.py b/unittests/model_tests/test_iptag_model.py index e870b6424..b3e9e78a9 100644 --- a/unittests/model_tests/test_iptag_model.py +++ b/unittests/model_tests/test_iptag_model.py @@ -25,7 +25,7 @@ def setUp(self): def test_new_iptag(self): board_config = BoardTestConfiguration() - board_config.set_up_remote_board() + board_config.set_up_remote_board(version=5) ip = "8.8.8.8" port = 1337 tag = 255 diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index b444bbe46..2e8ca16ae 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -40,7 +40,7 @@ def setUp(self): self.board_config = BoardTestConfiguration() def test_create_new_transceiver_to_board(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) connections = list() connections.append(SCAMPConnection( remote_host=self.board_config.remotehost)) @@ -49,7 +49,7 @@ def test_create_new_transceiver_to_board(self): trans.close() def test_create_new_transceiver_one_connection(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) connections = set() connections.add(SCAMPConnection( remote_host=self.board_config.remotehost)) @@ -58,7 +58,7 @@ def test_create_new_transceiver_one_connection(self): trans.close() def test_retrieving_machine_details(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) trans = create_transceiver_from_hostname(self.board_config.remotehost) SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) version = SpiNNManDataView.get_machine_version() @@ -72,7 +72,7 @@ def test_retrieving_machine_details(self): print(trans.get_cpu_infos()) def test_boot_board(self): - self.board_config.set_up_remote_board() + self.board_config.set_up_remote_board(version=5) with create_transceiver_from_hostname( self.board_config.remotehost) as trans: trans._boot_board() From a86bd77ffae4730e07713e0227b9834ecd44e830 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 16:33:29 +0100 Subject: [PATCH 26/39] flake8 --- spinnman/transceiver/version5transceiver.py | 1 - spinnman/transceiver/virtual5Transceiver.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spinnman/transceiver/version5transceiver.py b/spinnman/transceiver/version5transceiver.py index 78f95ef67..e1fc82455 100644 --- a/spinnman/transceiver/version5transceiver.py +++ b/spinnman/transceiver/version5transceiver.py @@ -33,4 +33,3 @@ def __init__(self, connections=None): @overrides(BaseTransceiver.boot_led_0_value) def boot_led_0_value(self): return 0x00000001 - diff --git a/spinnman/transceiver/virtual5Transceiver.py b/spinnman/transceiver/virtual5Transceiver.py index aebca383b..4d21168f2 100644 --- a/spinnman/transceiver/virtual5Transceiver.py +++ b/spinnman/transceiver/virtual5Transceiver.py @@ -43,7 +43,7 @@ def read_memory(self, x, y, base_address, length, cpu=0): try: return super().read_memory(x, y, base_address, length, cpu) except SpinnmanIOException: - if (x==y==255 and base_address==4110450434): + if (x == y == 255 and base_address == 4110450434): return bytearray(b'\x08\x08') raise NotImplementedError( f"Unexpected {x=} {y=} {base_address=}, {length=} {cpu=}") @@ -54,7 +54,8 @@ def _get_scamp_version( chip_y=AbstractSCPRequest.DEFAULT_DEST_Y_COORD, connection_selector=None, n_retries=N_RETRIES): try: - return super()._get_scamp_version(chip_x, chip_y, connection_selector, n_retries) + return super()._get_scamp_version( + chip_x, chip_y, connection_selector, n_retries) except SpinnmanIOException: version = VersionInfo( b'@\x00\x07\x08\xff\x00\x00\x00\x00\x00\x80\x00\x02\x00\x00\n' @@ -74,4 +75,4 @@ def get_cpu_infos(self, core_subsets=None, states=None, include=True): try: return super().get_cpu_infos(core_subsets, states, include) except SpinnmanIOException: - return None \ No newline at end of file + return None From fdd3b2c2fc0218deb3bee30b9e88bf9f25a48d56 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 16:39:46 +0100 Subject: [PATCH 27/39] raise from --- spinnman/transceiver/virtual5Transceiver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spinnman/transceiver/virtual5Transceiver.py b/spinnman/transceiver/virtual5Transceiver.py index 4d21168f2..ff7b14b06 100644 --- a/spinnman/transceiver/virtual5Transceiver.py +++ b/spinnman/transceiver/virtual5Transceiver.py @@ -42,11 +42,12 @@ def _boot_board(self, extra_boot_values=None): def read_memory(self, x, y, base_address, length, cpu=0): try: return super().read_memory(x, y, base_address, length, cpu) - except SpinnmanIOException: + except SpinnmanIOException as exc: if (x == y == 255 and base_address == 4110450434): return bytearray(b'\x08\x08') raise NotImplementedError( - f"Unexpected {x=} {y=} {base_address=}, {length=} {cpu=}") + f"Unexpected {x=} {y=} {base_address=}, {length=} {cpu=}") \ + from exc @overrides(Version5Transceiver._get_scamp_version) def _get_scamp_version( From 26121d2fe0c270908e597204a67dd4df80526f77 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 16:59:55 +0100 Subject: [PATCH 28/39] bmp_names is None so dont use --- spinnman/get_cores_in_run_state.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index 04846da23..f1128645b 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -83,7 +83,6 @@ def _make_transceiver(host, version, bmp_names): config = BoardTestConfiguration() config.set_up_remote_board() host = config.remotehost - bmp_names = config.bmp_names auto_detect_bmp = config.auto_detect_bmp else: if version is None: @@ -96,8 +95,7 @@ def _make_transceiver(host, version, bmp_names): print(f"talking to SpiNNaker system at {host}") return create_transceiver_from_hostname( - host, bmp_connection_data=bmp_names, - auto_detect_bmp=auto_detect_bmp) + host, auto_detect_bmp=auto_detect_bmp) def main(args): From ef14d77ab48e12ac7ed89145d2df48d821126348 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 14 Aug 2023 17:07:25 +0100 Subject: [PATCH 29/39] allow bmp from args --- spinnman/get_cores_in_run_state.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index f1128645b..fdb570225 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -83,6 +83,7 @@ def _make_transceiver(host, version, bmp_names): config = BoardTestConfiguration() config.set_up_remote_board() host = config.remotehost + bmp_names = None auto_detect_bmp = config.auto_detect_bmp else: if version is None: @@ -95,7 +96,8 @@ def _make_transceiver(host, version, bmp_names): print(f"talking to SpiNNaker system at {host}") return create_transceiver_from_hostname( - host, auto_detect_bmp=auto_detect_bmp) + host, bmp_connection_data=bmp_names, + auto_detect_bmp=auto_detect_bmp) def main(args): From 8ce8f7fb3b6119b6a8e264397aa7fe6f482e5002 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 15 Aug 2023 10:02:01 +0100 Subject: [PATCH 30/39] it is still 2023 --- spinnman/transceiver/version3transceiver.py | 2 +- spinnman/transceiver/version5transceiver.py | 2 +- spinnman/transceiver/virtual5Transceiver.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spinnman/transceiver/version3transceiver.py b/spinnman/transceiver/version3transceiver.py index e0f883101..bdb129b76 100644 --- a/spinnman/transceiver/version3transceiver.py +++ b/spinnman/transceiver/version3transceiver.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024 The University of Manchester +# Copyright (c) 2023 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. diff --git a/spinnman/transceiver/version5transceiver.py b/spinnman/transceiver/version5transceiver.py index e1fc82455..f3329ed9e 100644 --- a/spinnman/transceiver/version5transceiver.py +++ b/spinnman/transceiver/version5transceiver.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024 The University of Manchester +# Copyright (c) 2023 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. diff --git a/spinnman/transceiver/virtual5Transceiver.py b/spinnman/transceiver/virtual5Transceiver.py index ff7b14b06..01d6326ad 100644 --- a/spinnman/transceiver/virtual5Transceiver.py +++ b/spinnman/transceiver/virtual5Transceiver.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024 The University of Manchester +# Copyright (c) 2023 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. From 0f87f99d69c0d8df1545186fc9cad5bee80e9294 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 16 Aug 2023 13:05:53 +0100 Subject: [PATCH 31/39] add usgae comments --- spinnman/transceiver/transceiver.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 812ef0236..7add3c162 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -46,6 +46,9 @@ def send_sdp_message(self, message, connection=None): :param SDPMessage message: The message to send :param SDPConnection connection: An optional connection to use """ + # _ChipProvenanceUpdater._send_chip_update_provenance_and_exit + # DataSpeedUpPacketGatherMachineVertex. + # _determine_and_retransmit_missing_seq_nums @abstractmethod def discover_scamp_connections(self): @@ -65,6 +68,9 @@ def discover_scamp_connections(self): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # Used directly after Transceiver init + # Not called if add_scamp_connections is called + # Not called by SpallocJobController @abstractmethod def add_scamp_connections(self, connections): @@ -86,6 +92,7 @@ def add_scamp_connections(self, connections): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # Use on a spalloc created Transceiver @abstractmethod def get_connections(self): @@ -577,6 +584,7 @@ def send_signal(self, app_id, signal): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # Known usages are to send Signal START, SYNC0 and SYNC1 @abstractmethod def set_ip_tag(self, ip_tag, use_sender=False): From 74e205a92677d551870070869da366c1d9b4523d Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 16 Aug 2023 13:53:09 +0100 Subject: [PATCH 32/39] add comments of open prs --- spinnman/transceiver/transceiver.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 7add3c162..74bc1b9bc 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -47,6 +47,7 @@ def send_sdp_message(self, message, connection=None): :param SDPConnection connection: An optional connection to use """ # _ChipProvenanceUpdater._send_chip_update_provenance_and_exit + # https://github.com/SpiNNakerManchester/SpiNNMan/pull/357 # DataSpeedUpPacketGatherMachineVertex. # _determine_and_retransmit_missing_seq_nums @@ -144,6 +145,7 @@ def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): * If the version of software on the board is not compatible with this transceiver """ + # https://github.com/SpiNNakerManchester/SpiNNMan/pull/356 @abstractmethod def get_cpu_infos( @@ -329,6 +331,7 @@ def power_on(self, boards=0): :param int boards: The board or boards to power on """ + # https://github.com/SpiNNakerManchester/SpiNNMan/pull/356 @abstractmethod def power_off_machine(self): @@ -338,6 +341,7 @@ def power_off_machine(self): :rtype bool :return success or failure to power off the machine """ + # https://github.com/SpiNNakerManchester/SpiNNMan/pull/356 @abstractmethod def power_off(self, boards=0): @@ -346,6 +350,7 @@ def power_off(self, boards=0): :param int boards: The board or boards to power off """ + # https://github.com/SpiNNakerManchester/SpiNNMan/pull/356 @abstractmethod def read_fpga_register( From bacaa6ae03b795ec50e8e34ec51f51ddf4d3c43a Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 16 Aug 2023 15:07:14 +0100 Subject: [PATCH 33/39] add usage comments --- spinnman/transceiver/transceiver.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 74bc1b9bc..2bbdadc2b 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -105,6 +105,7 @@ def get_connections(self): :return: An iterable of connections known to the transceiver :rtype: set(Connection) """ + # used in unittest only @abstractmethod def get_machine_details(self): @@ -123,6 +124,7 @@ def get_machine_details(self): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # used by machine_generator @abstractmethod def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): @@ -176,6 +178,11 @@ def get_cpu_infos( :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # used by + # ASB.__recover_from_error + # application_finisher + # chip_provenance_updater + # emergency_recover_state_from_failure @abstractmethod def get_clock_drift(self, x, y): @@ -184,6 +191,7 @@ def get_clock_drift(self, x, y): :param int x: The x-coordinate of the chip to get drift for :param int y: The y-coordinate of the chip to get drift for """ + # used by drift_report @abstractmethod def read_user(self, x, y, p, user): @@ -208,6 +216,11 @@ def read_user(self, x, y, p, user): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # used by pair_compression and others during _check_for_success + # used by memory_map_on_host_chip_report._get_region_table_addr + # used by DataSpeedUpPacketGatherMachineVertex + # and ExtraMonitorSupportMachineVertex + # to .update_transaction_id_from_machine @abstractmethod def get_cpu_information_from_core(self, x, y, p): From 9bcac897151b55eb5ba4bda200367a431b49bbe0 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 17 Aug 2023 09:27:10 +0100 Subject: [PATCH 34/39] note another pr --- spinnman/transceiver/transceiver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 2bbdadc2b..a440d6ba2 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -50,6 +50,7 @@ def send_sdp_message(self, message, connection=None): # https://github.com/SpiNNakerManchester/SpiNNMan/pull/357 # DataSpeedUpPacketGatherMachineVertex. # _determine_and_retransmit_missing_seq_nums + # https://github.com/SpiNNakerManchester/SpiNNFrontEndCommon/pull/1102 @abstractmethod def discover_scamp_connections(self): From 69c055810f4c60a04f57a8b13967cdc468ebfcb4 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 18 Aug 2023 15:47:28 +0100 Subject: [PATCH 35/39] note another pr --- spinnman/transceiver/transceiver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index a440d6ba2..9ac9f3e13 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -179,6 +179,7 @@ def get_cpu_infos( :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # See https://github.com/SpiNNakerManchester/SpiNNMan/pull/358 # used by # ASB.__recover_from_error # application_finisher @@ -243,6 +244,7 @@ def get_cpu_information_from_core(self, x, y, p): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # see https://github.com/SpiNNakerManchester/SpiNNMan/pull/358 @abstractmethod def get_iobuf(self, core_subsets=None): From 38eb0f63115665f1d43d633633f093d130ab35df Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 22 Aug 2023 14:05:18 +0100 Subject: [PATCH 36/39] more usgae notes --- spinnman/transceiver/transceiver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index 9ac9f3e13..33d4fb7f3 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -220,6 +220,7 @@ def read_user(self, x, y, p, user): """ # used by pair_compression and others during _check_for_success # used by memory_map_on_host_chip_report._get_region_table_addr + # https://github.com/SpiNNakerManchester/SpiNNFrontEndCommon/pull/1104 # used by DataSpeedUpPacketGatherMachineVertex # and ExtraMonitorSupportMachineVertex # to .update_transaction_id_from_machine @@ -268,6 +269,7 @@ def get_iobuf(self, core_subsets=None): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # Used by IOBufExtractor @abstractmethod def get_core_state_count(self, app_id, state): @@ -339,6 +341,8 @@ def execute_flood( :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + # Used by load_app_images, load_sys_images and + # run_system_application._load_application @abstractmethod def power_on(self, boards=0): From e8dafdbd1f3762a2ddba4f77e6166d91f3085ac7 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 10 Oct 2023 09:24:16 +0100 Subject: [PATCH 37/39] dont pass version as that sets up localhost --- unittests/test_transceiver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 2e8ca16ae..b444bbe46 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -40,7 +40,7 @@ def setUp(self): self.board_config = BoardTestConfiguration() def test_create_new_transceiver_to_board(self): - self.board_config.set_up_remote_board(version=5) + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( remote_host=self.board_config.remotehost)) @@ -49,7 +49,7 @@ def test_create_new_transceiver_to_board(self): trans.close() def test_create_new_transceiver_one_connection(self): - self.board_config.set_up_remote_board(version=5) + self.board_config.set_up_remote_board() connections = set() connections.add(SCAMPConnection( remote_host=self.board_config.remotehost)) @@ -58,7 +58,7 @@ def test_create_new_transceiver_one_connection(self): trans.close() def test_retrieving_machine_details(self): - self.board_config.set_up_remote_board(version=5) + self.board_config.set_up_remote_board() trans = create_transceiver_from_hostname(self.board_config.remotehost) SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) version = SpiNNManDataView.get_machine_version() @@ -72,7 +72,7 @@ def test_retrieving_machine_details(self): print(trans.get_cpu_infos()) def test_boot_board(self): - self.board_config.set_up_remote_board(version=5) + self.board_config.set_up_remote_board() with create_transceiver_from_hostname( self.board_config.remotehost) as trans: trans._boot_board() From f84b2a2f67b6264b285dfce57743937c6722e802 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 10 Oct 2023 10:32:43 +0100 Subject: [PATCH 38/39] fix merge --- spinnman/transceiver/transceiver.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index e53d20959..02ab8508c 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -14,13 +14,11 @@ # pylint: disable=too-many-arguments -from spinn_utilities.abstract_base import ( - AbstractBase, abstractmethod) -from spinn_utilities.abstract_context_manager import AbstractContextManager +from spinn_utilities.abstract_base import abstractmethod from spinnman.model.enums import CPUState -class Transceiver(AbstractContextManager, metaclass=AbstractBase): +class Transceiver(object): """ An encapsulation of various communications with the SpiNNaker board. From 6adca50051ba64101e0ee6d2bd449c0597c120c6 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 10 Oct 2023 11:37:59 +0100 Subject: [PATCH 39/39] flake8 --- spinnman/transceiver/base_transceiver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index ae018c1bb..2fa7ae3b6 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -107,6 +107,7 @@ "reset_machine_on_startup = False in the [Machine] section of the " "relevant configuration (cfg) file to avoid this warning in future.") + class BaseTransceiver(ExtendableTransceiver, metaclass=AbstractBase): """ A base for all the code shared by all Version of the Transciever.