diff --git a/quick_tests/quick_tests.py b/quick_tests/quick_tests.py index 837b1cf49..73e4c20a8 100644 --- a/quick_tests/quick_tests.py +++ b/quick_tests/quick_tests.py @@ -29,7 +29,8 @@ from spinn_machine.tags import IPTag, ReverseIPTag from spinnman.data import SpiNNManDataView from spinnman.config_setup import unittest_setup -from spinnman.transceiver import create_transceiver_from_hostname +from spinnman.extended.extended_transceiver import ( + create_transceiver_from_hostname) from spinnman.model.enums import CPUState from spinnman.messages.scp.enums import Signal from spinnman.model import DiagnosticFilter @@ -42,6 +43,7 @@ logging.basicConfig(level=logging.INFO) logging.getLogger("spinnman.transceiver").setLevel(logging.DEBUG) +unittest_setup() board_config = BoardTestConfiguration() board_config.set_up_remote_board() @@ -352,7 +354,6 @@ def print_transceiver_tests(transceiver): print(heap_element) -unittest_setup() with create_transceiver_from_hostname( board_config.remotehost, board_config.board_version, bmp_connection_data=board_config.bmp_names, diff --git a/setup.cfg b/setup.cfg index 5fa86d612..469af333e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ packages = find: zip_safe = True include_package_data = True install_requires = - SpiNNMachine == 1!6.0.1 + SpiNNMachine == 1!7.1.0 websocket-client [options.packages.find] diff --git a/spinnman/_version.py b/spinnman/_version.py index 54e334e5a..6288012f2 100644 --- a/spinnman/_version.py +++ b/spinnman/_version.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1!6.0.1" -__version_month__ = "May" -__version_year__ = "2021" +__version__ = "1!7.1.0" +__version_month__ = "TBD" +__version_year__ = "TBD" __version_day__ = "TBD" __version_name__ = "Not yet released" diff --git a/spinnman/board_test_configuration.cfg b/spinnman/board_test_configuration.cfg deleted file mode 100644 index b19bb182a..000000000 --- a/spinnman/board_test_configuration.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[Machine] -machineName = spinn-4.cs.man.ac.uk -bmp_names = spinn-4c.cs.man.ac.uk -version = 5 - -#machineName = 192.168.240.253 -#version = 2 -#bmp_names = None -auto_detect_bmp = False diff --git a/spinnman/board_test_configuration.py b/spinnman/board_test_configuration.py index b936c8603..329c625fd 100644 --- a/spinnman/board_test_configuration.py +++ b/spinnman/board_test_configuration.py @@ -12,13 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import configparser -from contextlib import closing -import os -import socket import unittest +from spinn_utilities.config_holder import set_config from spinn_utilities.ping import Ping -from spinnman.model import BMPConnectionData +# from spinnman.model import BMPConnectionData _LOCALHOST = "127.0.0.1" # Microsoft invalid IP address. For more details see: @@ -30,44 +27,38 @@ class BoardTestConfiguration(object): def __init__(self): - self.localhost = None self.localport = None self.remotehost = None self.board_version = None self.bmp_names = None self.auto_detect_bmp = None - self._config = configparser.RawConfigParser() - config_file = os.path.join( - os.path.dirname(__file__), "board_test_configuration.cfg") - self._config.read(config_file) - def set_up_local_virtual_board(self): - self.localhost = _LOCALHOST self.localport = _PORT self.remotehost = _LOCALHOST - self.board_version = self._config.getint("Machine", "version") + self.board_version = 5 def set_up_remote_board(self): - self.remotehost = self._config.get("Machine", "machineName") - if not Ping.host_is_reachable(self.remotehost): - raise unittest.SkipTest("test board appears to be down") - self.board_version = self._config.getint("Machine", "version") - self.bmp_names = self._config.get("Machine", "bmp_names") - if self.bmp_names == "None": - self.bmp_names = 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) else: - self.bmp_names = [BMPConnectionData( - 0, 0, self.bmp_names, [0], None)] - self.auto_detect_bmp = \ - self._config.getboolean("Machine", "auto_detect_bmp") - self.localport = _PORT - with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s: - s.connect((self.remotehost, _PORT)) - self.localhost = s.getsockname()[0] + 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.localhost = None self.localport = _PORT self.remotehost = _NOHOST - self.board_version = self._config.getint("Machine", "version") + self.board_version = None diff --git a/spinnman/connections/udp_packet_connections/bmp_connection.py b/spinnman/connections/udp_packet_connections/bmp_connection.py index b939c30dd..63e45379e 100644 --- a/spinnman/connections/udp_packet_connections/bmp_connection.py +++ b/spinnman/connections/udp_packet_connections/bmp_connection.py @@ -30,10 +30,7 @@ class BMPConnection(UDPConnection, AbstractSCPConnection): """ A BMP connection which supports queries to the BMP of a SpiNNaker machine. """ - __slots__ = ( - "_boards", - "_cabinet", - "_frame") + __slots__ = ("_boards", ) def __init__(self, connection_data: BMPConnectionData): """ @@ -44,28 +41,8 @@ def __init__(self, connection_data: BMPConnectionData): else connection_data.port_num super().__init__( remote_host=connection_data.ip_address, remote_port=port) - self._cabinet = connection_data.cabinet - self._frame = connection_data.frame self._boards = connection_data.boards - @property - def cabinet(self) -> int: - """ - The cabinet ID of the BMP. - - :rtype: int - """ - return self._cabinet - - @property - def frame(self) -> int: - """ - The frame ID of the BMP. - - :rtype: int - """ - return self._frame - @property def boards(self) -> Sequence[int]: """ @@ -109,7 +86,7 @@ def send_scp_request(self, scp_request: AbstractSCPRequest): def __repr__(self): return ( - f"BMPConnection(cabinet={self._cabinet}, frame={self._frame}, " + f"BMPConnection(" f"boards={self._boards}, local_host={self.local_ip_address}, " f"local_port={self.local_port}, " f"remote_host={self.remote_ip_address}, " diff --git a/spinnman/extended/__init__.py b/spinnman/extended/__init__.py new file mode 100644 index 000000000..2cb63a7f3 --- /dev/null +++ b/spinnman/extended/__init__.py @@ -0,0 +1,22 @@ +# 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 .bmp_set_led import BMPSetLed +from .de_alloc_sdram_process import DeAllocSDRAMProcess +from .read_adc import ReadADC +from .set_led import SetLED +from .write_memory_flood_process import WriteMemoryFloodProcess + +__all__ = ["BMPSetLed", "DeAllocSDRAMProcess", "ReadADC", "SetLED", + "WriteMemoryFloodProcess"] diff --git a/spinnman/messages/scp/impl/bmp_set_led.py b/spinnman/extended/bmp_set_led.py similarity index 78% rename from spinnman/messages/scp/impl/bmp_set_led.py rename to spinnman/extended/bmp_set_led.py index 902073d3d..c76f2d220 100644 --- a/spinnman/messages/scp/impl/bmp_set_led.py +++ b/spinnman/extended/bmp_set_led.py @@ -11,26 +11,25 @@ # 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 typing import Iterable, Union + from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.abstract_messages import ( - AbstractSCPRequest, BMPRequest, BMPOKResponse) -from spinnman.messages.scp.abstract_messages.bmp_request import Boards -from spinnman.messages.scp.enums import SCPCommand, LEDAction + AbstractSCPRequest, BMPRequest) +from spinnman.messages.scp.enums import SCPCommand +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse -class BMPSetLed(BMPRequest[BMPOKResponse]): +class BMPSetLed(BMPRequest): """ Set the LED(s) of a board to either on, off or toggling. This class is currently deprecated and untested as there is no known use except for Transceiver.set_led which is itself deprecated. """ - __slots__ = () + __slots__ = [] - def __init__(self, led: Union[int, Iterable[int]], action: LEDAction, - boards: Boards): + def __init__(self, led, action, boards): """ :param led: Number of the LED or an iterable of LEDs to set the state of (0-7) @@ -45,7 +44,7 @@ def __init__(self, led: Union[int, Iterable[int]], action: LEDAction, if isinstance(led, int): leds = [led] else: - leds = list(led) + leds = led # LED setting actions arg1 = sum(action.value << (led * 2) for led in leds) @@ -60,5 +59,5 @@ def __init__(self, led: Union[int, Iterable[int]], action: LEDAction, argument_1=arg1, argument_2=arg2) @overrides(AbstractSCPRequest.get_scp_response) - def get_scp_response(self) -> BMPOKResponse: - return BMPOKResponse("Set the LEDs of a board", SCPCommand.CMD_LED) + def get_scp_response(self): + return CheckOKResponse("Set the LEDs of a board", "CMD_LED") diff --git a/spinnman/extended/de_alloc_sdram_process.py b/spinnman/extended/de_alloc_sdram_process.py new file mode 100644 index 000000000..916a2500d --- /dev/null +++ b/spinnman/extended/de_alloc_sdram_process.py @@ -0,0 +1,65 @@ + +# Copyright (c) 2015 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.messages.scp.impl import SDRAMDeAlloc +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) + + +class DeAllocSDRAMProcess(AbstractMultiConnectionProcess): + """ + .. warning:: + This class is currently deprecated and untested as there is no + known use except for Transceiver.free_sdram and free_sdram_by_app_id + which are both themselves deprecated. + """ + __slots__ = [ + "_no_blocks_freed"] + + def __init__(self, connection_selector): + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + self._no_blocks_freed = None + + def de_alloc_sdram(self, x, y, app_id, base_address=None): + """ + :param int x: + :param int y: + :param int app_id: + :param base_address: + :type base_address: int or None + """ + callback = None + # deallocate space in the SDRAM + if base_address is None: + callback = self._handle_sdram_alloc_response + self._send_request(SDRAMDeAlloc(x, y, app_id, base_address), + callback=callback) + self._finish() + self.check_for_error() + + def _handle_sdram_alloc_response(self, response): + self._no_blocks_freed = response.number_of_blocks_freed + + @property + def no_blocks_freed(self): + """ + :rtype: int + """ + return self._no_blocks_freed diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py new file mode 100644 index 000000000..5ae7038f5 --- /dev/null +++ b/spinnman/extended/extended_transceiver.py @@ -0,0 +1,809 @@ +# 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 +import io +import os +import logging +from threading import Condition, RLock +import time +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 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) +from spinnman.model import DiagnosticFilter +from spinnman.model.enums import CPUState +from spinnman.messages.scp.enums import Signal +from spinnman.messages.scp.impl import ( + ReadMemory, ApplicationRun) +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 import _EXECUTABLE_ADDRESS, _ONE_BYTE, _ONE_WORD +from spinnman.utilities.utility_functions import ( + work_out_bmp_from_machine_details) + +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 ExtendedTransceiver(version, connections=connections) + + +class ExtendedTransceiver(Transceiver): + """ + 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__ = ["_flood_write_lock", "_nearest_neighbour_id", + "_nearest_neighbour_lock"] + + def __init__(self, version, 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. + :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__(version, 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): + """ + Sends an SCP message, without expecting a response. + + :param message: The message to send + :type message: + spinnman.messages.scp.abstract_messages.AbstractSCPRequest + :param SCAMPConnection connection: + The connection to use (omit to pick a random one) + :raise SpinnmanTimeoutException: + If there is a timeout before a message is received + :raise SpinnmanInvalidParameterException: + If one of the fields of the received message is invalid + :raise SpinnmanInvalidPacketException: + * If the message is not a recognised packet type + * If a packet is received that is not a valid response + :raise SpinnmanUnsupportedOperationException: + If no connection can send the type of message given + :raise SpinnmanIOException: + If there is an error sending the message or receiving the response + :raise SpinnmanUnexpectedResponseCodeException: + If the response is not one of the expected codes + """ + if connection is 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. + + :param Connection connection: + The connection which is to be tested. If `None`, + all 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) + + 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. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :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 IOBUF for + :return: An IOBUF buffer + :rtype: 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 + """ + warn_once(logger, "The get_iobuf_from_core method is deprecated and " + "likely to be removed.") + core_subsets = CoreSubsets() + core_subsets.add_processor(x, y, p) + return next(self.get_iobuf(core_subsets)) + + @contextmanager + def _chip_execute_lock(self, x, y): + """ + Get a lock for executing an executable on a chip. + + .. warning:: + This method is currently deprecated and untested as there is no + known use except for execute, which is itself deprecated. + + :param int x: + :param int y: + """ + # Check if there is a lock for the given chip + with self._chip_execute_lock_condition: + chip_lock = self._chip_execute_locks[x, y] + # Acquire the lock for the chip + chip_lock.acquire() + + # Increment the lock counter (used for the flood lock) + with self._chip_execute_lock_condition: + self._n_chip_execute_locks += 1 + + try: + yield chip_lock + finally: + with self._chip_execute_lock_condition: + # Release the chip lock + chip_lock.release() + # Decrement the lock and notify + self._n_chip_execute_locks -= 1 + self._chip_execute_lock_condition.notify_all() + + def execute( + self, x, y, processors, executable, app_id, n_bytes=None, + wait=False, is_filename=False): + """ + Start an executable running on a single chip. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :param int x: + The x-coordinate of the chip on which to run the executable + :param int y: + The y-coordinate of the chip on which to run the executable + :param list(int) processors: + The cores on the chip on which to run the application + :param executable: + The data that is to be executed. Should be one of the following: + + * An instance of RawIOBase + * A bytearray/bytes + * A filename of a file containing the 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 binary should enter a "wait" state on loading + :param bool is_filename: True if executable 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 x, y, p does not lead to a valid core + * If app_id is an invalid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + warn_once(logger, "The Transceiver's execute method is deprecated " + "likely to be removed.") + # Lock against updates + with self._chip_execute_lock(x, y): + # Write the executable + self.write_memory( + x, y, _EXECUTABLE_ADDRESS, executable, n_bytes, + is_filename=is_filename) + + # Request the start of the executable + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(ApplicationRun(app_id, x, y, processors, wait)) + + def execute_application(self, executable_targets, app_id): + """ + Execute a set of binaries that make up a complete application on + specified cores, wait for them to be ready and then start all of the + binaries. + + .. note:: + This will get the binaries into c_main but will not signal the + barrier. + + :param ExecutableTargets executable_targets: + The binaries to be executed and the cores to execute them on + :param int app_id: The app_id to give this application + """ + # Execute each of the binaries and get them in to a "wait" state + for binary in executable_targets.binaries: + core_subsets = executable_targets.get_cores_for_binary(binary) + self.execute_flood( + core_subsets, binary, app_id, wait=True, is_filename=True) + + # Sleep to allow cores to get going + time.sleep(0.5) + + # Check that the binaries have reached a wait state + count = self.get_core_state_count(app_id, CPUState.READY) + if count < executable_targets.total_processors: + cores_ready = self.get_cpu_infos( + executable_targets.all_core_subsets, [CPUState.READY], + include=False) + if len(cores_ready) > 0: + raise SpinnmanException( + f"Only {count} of {executable_targets.total_processors} " + "cores reached ready state: " + f"{cores_ready.get_status_string()}") + + # Send a signal telling the application to start + self.send_signal(app_id, Signal.START) + + def set_led(self, led, action, board): + """ + Set the LED state of a board in the machine. + + .. 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 led: + Number of the LED or an iterable of LEDs to set the state of (0-7) + :type led: int or iterable(int) + :param LEDAction action: + State to set the LED to, either on, off or toggle + :param board: Specifies the board to control the LEDs of. This may + also be an iterable of multiple boards. The + command will actually be sent to the first board in the iterable. + :type board: int or iterable(int) + """ + warn_once(logger, "The set_led method is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._bmp_selector) + process.execute(BMPSetLed(led, action, board)) + + def read_adc_data(self, board): + """ + Read the BMP ADC data. + + .. 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 board: which board to request the ADC data from + :return: the FPGA's ADC data object + :rtype: ADCInfo + """ + warn_once(logger, "The read_adc_data method is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._bmp_selector) + response = process.execute(ReadADC(board)) + return response.adc_info # pylint: disable=no-member + + def write_neighbour_memory(self, x, y, link, base_address, data, + n_bytes=None, offset=0, cpu=0): + """ + Write to the memory of a neighbouring chip using a LINK_READ SCP + command. If sent to a BMP, this command can be used to communicate + with the FPGAs' debug registers. + + .. warning:: + This method is deprecated and untested due to no known use. + + :param int x: + The x-coordinate of the chip whose neighbour is to be written to + :param int y: + The y-coordinate of the chip whose neighbour is to be written to + :param int link: + The link index to send the request to (or if BMP, the FPGA number) + :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 + :type data: + ~io.RawIOBase or bytes or bytearray or int + :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 + :param int offset: + The offset where the valid data starts (if `data` is + an int then offset will be ignored and used 0) + :param int cpu: + The CPU to use, typically 0 (or if a BMP, the slot number) + :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 + """ + warn_once(logger, "The write_neighbour_memory method is deprecated " + "and untested due to no known use.") + 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) + elif isinstance(data, int): + data_to_write = _ONE_WORD.pack(data) + process.write_link_memory_from_bytearray( + x, y, cpu, link, base_address, data_to_write, 0, 4) + else: + if n_bytes is None: + n_bytes = len(data) + process.write_link_memory_from_bytearray( + x, y, cpu, link, base_address, data, offset, n_bytes) + + def read_neighbour_memory(self, x, y, link, base_address, length, cpu=0): + """ + Read some areas of memory on a neighbouring chip using a LINK_READ + SCP command. If sent to a BMP, this command can be used to + communicate with the FPGAs' debug registers. + + .. 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: + The x-coordinate of the chip whose neighbour is to be read from + :param int y: + The y-coordinate of the chip whose neighbour is to be read from + :param int cpu: + The CPU to use, typically 0 (or if a BMP, the slot number) + :param int link: + The link index to send the request to (or if BMP, the FPGA number) + :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 + :return: An iterable of chunks of data read in order + :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: + warn_once(logger, "The read_neighbour_memory method is deprecated " + "and untested due to no known use.") + 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)) + raise + + def _get_next_nearest_neighbour_id(self): + with self._nearest_neighbour_lock: + next_nearest_neighbour_id = (self._nearest_neighbour_id + 1) % 127 + self._nearest_neighbour_id = next_nearest_neighbour_id + return next_nearest_neighbour_id + + def write_memory_flood( + self, base_address, data, n_bytes=None, offset=0, + is_filename=False): + """ + Write to the SDRAM of all chips. + + :param int base_address: + The address in SDRAM where the region of memory is to be written + :param data: + The data that is to be written. Should be one of the following: + + * An instance of RawIOBase + * A byte-string + * A single integer + * A file name of a file to read (in which case `is_filename` + should 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 or bytes, the length of the bytearray + will be used + * If `data` is an int, 4 will be used + * If `data` is a str, the size of the file will be used + :param int offset: + The offset where the valid data starts; if `data` is + an int, then the offset will be ignored and 0 is used. + :param bool is_filename: + True if `data` should be interpreted as a file name + :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 chips is not valid + * If `app_id` is an invalid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + process = WriteMemoryFloodProcess(self._scamp_connection_selector) + # Ensure only one flood fill occurs at any one time + with self._flood_write_lock: + # Start the flood fill + nearest_neighbour_id = self._get_next_nearest_neighbour_id() + if isinstance(data, io.RawIOBase): + process.write_memory_from_reader( + nearest_neighbour_id, base_address, data, n_bytes) + elif isinstance(data, str) and is_filename: + if n_bytes is None: + n_bytes = os.stat(data).st_size + with open(data, "rb") as reader: + process.write_memory_from_reader( + nearest_neighbour_id, base_address, reader, n_bytes) + elif isinstance(data, int): + data_to_write = _ONE_WORD.pack(data) + process.write_memory_from_bytearray( + nearest_neighbour_id, base_address, data_to_write, 0) + else: + if n_bytes is None: + n_bytes = len(data) + process.write_memory_from_bytearray( + nearest_neighbour_id, base_address, data, offset, n_bytes) + + def set_leds(self, x, y, cpu, led_states): + """ + Set LED states. + + .. warning:: + The set_leds is deprecated and untested due to no known use. + + :param int x: The x-coordinate of the chip on which to set the LEDs + :param int y: The x-coordinate of the chip on which to set the LEDs + :param int cpu: The CPU of the chip on which to set the LEDs + :param dict(int,int) led_states: + A dictionary mapping SetLED index to state with + 0 being off, 1 on and 2 inverted. + :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: + warn_once(logger, "The set_leds is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(SetLED(x, y, cpu, led_states)) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def free_sdram(self, x, y, base_address, app_id): + """ + Free allocated SDRAM. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :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 base_address: The base address of the allocated memory + :param int app_id: The app ID of the allocated memory + """ + try: + warn_once(logger, "The free_sdram method is deprecated and " + "likely to be removed.") + 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)) + raise + + def free_sdram_by_app_id(self, x, y, app_id): + """ + Free all SDRAM allocated to a given app ID. + + .. 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: 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 app_id: The app ID of the allocated memory + :return: The number of blocks freed + :rtype: int + """ + try: + warn_once(logger, "The free_sdram_by_app_id method is deprecated " + "and untested due to no known use.") + process = DeAllocSDRAMProcess(self._scamp_connection_selector) + process.de_alloc_sdram(x, y, app_id) + return process.no_blocks_freed + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def get_router_diagnostic_filter(self, x, y, position): + """ + Gets a router diagnostic filter from a router. + + :param int x: + the X address of the router from which this filter is being + retrieved + :param int y: + the Y address of the router from which this filter is being + retrieved + :param int position: + the position in the list of filters to read the information from + :return: The diagnostic filter read + :rtype: DiagnosticFilter + :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 position is less than 0 or more than 15 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + try: + memory_position = ( + ROUTER_REGISTER_BASE_ADDRESS + ROUTER_FILTER_CONTROLS_OFFSET + + position * ROUTER_DIAGNOSTIC_FILTER_SIZE) + + process = SendSingleCommandProcess( + self._scamp_connection_selector) + response = process.execute(ReadMemory(x, y, memory_position, 4)) + return DiagnosticFilter.read_from_int(_ONE_WORD.unpack_from( + response.data, response.offset)[0]) + # pylint: disable=no-member + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + @property + def number_of_boards_located(self): + """ + The number of boards currently configured. + + .. warning:: + This property is currently deprecated and likely to be removed. + + :rtype: int + """ + 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)) + else: + # if no BMPs are available, then there's still at least one board + return 1 + + def get_heap(self, x, y, heap=SystemVariableDefinition.sdram_heap_address): + """ + Get the contents of the given heap on a given chip. + + :param int x: The x-coordinate of the chip + :param int y: The y-coordinate of the chip + :param SystemVariableDefinition heap: + The SystemVariableDefinition which is the heap to read + :rtype: list(HeapElement) + """ + try: + process = GetHeapProcess(self._scamp_connection_selector) + return process.get_heap((x, y), heap) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise diff --git a/spinnman/messages/scp/impl/read_adc.py b/spinnman/extended/read_adc.py similarity index 61% rename from spinnman/messages/scp/impl/read_adc.py rename to spinnman/extended/read_adc.py index 9ab919ac4..6fedf28e3 100644 --- a/spinnman/messages/scp/impl/read_adc.py +++ b/spinnman/extended/read_adc.py @@ -11,15 +11,17 @@ # 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 spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.abstract_messages import ( - AbstractSCPRequest, BMPRequest, BMPResponse) -from spinnman.messages.scp.enums import BMPInfo, SCPCommand + AbstractSCPRequest, AbstractSCPResponse, BMPRequest, BMPResponse) +from spinnman.messages.scp.enums import BMPInfo, SCPCommand, SCPResult +from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException from spinnman.model import ADCInfo -class ReadADC(BMPRequest['_SCPReadADCResponse']): +class ReadADC(BMPRequest): """ SCP Request for the data from the BMP including voltages and temperature. @@ -31,34 +33,44 @@ class ReadADC(BMPRequest['_SCPReadADCResponse']): .. note:: The equivalent code in Java is *not* deprecated. """ - __slots__ = () + __slots__ = [] - def __init__(self, board: int): + def __init__(self, board): """ :param int board: which board to request the ADC register from """ super().__init__( board, SCPRequestHeader(command=SCPCommand.CMD_BMP_INFO), - argument_1=BMPInfo.ADC.value) + argument_1=BMPInfo.ADC) @overrides(AbstractSCPRequest.get_scp_response) - def get_scp_response(self) -> '_SCPReadADCResponse': - return _SCPReadADCResponse("Read ADC", SCPCommand.CMD_BMP_INFO) + def get_scp_response(self): + return _SCPReadADCResponse() -class _SCPReadADCResponse(BMPResponse[ADCInfo]): +class _SCPReadADCResponse(BMPResponse): """ An SCP response to a request for ADC information. """ - __slots__ = () + __slots__ = [ + "_adc_info"] + + def __init__(self): + super().__init__() + self._adc_info = None - def _parse_payload(self, data: bytes, offset: int) -> ADCInfo: - return ADCInfo(data, offset) + @overrides(AbstractSCPResponse.read_data_bytestring) + def read_data_bytestring(self, data, offset): + result = self.scp_response_header.result + if result != SCPResult.RC_OK: + raise SpinnmanUnexpectedResponseCodeException( + "ADC", "CMD_ADC", result.name) + self._adc_info = ADCInfo(data, offset) @property - def adc_info(self) -> ADCInfo: + def adc_info(self): """ The ADC information. """ - return self._value + return self._adc_info diff --git a/spinnman/messages/scp/impl/set_led.py b/spinnman/extended/set_led.py similarity index 90% rename from spinnman/messages/scp/impl/set_led.py rename to spinnman/extended/set_led.py index 7cd3af944..220ae5695 100644 --- a/spinnman/messages/scp/impl/set_led.py +++ b/spinnman/extended/set_led.py @@ -11,13 +11,13 @@ # 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 typing import Dict + from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.abstract_messages import AbstractSCPRequest from spinnman.messages.scp.enums import SCPCommand from spinnman.messages.sdp import SDPFlag, SDPHeader -from .check_ok_response import CheckOKResponse +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse class SetLED(AbstractSCPRequest): @@ -27,9 +27,9 @@ class SetLED(AbstractSCPRequest): This class is currently deprecated and untested as there is no known use except for Transceiver.set_led which is itself deprecated. """ - __slots__ = () + __slots__ = [] - def __init__(self, x: int, y: int, cpu: int, led_states: Dict[int, int]): + def __init__(self, x, y, cpu, led_states): """ :param int x: The x-coordinate of the chip, between 0 and 255 :param int y: The y-coordinate of the chip, between 0 and 255 @@ -51,5 +51,5 @@ def __init__(self, x: int, y: int, cpu: int, led_states: Dict[int, int]): argument_1=encoded_led_states) @overrides(AbstractSCPRequest.get_scp_response) - def get_scp_response(self) -> CheckOKResponse: + def get_scp_response(self): return CheckOKResponse("Set SetLED", "CMD_LED") diff --git a/spinnman/extended/write_memory_flood_process.py b/spinnman/extended/write_memory_flood_process.py new file mode 100644 index 000000000..4ef491bf7 --- /dev/null +++ b/spinnman/extended/write_memory_flood_process.py @@ -0,0 +1,108 @@ +# Copyright (c) 2015 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 math +from spinnman.messages.scp.impl import ( + FloodFillEnd, FloodFillStart, FloodFillData) +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) +from spinnman.constants import UDP_MESSAGE_MAX_SIZE + + +class WriteMemoryFloodProcess(AbstractMultiConnectionProcess): + """ + A process for writing memory on multiple SpiNNaker chips at once. + """ + __slots__ = [] + + def __init__(self, next_connection_selector): + AbstractMultiConnectionProcess.__init__( + self, next_connection_selector, n_channels=3, + intermediate_channel_waits=2) + + def _start_flood_fill(self, n_bytes, nearest_neighbour_id): + n_blocks = int(math.ceil(math.ceil(n_bytes / 4.0) / + UDP_MESSAGE_MAX_SIZE)) + self._send_request( + FloodFillStart(nearest_neighbour_id, n_blocks)) + self._finish() + self.check_for_error() + + def _end_flood_fill(self, nearest_neighbour_id): + self._send_request(FloodFillEnd(nearest_neighbour_id)) + self._finish() + self.check_for_error() + + def write_memory_from_bytearray(self, nearest_neighbour_id, base_address, + data, offset, n_bytes=None): + """ + :param int nearest_neighbour_id: + :param int base_address: + :param data: + :type data: bytes or bytearray + :param int offset: + :param int n_bytes: + """ + # pylint: disable=too-many-arguments + if n_bytes is None: + n_bytes = len(data) + self._start_flood_fill(n_bytes, nearest_neighbour_id) + + data_offset = offset + offset = base_address + block_no = 0 + while n_bytes > 0: + bytes_to_send = min((int(n_bytes), UDP_MESSAGE_MAX_SIZE)) + + self._send_request(FloodFillData( + nearest_neighbour_id, block_no, offset, + data, data_offset, bytes_to_send)) + + block_no += 1 + n_bytes -= bytes_to_send + offset += bytes_to_send + data_offset += bytes_to_send + self._finish() + self.check_for_error() + + self._end_flood_fill(nearest_neighbour_id) + + def write_memory_from_reader(self, nearest_neighbour_id, base_address, + reader, n_bytes): + """ + :param int nearest_neighbour_id: + :param int base_address: + :param reader: + :type reader: ~io.RawIOBase or ~io.BufferedIOBase + :param int n_bytes: + """ + self._start_flood_fill(n_bytes, nearest_neighbour_id) + + offset = base_address + block_no = 0 + while n_bytes > 0: + bytes_to_send = min((int(n_bytes), UDP_MESSAGE_MAX_SIZE)) + data_array = reader.read(bytes_to_send) + + self._send_request(FloodFillData( + nearest_neighbour_id, block_no, offset, + data_array, 0, len(data_array))) + + block_no += 1 + n_bytes -= bytes_to_send + offset += bytes_to_send + self._finish() + self.check_for_error() + + self._end_flood_fill(nearest_neighbour_id) diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index 5b4374029..075cb166a 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -21,8 +21,8 @@ from spinnman.transceiver import create_transceiver_from_hostname from spinn_machine import CoreSubsets, CoreSubset from spinnman.board_test_configuration import BoardTestConfiguration +from spinnman.config_setup import unittest_setup from spinnman.model.enums import CPUState - SCAMP_ID = 0 IGNORED_IDS = {SCAMP_ID, 16} # WHY 16? @@ -104,6 +104,7 @@ def main(args): """ Runs the script. """ + unittest_setup() ap = argparse.ArgumentParser( description="Check the state of a SpiNNaker machine.") ap.add_argument( diff --git a/spinnman/messages/scp/impl/__init__.py b/spinnman/messages/scp/impl/__init__.py index d058b6fe1..05c8fb667 100644 --- a/spinnman/messages/scp/impl/__init__.py +++ b/spinnman/messages/scp/impl/__init__.py @@ -16,7 +16,6 @@ from .app_copy_run import AppCopyRun from .application_run import ApplicationRun from .bmp_get_version import BMPGetVersion -from .bmp_set_led import BMPSetLed from .check_ok_response import CheckOKResponse from .count_state import CountState from .do_sync import DoSync @@ -31,7 +30,6 @@ from .iptag_get_info import IPTagGetInfo from .iptag_set import IPTagSet from .iptag_set_tto import IPTagSetTTO -from .read_adc import ReadADC from .read_fpga_register import ReadFPGARegister from .read_link import ReadLink from .read_memory import ReadMemory @@ -42,7 +40,6 @@ from .sdram_alloc import SDRAMAlloc from .sdram_de_alloc import SDRAMDeAlloc from .send_signal import SendSignal -from .set_led import SetLED from .set_power import SetPower from .write_fpga_register import WriteFPGARegister from .write_link import WriteLink @@ -51,15 +48,13 @@ from .fixed_route_read import FixedRouteRead __all__ = ["AppStop", "ApplicationRun", "AppCopyRun", - "BMPSetLed", "BMPGetVersion", - "CheckOKResponse", "GetChipInfo", "CountState", + "BMPGetVersion", "CheckOKResponse", "GetChipInfo", "CountState", "DoSync", "FloodFillData", "FillRequest", "FloodFillEnd", "FloodFillStart", "IPTagClear", "IPTagGet", "IPTagGetInfo", - "IPTagSet", "IPTagSetTTO", "SetLED", - "SetPower", "ReadADC", - "ReadFPGARegister", "ReadLink", + "IPTagSet", "IPTagSetTTO", + "SetPower", "ReadFPGARegister", "ReadLink", "ReadMemory", "ReverseIPTagSet", "RouterAlloc", "RouterClear", "RouterInit", "SDRAMAlloc", diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py index 260d6d2eb..54efe26a1 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py @@ -47,7 +47,10 @@ def __init__(self, board_version: Optional[int] = None, SystemVariableDefinition, Any]] = None): """ :param int board_version: The version of the board to be booted - :param extra_boot_values: Any additional values to be set during boot + :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. :type extra_boot_values: dict(SystemVariableDefinition, object) :raise SpinnmanInvalidParameterException: If the board version is not supported diff --git a/spinnman/model/bmp_connection_data.py b/spinnman/model/bmp_connection_data.py index 0ce1cc389..492669375 100644 --- a/spinnman/model/bmp_connection_data.py +++ b/spinnman/model/bmp_connection_data.py @@ -20,39 +20,15 @@ class BMPConnectionData(object): """ __slots__ = [ "_boards", - "_cabinet", - "_frame", "_ip_address", "_port_num"] - def __init__( - self, cabinet: int, frame: int, ip_address: str, - boards: Sequence[int], port_num: Optional[int]): - # pylint: disable=too-many-arguments - self._cabinet = cabinet - self._frame = frame + def __init__(self, ip_address: str, boards: Sequence[int], + port_num: Optional[int]): self._ip_address = ip_address self._boards = tuple(boards) self._port_num = port_num - @property - def cabinet(self) -> int: - """ - The cabinet number. - - :rtype: int - """ - return self._cabinet - - @property - def frame(self) -> int: - """ - The frame number. - - :rtype: int - """ - return self._frame - @property def ip_address(self) -> str: """ @@ -81,8 +57,7 @@ def port_num(self) -> Optional[int]: return self._port_num def __str__(self) -> str: - return (f"{self._cabinet}:{self._frame}:{self._ip_address}:" - f"{self._boards}:{self._port_num}") + return f"{self._ip_address}:{self._boards}:{self._port_num}" def __repr__(self) -> str: return self.__str__() diff --git a/spinnman/processes/__init__.py b/spinnman/processes/__init__.py index 76700cf71..8a88d3113 100644 --- a/spinnman/processes/__init__.py +++ b/spinnman/processes/__init__.py @@ -17,7 +17,7 @@ ConnectionSelector) from .application_copy_run_process import ApplicationCopyRunProcess from .application_run_process import ApplicationRunProcess -from .de_alloc_sdram_process import DeAllocSDRAMProcess + from .fixed_connection_selector import FixedConnectionSelector from .get_heap_process import GetHeapProcess from .get_cpu_info_process import GetCPUInfoProcess @@ -39,7 +39,6 @@ from .read_router_diagnostics_process import ReadRouterDiagnosticsProcess from .round_robin_connection_selector import RoundRobinConnectionSelector from .send_single_command_process import SendSingleCommandProcess -from .write_memory_flood_process import WriteMemoryFloodProcess from .write_memory_process import WriteMemoryProcess # Old name @@ -51,7 +50,7 @@ "RoundRobinConnectionSelector", "AbstractMultiConnectionProcess", "ApplicationRunProcess", "ApplicationCopyRunProcess", - "DeAllocSDRAMProcess", "GetCPUInfoProcess", + "GetCPUInfoProcess", "GetExcludeCPUInfoProcess", "GetIncludeCPUInfoProcess", "GetHeapProcess", "GetMachineProcess", "GetMultiCastRoutesProcess", "GetTagsProcess", @@ -59,5 +58,4 @@ "LoadMultiCastRoutesProcess", "MallocSDRAMProcess", "ReadFixedRouteRoutingEntryProcess", "ReadIOBufProcess", "ReadMemoryProcess", "ReadRouterDiagnosticsProcess", - "SendSingleCommandProcess", "WriteMemoryFloodProcess", - "WriteMemoryProcess"] + "SendSingleCommandProcess", "WriteMemoryProcess"] diff --git a/spinnman/processes/de_alloc_sdram_process.py b/spinnman/processes/de_alloc_sdram_process.py deleted file mode 100644 index ad3479cea..000000000 --- a/spinnman/processes/de_alloc_sdram_process.py +++ /dev/null @@ -1,67 +0,0 @@ - -# Copyright (c) 2015 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 typing import Optional -from spinnman.messages.scp.impl.sdram_de_alloc import ( - SDRAMDeAlloc, _SCPSDRAMDeAllocResponse) -from .abstract_multi_connection_process import AbstractMultiConnectionProcess -from .abstract_multi_connection_process_connection_selector import ( - ConnectionSelector) - - -class DeAllocSDRAMProcess( - AbstractMultiConnectionProcess[_SCPSDRAMDeAllocResponse]): - """ - .. warning:: - This class is currently deprecated and untested as there is no - known use except for Transceiver.free_sdram and free_sdram_by_app_id - which are both themselves deprecated. - """ - __slots__ = ("_no_blocks_freed", ) - - def __init__(self, connection_selector: ConnectionSelector): - """ - :param ConnectionSelector connection_selector: - """ - super().__init__(connection_selector) - self._no_blocks_freed: Optional[int] = None - - def de_alloc_sdram( - self, x: int, y: int, app_id: int, - base_address: Optional[int] = None): - """ - :param int x: - :param int y: - :param int app_id: - :param base_address: - :type base_address: int or None - """ - # deallocate space in the SDRAM - with self._collect_responses(): - self._send_request( - SDRAMDeAlloc(x, y, app_id=app_id, base_address=base_address), - callback=(self._handle_sdram_alloc_response - if base_address is None else None)) - - def _handle_sdram_alloc_response(self, response: _SCPSDRAMDeAllocResponse): - self._no_blocks_freed = response.number_of_blocks_freed - - @property - def no_blocks_freed(self) -> int: - """ - The number of blocks freed. Only valid if no base address was supplied - in the request. - """ - assert self._no_blocks_freed is not None, "wrong request type" - return self._no_blocks_freed diff --git a/spinnman/processes/get_machine_process.py b/spinnman/processes/get_machine_process.py index b6becb917..62ab4ce00 100644 --- a/spinnman/processes/get_machine_process.py +++ b/spinnman/processes/get_machine_process.py @@ -23,12 +23,12 @@ from spinn_utilities.data import UtilsDataView from spinn_utilities.log import FormatAdapter from spinn_utilities.typing.coords import XY -from spinn_machine import (Router, Chip, Link, machine_from_size) +from spinn_machine import (Router, Chip, Link, Machine) from spinn_machine.ignores import IgnoreChip, IgnoreCore, IgnoreLink -from spinn_machine import Machine from spinn_machine.machine_factory import machine_repair from spinnman.constants import ( ROUTER_REGISTER_P2P_ADDRESS, SYSTEM_VARIABLE_BASE_ADDRESS) +from spinnman.data import SpiNNManDataView from spinnman.messages.spinnaker_boot import ( SystemVariableDefinition) from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException @@ -101,7 +101,9 @@ def _make_chip(self, chip_info: ChipSummaryInfo, machine: Machine) -> Chip: :rtype: ~spinn_machine.Chip """ # Create the down cores set if any - n_cores = min(chip_info.n_cores, Machine.max_cores_per_chip()) + n_cores = \ + SpiNNManDataView.get_machine_version().max_cores_per_chip + n_cores = min(chip_info.n_cores, n_cores) core_states = chip_info.core_states down_cores = self._ignore_cores_map.get( (chip_info.x, chip_info.y), None) @@ -244,7 +246,8 @@ def get_machine_details( logger.warning( "Chip {}, {} was expected but didn't reply", x, y) - machine = machine_from_size(width, height) + version = SpiNNManDataView.get_machine_version() + machine = version.create_machine(width, height) self._preprocess_ignore_chips(machine) self._process_ignore_links(machine) self._preprocess_ignore_cores(machine) diff --git a/spinnman/processes/write_memory_flood_process.py b/spinnman/processes/write_memory_flood_process.py deleted file mode 100644 index da332eb48..000000000 --- a/spinnman/processes/write_memory_flood_process.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2015 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 math -from typing import Optional, BinaryIO -from spinnman.messages.scp.impl import ( - FloodFillEnd, FloodFillStart, FloodFillData) -from .abstract_multi_connection_process import AbstractMultiConnectionProcess -from spinnman.constants import UDP_MESSAGE_MAX_SIZE -from spinnman.messages.scp.impl import CheckOKResponse -from .abstract_multi_connection_process_connection_selector import ( - ConnectionSelector) - - -class WriteMemoryFloodProcess(AbstractMultiConnectionProcess[CheckOKResponse]): - """ - A process for writing memory on multiple SpiNNaker chips at once. - """ - __slots__ = () - - def __init__(self, connection_selector: ConnectionSelector): - super().__init__( - connection_selector, n_channels=3, intermediate_channel_waits=2) - - def _start_flood_fill(self, n_bytes: int, nearest_neighbour_id: int): - n_blocks = int(math.ceil(math.ceil(n_bytes / 4.0) / - UDP_MESSAGE_MAX_SIZE)) - with self._collect_responses(): - self._send_request( - FloodFillStart(nearest_neighbour_id, n_blocks)) - - def _end_flood_fill(self, nearest_neighbour_id: int): - with self._collect_responses(): - self._send_request(FloodFillEnd(nearest_neighbour_id)) - - def write_memory_from_bytearray( - self, nearest_neighbour_id: int, base_address: int, - data: bytes, offset: int = 0, n_bytes: Optional[int] = None): - """ - :param int nearest_neighbour_id: - :param int base_address: - :param data: - :type data: bytes or bytearray - :param int offset: - :param int n_bytes: - """ - # pylint: disable=too-many-arguments - if n_bytes is None: - n_bytes = len(data) - self._start_flood_fill(n_bytes, nearest_neighbour_id) - - data_offset = offset - offset = base_address - block_no = 0 - with self._collect_responses(): - while n_bytes > 0: - bytes_to_send = min(int(n_bytes), UDP_MESSAGE_MAX_SIZE) - - self._send_request(FloodFillData( - nearest_neighbour_id, block_no, offset, - data, data_offset, bytes_to_send)) - - block_no += 1 - n_bytes -= bytes_to_send - offset += bytes_to_send - data_offset += bytes_to_send - - self._end_flood_fill(nearest_neighbour_id) - - def write_memory_from_reader( - self, nearest_neighbour_id: int, base_address: int, - reader: BinaryIO, n_bytes: int): - """ - :param int nearest_neighbour_id: - :param int base_address: - :param reader: - :type reader: ~io.RawIOBase or ~io.BufferedIOBase - :param int n_bytes: - """ - self._start_flood_fill(n_bytes, nearest_neighbour_id) - - offset = base_address - block_no = 0 - with self._collect_responses(): - while n_bytes > 0: - data_array = reader.read( - min(int(n_bytes), UDP_MESSAGE_MAX_SIZE)) - bytes_to_send = len(data_array) - if not bytes_to_send: - # EOF? - break - - self._send_request(FloodFillData( - nearest_neighbour_id, block_no, offset, - data_array, 0, bytes_to_send)) - - block_no += 1 - n_bytes -= bytes_to_send - offset += bytes_to_send - - self._end_flood_fill(nearest_neighbour_id) diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py index 4d80fe9a1..0c204fcd9 100644 --- a/spinnman/spalloc/spalloc_client.py +++ b/spinnman/spalloc/spalloc_client.py @@ -72,12 +72,15 @@ class SpallocClient(ACM, AbstractSpallocClient): Basic client library for talking to new Spalloc. """ __slots__ = ("__session", - "__machines_url", "__jobs_url", "version") + "__machines_url", "__jobs_url", "version", + "__group", "__collab", "__nmpi_job", "__nmpi_user") def __init__( self, service_url: str, username: Optional[str] = None, password: Optional[str] = None, - bearer_token: Optional[str] = None): + bearer_token: Optional[str] = None, + group: Optional[str] = None, collab: Optional[str] = None, + nmpi_job: Optional[str] = None, nmpi_user: Optional[str] = None): """ :param str service_url: The reference to the service. May have username and password supplied as part of the network @@ -98,6 +101,10 @@ def __init__( f"{v['major-version']}.{v['minor-version']}.{v['revision']}") self.__machines_url = obj["machines-ref"] self.__jobs_url = obj["jobs-ref"] + self.__group = group + self.__collab = collab + self.__nmpi_job = nmpi_job + self.__nmpi_user = nmpi_user logger.info("established session to {} for {}", service_url, username) @staticmethod @@ -171,7 +178,15 @@ def _create(self, create, machine_name) -> SpallocJob: create["machine-name"] = machine_name else: create["tags"] = ["default"] - r = self.__session.post(self.__jobs_url, create) + if self.__group is not None: + create["group"] = self.__group + if self.__collab is not None: + create["nmpi-collab"] = self.__collab + if self.__nmpi_job is not None: + create["nmpi-job-id"] = self.__nmpi_job + if self.__nmpi_user is not None: + create["owner"] = self.__nmpi_user + r = self.__session.post(self.__jobs_url, create, timeout=30) url = r.headers["Location"] return _SpallocJob(self.__session, url) diff --git a/spinnman/spinnman.cfg b/spinnman/spinnman.cfg index 118829780..e530f855f 100644 --- a/spinnman/spinnman.cfg +++ b/spinnman/spinnman.cfg @@ -13,15 +13,13 @@ report_waiting_logs = False turn_off_machine = False # format is: -# bmp_names = [:]* -# = [[;];][/(|board_id[,board_id]*)] +# bmp_names = [/(|board_id[,board_id]*) # = - # where: -# is the ID of a cabinet -# is the ID of a frame in a cabinet # is the hostname or IP address of the BMP # is a range of boards that the BMP can speak to # is the ID of a single board in a frame +# Note this no longer supports multiple host nor cabinet or frame bmp_names = None auto_detect_bmp = False diff --git a/spinnman/transceiver.py b/spinnman/transceiver.py index 085f983d8..754e93b6f 100644 --- a/spinnman/transceiver.py +++ b/spinnman/transceiver.py @@ -22,7 +22,7 @@ import random import socket import struct -from threading import Condition, RLock +from threading import Condition import time from typing import ( BinaryIO, Collection, Dict, FrozenSet, Iterable, @@ -31,12 +31,10 @@ 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_utilities.logger_utils import warn_once from spinn_utilities.progress_bar import ProgressBar from spinn_utilities.typing.coords import XY from spinn_machine import ( CoreSubsets, FixedRouteEntry, Machine, MulticastRoutingEntry) -from spinn_machine.spinnaker_triad_geometry import SpiNNakerTriadGeometry from spinn_machine.tags import AbstractTag, IPTag, ReverseIPTag from spinnman.constants import ( BMP_POST_POWER_ON_SLEEP_TIME, BMP_POWER_ON_TIMEOUT, BMP_TIMEOUT, @@ -53,34 +51,34 @@ SpinnmanUnexpectedResponseCodeException, SpiNNManCoresNotInStateException) from spinnman.model import ( - ADCInfo, CPUInfo, CPUInfos, DiagnosticFilter, - ChipSummaryInfo, ExecutableTargets, - HeapElement, IOBuffer, MachineDimensions, RouterDiagnostics, VersionInfo) + CPUInfo, CPUInfos, DiagnosticFilter, ChipSummaryInfo, + IOBuffer, MachineDimensions, RouterDiagnostics, VersionInfo) from spinnman.model.enums import ( CPUState, SDP_PORTS, SDP_RUNNING_MESSAGE_CODES, UserRegister) from spinnman.messages.scp.abstract_messages import ( AbstractSCPRequest, AbstractSCPResponse) -from spinnman.messages.scp.enums import Signal, PowerCommand, LEDAction -from spinnman.messages.scp.impl import ( - BMPSetLed, BMPGetVersion, SetPower, ReadADC, ReadFPGARegister, - WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, ReadMemory, - CountState, WriteMemory, SetLED, SendSignal, AppStop, CheckOKResponse, - IPTagSet, IPTagClear, RouterClear, DoSync, GetChipInfo) -from spinnman.messages.scp.impl.get_chip_info_response import ( - GetChipInfoResponse) +from spinnman.messages.scp.enums import Signal, PowerCommand +from spinnman.messages.scp.impl.get_chip_info import GetChipInfo from spinnman.messages.sdp import SDPFlag, SDPHeader, SDPMessage from spinnman.messages.spinnaker_boot import ( SystemVariableDefinition, SpinnakerBootMessages) +from spinnman.messages.scp.impl import ( + BMPGetVersion, SetPower, ReadFPGARegister, + WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, + CountState, WriteMemory, SendSignal, AppStop, CheckOKResponse, + IPTagSet, IPTagClear, RouterClear, DoSync) +from spinnman.messages.scp.impl.get_chip_info_response import ( + GetChipInfoResponse) from spinnman.connections.abstract_classes import Connection from spinnman.connections.udp_packet_connections import ( BMPConnection, BootConnection, SCAMPConnection, SDPConnection) from spinnman.processes import ( - DeAllocSDRAMProcess, GetMachineProcess, GetVersionProcess, + GetMachineProcess, GetVersionProcess, MallocSDRAMProcess, WriteMemoryProcess, ReadMemoryProcess, GetCPUInfoProcess, GetExcludeCPUInfoProcess, GetIncludeCPUInfoProcess, - ReadIOBufProcess, ApplicationRunProcess, GetHeapProcess, + ReadIOBufProcess, ApplicationRunProcess, LoadFixedRouteRoutingEntryProcess, FixedConnectionSelector, - ReadFixedRouteRoutingEntryProcess, WriteMemoryFloodProcess, + ReadFixedRouteRoutingEntryProcess, LoadMultiCastRoutesProcess, GetTagsProcess, GetMultiCastRoutesProcess, SendSingleCommandProcess, ReadRouterDiagnosticsProcess, MostDirectConnectionSelector, ApplicationCopyRunProcess, @@ -119,7 +117,7 @@ def create_transceiver_from_hostname( hostname: Optional[str], version: int, *, - bmp_connection_data: Optional[List[BMPConnectionData]] = None, + bmp_connection_data: Optional[BMPConnectionData] = None, number_of_boards: Optional[int] = None, auto_detect_bmp: bool = False) -> 'Transceiver': """ @@ -139,7 +137,7 @@ def create_transceiver_from_hostname( :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: + :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 @@ -171,17 +169,15 @@ def create_transceiver_from_hostname( if number_of_boards is None or number_of_boards < 1: raise ValueError( "number_of_boards is required if deriving BMP details") - bmp_connection_data = [ - work_out_bmp_from_machine_details(hostname, number_of_boards)] + 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[Optional[str]] = [] - 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) + 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)) @@ -209,18 +205,15 @@ class Transceiver(AbstractContextManager): """ __slots__ = ( "_all_connections", - "_bmp_connection_selectors", - "_bmp_connections", + "_bmp_selector", + "_bmp_connection", "_boot_send_connection", "_chip_execute_lock_condition", "_chip_execute_locks", - "_flood_write_lock", "_height", "_iobuf_size", "_machine_off", "_n_chip_execute_locks", - "_nearest_neighbour_id", - "_nearest_neighbour_lock", "_scamp_connection_selector", "_scamp_connections", "_udp_scamp_connections", @@ -271,21 +264,13 @@ def __init__(self, version: int, self._scamp_connections: List[SCAMPConnection] = list() # The BMP connections - self._bmp_connections: List[BMPConnection] = list() + self._bmp_connection: Optional[BMPConnection] = None # build connection selectors for the processes. - self._bmp_connection_selectors: Dict[ - Tuple[int, int], FixedConnectionSelector[BMPConnection]] = dict() + self._bmp_selector: Optional[ + FixedConnectionSelector[BMPConnection]] = None self._scamp_connection_selector = \ - self._identify_connections(connections) - - # The nearest neighbour start ID and lock - self._nearest_neighbour_id = 1 - self._nearest_neighbour_lock = RLock() - - # A lock against multiple flood fill writes - needed as SCAMP cannot - # cope with this - self._flood_write_lock = Condition() + self.__identify_connections(connections) # A lock against single chip executions (entry is (x, y)) # The condition should be acquired before the locks are @@ -298,7 +283,7 @@ def __init__(self, version: int, self._n_chip_execute_locks = 0 # Check that the BMP connections are valid - self._check_bmp_connections() + self.__check_bmp_connection() self._machine_off = False @@ -321,7 +306,7 @@ def __where_is_xy(self, x: int, y: int) -> str: except Exception as ex: # pylint: disable=broad-except return str(ex) - def _identify_connections( + def __identify_connections( self, connections: Iterable[Connection] ) -> MostDirectConnectionSelector: for conn in connections: @@ -337,9 +322,11 @@ def _identify_connections( # Locate any connections that talk to a BMP if isinstance(conn, BMPConnection): # If it is a BMP conn, add it here - self._bmp_connections.append(conn) - self._bmp_connection_selectors[conn.cabinet, conn.frame] =\ - FixedConnectionSelector(conn) + if self._bmp_connection is not None: + raise NotImplementedError( + "Only one BMP connection supported") + self._bmp_connection = conn + self._bmp_selector = FixedConnectionSelector(conn) # Otherwise, check if it can send and receive SCP (talk to SCAMP) elif isinstance(conn, SCAMPConnection): self._scamp_connections.append(conn) @@ -347,7 +334,7 @@ def _identify_connections( # update the transceiver with the conn selectors. return MostDirectConnectionSelector(self._scamp_connections) - def _check_bmp_connections(self) -> None: + def __check_bmp_connection(self) -> None: """ Check that the BMP connections are actually connected to valid BMPs. @@ -355,13 +342,13 @@ def _check_bmp_connections(self) -> None: """ # check that the UDP BMP conn is actually connected to a BMP # via the sver command - for conn in self._bmp_connections: + if self._bmp_connection is not None: + conn = self._bmp_connection # try to send a BMP sver to check if it responds as expected try: - version_info = self.get_scamp_version( - conn.chip_x, conn.chip_y, - self._bmp_connection_selectors[conn.cabinet, conn.frame]) + version_info = self._get_scamp_version( + conn.chip_x, conn.chip_y, self._bmp_selector) fail_version_name = version_info.name != _BMP_NAME fail_version_num = \ version_info.version_number[0] not in _BMP_MAJOR_VERSIONS @@ -420,39 +407,7 @@ def _check_connection( return None @contextmanager - def _chip_execute_lock(self, x: int, y: int) -> Iterator[Condition]: - """ - Get a lock for executing an executable on a chip. - - .. warning:: - This method is currently deprecated and untested as there is no - known use except for execute, which is itself deprecated. - - :param int x: - :param int y: - """ - # Check if there is a lock for the given chip - with self._chip_execute_lock_condition: - chip_lock = self._chip_execute_locks[x, y] - # Acquire the lock for the chip - chip_lock.acquire() - - # Increment the lock counter (used for the flood lock) - with self._chip_execute_lock_condition: - self._n_chip_execute_locks += 1 - - try: - yield chip_lock - finally: - with self._chip_execute_lock_condition: - # Release the chip lock - chip_lock.release() - # Decrement the lock and notify - self._n_chip_execute_locks -= 1 - self._chip_execute_lock_condition.notify_all() - - @contextmanager - def _flood_execute_lock(self) -> Iterator[Condition]: + def __flood_execute_lock(self) -> Iterator[Condition]: """ Get a lock for executing a flood fill of an executable. """ @@ -478,34 +433,6 @@ def _get_random_connection( return None return connections[random.randint(0, len(connections) - 1)] - def send_scp_message( - self, message: AbstractSCPRequest, - connection: Optional[SCAMPConnection] = None): - """ - Sends an SCP message, without expecting a response. - - :param AbstractSCPRequest message: The message to send - :param SCAMPConnection connection: - The connection to use (omit to pick a random one) - :raise SpinnmanTimeoutException: - If there is a timeout before a message is received - :raise SpinnmanInvalidParameterException: - If one of the fields of the received message is invalid - :raise SpinnmanInvalidPacketException: - * If the message is not a recognised packet type - * If a packet is received that is not a valid response - :raise SpinnmanUnsupportedOperationException: - If no connection can send the type of message given - :raise SpinnmanIOException: - If there is an error sending the message or receiving the response - :raise SpinnmanUnexpectedResponseCodeException: - If the response is not one of the expected codes - """ - if connection is None: - connection = self._get_random_connection(self._scamp_connections) - if connection is not None: - connection.send_scp_request(message) - def send_sdp_message( self, message: SDPMessage, connection: Optional[SDPConnection] = None): @@ -575,11 +502,11 @@ def discover_scamp_connections(self) -> None: return # Get the machine dimensions - dims = self.get_machine_dimensions() + dims = self._get_machine_dimensions() # Find all the new connections via the machine Ethernet-connected chips - geometry = SpiNNakerTriadGeometry.get_spinn5_geometry() - for x, y in geometry.get_potential_ethernet_chips( + version = SpiNNManDataView.get_machine_version() + for x, y in version.get_potential_ethernet_chips( dims.width, dims.height): ip_addr_item = SystemVariableDefinition.ethernet_ip_address try: @@ -620,18 +547,7 @@ def add_scamp_connections(self, connections: Dict[XY, str]): self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) - def get_connections(self) -> Iterable[Connection]: - """ - 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: iterable(Connection) - """ - return self._all_connections - - def get_machine_dimensions(self) -> MachineDimensions: + def _get_machine_dimensions(self) -> MachineDimensions: """ Get the maximum chip X-coordinate and maximum chip Y-coordinate of the chips in the machine. @@ -675,10 +591,10 @@ def get_machine_details(self) -> Machine: If a response indicates an error during the exchange """ # Get the width and height of the machine - dims = self.get_machine_dimensions() + dims = self._get_machine_dimensions() # Get the coordinates of the boot chip - version_info = self.get_scamp_version() + version_info = self._get_scamp_version() # Get the details of all the chips get_machine_process = GetMachineProcess( @@ -691,27 +607,10 @@ def get_machine_details(self) -> Machine: machine.add_fpga_links() if self._boot_send_connection: - logger.info("Detected a machine on IP address {} which has {}", - machine.boot_chip.ip_address, - machine.cores_and_link_output_string()) + logger.info(f"Detected {machine.summary_string()}") return machine - def is_connected(self, connection: Optional[Connection] = None) -> bool: - """ - Determines if the board can be contacted. - - :param Connection connection: - The connection which is to be tested. If `None`, - all 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) - - def get_scamp_version( + def _get_scamp_version( self, chip_x: int = AbstractSCPRequest.DEFAULT_DEST_X_COORD, chip_y: int = AbstractSCPRequest.DEFAULT_DEST_Y_COORD, connection_selector: Optional[ConnectionSelector] = None, *, @@ -740,7 +639,7 @@ def get_scamp_version( process = GetVersionProcess(connection_selector, n_retries) return process.get_version(x=chip_x, y=chip_y, p=0) - def boot_board(self, extra_boot_values: Optional[Dict[ + def _boot_board(self, extra_boot_values: Optional[Dict[ SystemVariableDefinition, object]] = None): """ Attempt to boot the board. No check is performed to see if the @@ -748,6 +647,9 @@ def boot_board(self, extra_boot_values: Optional[Dict[ :param dict(SystemVariableDefinition,object) extra_boot_values: extra values to set during boot + 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. :raise SpinnmanInvalidParameterException: If the board version is not known :raise SpinnmanIOException: @@ -772,7 +674,7 @@ def __call(self, req: AbstractSCPRequest[R], **kwargs) -> R: return proc.execute(req) @staticmethod - def is_scamp_version_compabible(version: Tuple[int, int, int]) -> bool: + def _is_scamp_version_compabible(version: Tuple[int, int, int]) -> bool: """ Determine if the version of SCAMP is compatible with this transceiver. @@ -803,7 +705,9 @@ def ensure_board_is_ready( :param int n_retries: The number of times to retry booting :param dict(SystemVariableDefinition,object) extra_boot_values: - Any additional values to set during boot + 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: @@ -820,10 +724,10 @@ def ensure_board_is_ready( INITIAL_FIND_SCAMP_RETRIES_COUNT, extra_boot_values) # If we fail to get a SCAMP version this time, try other things - if version_info is None and self._bmp_connections: + if version_info is None and self._bmp_connection is not None: # start by powering up each BMP connection logger.info("Attempting to power on machine") - self.power_on_machine() + self._power_on_machine() # Sleep a bit to let things get going time.sleep(2.0) @@ -837,7 +741,7 @@ def ensure_board_is_ready( if version_info is None: raise SpinnmanIOException("Failed to communicate with the machine") if (version_info.name != _SCAMP_NAME or - not self.is_scamp_version_compabible( + not self._is_scamp_version_compabible( version_info.version_number)): raise SpinnmanIOException( f"The machine is currently booted with {version_info.name}" @@ -877,9 +781,10 @@ def _try_to_find_scamp_and_boot( :param int tries_to_go: how many attempts should be supported :param dict(SystemVariableDefinition,object) extra_boot_values: - Any additional values to set during boot - :return: version info, or `None` if we couldn't boot anything - because of timeouts + 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: version info :rtype: VersionInfo :raise SpinnmanIOException: If there is a problem communicating with the machine @@ -888,14 +793,14 @@ def _try_to_find_scamp_and_boot( current_tries_to_go = tries_to_go while version_info is None and current_tries_to_go > 0: try: - version_info = self.get_scamp_version(n_retries=BOOT_RETRIES) + version_info = self._get_scamp_version(n_retries=BOOT_RETRIES) if self.__is_default_destination(version_info): version_info = None time.sleep(0.1) except SpinnmanGenericProcessException as e: if isinstance(e.exception, SpinnmanTimeoutException): logger.info("Attempting to boot machine") - self.boot_board(extra_boot_values) + self._boot_board(extra_boot_values) current_tries_to_go -= 1 elif isinstance(e.exception, SpinnmanIOException): raise SpinnmanIOException( @@ -904,7 +809,7 @@ def _try_to_find_scamp_and_boot( raise except SpinnmanTimeoutException: logger.info("Attempting to boot machine") - self.boot_board(extra_boot_values) + self._boot_board(extra_boot_values) current_tries_to_go -= 1 except SpinnmanIOException as e: raise SpinnmanIOException( @@ -913,7 +818,7 @@ def _try_to_find_scamp_and_boot( # The last thing we tried was booting, so try again to get the version if version_info is None: with suppress(SpinnmanException): - version_info = self.get_scamp_version() + version_info = self._get_scamp_version() if self.__is_default_destination(version_info): version_info = None if version_info is not None: @@ -1113,88 +1018,6 @@ def get_iobuf(self, core_subsets: Optional[CoreSubsets] = None process = ReadIOBufProcess(self._scamp_connection_selector) return process.read_iobuf(self._iobuf_size, core_subsets) - def set_watch_dog_on_chip( - self, x: int, y: int, watch_dog: Union[bool, int]): - """ - 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 = cast(int, WATCHDOG.default) if watch_dog else 0 - - # build data holder, a single byte - 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: Union[bool, int]): - """ - 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: int, y: int, p: int) -> IOBuffer: - """ - Get the contents of IOBUF for a given core. - - .. warning:: - This method is currently deprecated and likely to be removed. - - :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 IOBUF for - :return: An IOBUF buffer - :rtype: 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 - """ - warn_once(logger, "The get_iobuf_from_core method is deprecated and " - "likely to be removed.") - core_subsets = CoreSubsets() - core_subsets.add_processor(x, y, p) - return next(iter(self.get_iobuf(core_subsets))) - def get_core_state_count(self, app_id: int, state: CPUState) -> int: """ Get a count of the number of cores which have a given state. @@ -1217,12 +1040,6 @@ def get_core_state_count(self, app_id: int, state: CPUState) -> int: """ return self.__call(CountState(app_id, state)).count - def _get_next_nearest_neighbour_id(self) -> int: - with self._nearest_neighbour_lock: - next_nearest_neighbour_id = (self._nearest_neighbour_id + 1) % 127 - self._nearest_neighbour_id = next_nearest_neighbour_id - return next_nearest_neighbour_id - def execute_flood( self, core_subsets: CoreSubsets, executable: Union[BinaryIO, bytes, str], app_id: int, *, @@ -1272,7 +1089,7 @@ def execute_flood( # No executable is 4 bytes long raise TypeError("executable may not be int") # 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=n_bytes, @@ -1291,72 +1108,27 @@ def execute_flood( self._scamp_connection_selector) copy_run.run(n_bytes, app_id, core_subsets, chksum, wait) - def execute_application( - self, executable_targets: ExecutableTargets, app_id: int): - """ - Execute a set of binaries that make up a complete application on - specified cores, wait for them to be ready and then start all of the - binaries. - - .. note:: - This will get the binaries into c_main but will not signal the - barrier. - - :param ExecutableTargets executable_targets: - The binaries to be executed and the cores to execute them on - :param int app_id: The app_id to give this application - """ - # Execute each of the binaries and get them in to a "wait" state - for binary in executable_targets.binaries: - core_subsets = executable_targets.get_cores_for_binary(binary) - self.execute_flood(core_subsets, binary, app_id, wait=True) - - # Sleep to allow cores to get going - time.sleep(0.5) - - # Check that the binaries have reached a wait state - count = self.get_core_state_count(app_id, CPUState.READY) - if count < executable_targets.total_processors: - cores_ready = self.get_cpu_infos( - executable_targets.all_core_subsets, CPUState.READY, - include=False) - if len(cores_ready) > 0: - raise SpinnmanException( - f"Only {count} of {executable_targets.total_processors} " - "cores reached ready state: " - f"{cores_ready.get_status_string()}") - - # Send a signal telling the application to start - self.send_signal(app_id, Signal.START) - - def power_on_machine(self) -> bool: + def _power_on_machine(self) -> bool: """ Power on the whole machine. :rtype bool :return success of failure to power on machine """ - if not self._bmp_connections: + if self._bmp_connection is None: logger.warning("No BMP connections, so can't power on") return False - for bmp_connection in self._bmp_connections: - self.power_on(bmp_connection.boards, bmp_connection.cabinet, - bmp_connection.frame) + self.power_on() return True - def power_on(self, boards: Union[int, Iterable[int]] = 0, - cabinet: int = 0, frame: int = 0): + def power_on(self, boards: Union[int, Iterable[int]] = 0): """ Power on a set of boards in the machine. :param boards: The board or boards to power on :type boards: list(int) or int - :param int cabinet: the ID of the cabinet containing the frame, or 0 - if the frame is not in a cabinet - :param int frame: the ID of the frame in the cabinet containing the - board(s), or 0 if the board is not in a frame """ - self._power(PowerCommand.POWER_ON, boards, cabinet, frame) + self._power(PowerCommand.POWER_ON, boards) def power_off_machine(self) -> bool: """ @@ -1365,77 +1137,49 @@ def power_off_machine(self) -> bool: :rtype bool :return success or failure to power off the machine """ - if not self._bmp_connections: + if self._bmp_connection is None: logger.warning("No BMP connections, so can't power off") return False logger.info("Turning off machine") - for bmp_connection in self._bmp_connections: - self.power_off(bmp_connection.boards, bmp_connection.cabinet, - bmp_connection.frame) + self.power_off() return True - def power_off(self, boards: Union[int, Iterable[int]] = 0, - cabinet: int = 0, frame: int = 0): + def power_off(self, boards: Union[int, Iterable[int]] = 0): """ Power off a set of boards in the machine. - :param int boards: The board or boards to power off + :param boards: The board or boards to power off :type boards: list(int) or int - :param int cabinet: the ID of the cabinet containing the frame, or 0 - if the frame is not in a cabinet - :param int frame: the ID of the frame in the cabinet containing the - board(s), or 0 if the board is not in a frame """ - self._power(PowerCommand.POWER_OFF, boards, cabinet, frame) + self._power(PowerCommand.POWER_OFF, boards) - def _bmp_connection( - self, cabinet: int, frame: int) -> FixedConnectionSelector: - """ - :param int cabinet: - :param int frame: - :rtype: FixedConnectionSelector - """ - sel = self._bmp_connection_selectors.get((cabinet, frame)) - if not sel: - raise SpinnmanInvalidParameterException( - "cabinet and frame", f"{cabinet} and {frame}", - "Unknown combination") - return sel - - def __bmp_call( - self, cabinet: int, frame: int, req: AbstractSCPRequest[R], - **kwargs) -> R: + def __bmp_call(self, req: AbstractSCPRequest[R], **kwargs) -> R: """ Wrapper that makes doing simple BMP calls easier, especially with types. """ + if self._bmp_selector is None: + raise SpinnmanException( + "this transceiver does not support BMP operations") proc: SendSingleCommandProcess[R] = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame), **kwargs) + self._bmp_selector, **kwargs) return proc.execute(req) def _power( self, power_command: PowerCommand, - boards: Union[int, Iterable[int]] = 0, - cabinet: int = 0, frame: int = 0): + boards: Union[int, Iterable[int]] = 0): """ Send a power request to the machine. :param PowerCommand power_command: The power command to send :param boards: The board or boards to send the command to :type boards: list(int) or int - :param int cabinet: the ID of the cabinet containing the frame, or 0 - if the frame is not in a cabinet - :param int frame: the ID of the frame in the cabinet containing the - board(s), or 0 if the board is not in a frame - """ - if not self._bmp_connection_selectors: - warn_once( - logger, "No BMP connections so power control unavailable") + """ timeout = ( BMP_POWER_ON_TIMEOUT if power_command == PowerCommand.POWER_ON else BMP_TIMEOUT) - self.__bmp_call(cabinet, frame, SetPower(power_command, boards), + self.__bmp_call(SetPower(power_command, boards), timeout=timeout, n_retries=0) self._machine_off = power_command == PowerCommand.POWER_OFF @@ -1443,36 +1187,8 @@ def _power( if not self._machine_off: time.sleep(BMP_POST_POWER_ON_SLEEP_TIME) - def set_led( - self, led: Union[int, Iterable[int]], action: LEDAction, - board: int, cabinet: int, frame: int): - """ - Set the LED state of a board in the machine. - - .. 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 led: - Number of the LED or an iterable of LEDs to set the state of (0-7) - :type led: int or iterable(int) - :param LEDAction action: - State to set the LED to, either on, off or toggle - :param board: Specifies the board to control the LEDs of. This may - also be an iterable of multiple boards (in the same frame). The - command will actually be sent to the first board in the iterable. - :type board: int or iterable(int) - :param int cabinet: the cabinet this is targeting - :param int frame: the frame this is targeting - """ - warn_once(logger, "The set_led method is deprecated and " - "untested due to no known use.") - self.__bmp_call(cabinet, frame, BMPSetLed(led, action, board)) - def read_fpga_register( - self, fpga_num: int, register: int, cabinet: int, frame: int, - board: int) -> int: + self, fpga_num: int, register: int, board: int = 0) -> int: """ Read a register on a FPGA of a board. The meaning of the register's contents will depend on the FPGA's configuration. @@ -1481,20 +1197,17 @@ def read_fpga_register( :param int register: Register address to read to (will be rounded down to the nearest 32-bit word boundary). - :param int cabinet: cabinet: the cabinet this is targeting - :param int frame: the frame this is targeting :param int board: which board to request the FPGA register from :return: the register data :rtype: int """ response = self.__bmp_call( - cabinet, frame, ReadFPGARegister(fpga_num, register, board), + ReadFPGARegister(fpga_num, register, board), timeout=1.0) return response.fpga_register def write_fpga_register( - self, fpga_num: int, register: int, value: int, - cabinet: int, frame: int, board: int): + self, fpga_num: int, register: int, value: int, board: int = 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. @@ -1504,45 +1217,19 @@ def write_fpga_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 cabinet: cabinet: the cabinet this is targeting - :param int frame: the frame this is targeting :param int board: which board to write the FPGA register to """ self.__bmp_call( - cabinet, frame, WriteFPGARegister(fpga_num, register, value, board)) - def read_adc_data(self, board: int, cabinet: int, frame: int) -> ADCInfo: - """ - Read the BMP ADC data. - - .. 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 cabinet: cabinet: the cabinet this is targeting - :param int frame: the frame this is targeting - :param int board: which board to request the ADC data from - :return: the FPGA's ADC data object - :rtype: ADCInfo - """ - warn_once(logger, "The read_adc_data method is deprecated and " - "untested due to no known use.") - response = self.__bmp_call(cabinet, frame, ReadADC(board)) - return response.adc_info - - def read_bmp_version( - self, board: int, cabinet: int, frame: int) -> VersionInfo: + def read_bmp_version(self, board: int) -> VersionInfo: """ Read the BMP version. - :param int cabinet: cabinet: the cabinet this is targeting - :param int frame: the frame this is targeting :param int board: which board to request the data from :return: the sver from the BMP """ - response = self.__bmp_call(cabinet, frame, BMPGetVersion(board)) + response = self.__bmp_call(BMPGetVersion(board)) return response.version_info def write_memory( @@ -1646,151 +1333,6 @@ def write_user( addr = self.__get_user_register_address_from_core(p, user) self.write_memory(x, y, addr, int(value)) - def write_neighbour_memory( - self, x: int, y: int, link: int, base_address: int, - data: Union[BinaryIO, bytes, int], *, - n_bytes: Optional[int] = None, offset: int = 0, cpu: int = 0): - """ - Write to the memory of a neighbouring chip using a LINK_READ SCP - command. If sent to a BMP, this command can be used to communicate - with the FPGAs' debug registers. - - .. warning:: - This method is deprecated and untested due to no known use. - - :param int x: - The x-coordinate of the chip whose neighbour is to be written to - :param int y: - The y-coordinate of the chip whose neighbour is to be written to - :param int link: - The link index to send the request to (or if BMP, the FPGA number) - :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 - :type data: - ~io.RawIOBase or bytes or bytearray or int - :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 - :param int offset: - The offset where the valid data starts (if `data` is - an int then offset will be ignored and used 0) - :param int cpu: - The CPU to use, typically 0 (or if a BMP, the slot number) - :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 - """ - warn_once(logger, "The write_neighbour_memory method is deprecated " - "and untested due to no known use.") - process = WriteMemoryProcess(self._scamp_connection_selector) - if isinstance(data, io.RawIOBase): - assert n_bytes is not None - process.write_link_memory_from_reader( - x, y, cpu, link, base_address, cast(BinaryIO, data), n_bytes) - elif isinstance(data, int): - n_bytes = 4 - data_to_write = _ONE_WORD.pack(data) - process.write_link_memory_from_bytearray( - x, y, cpu, link, base_address, data_to_write, 0, 4) - else: - assert isinstance(data, (bytes, bytearray)) - if n_bytes is None: - n_bytes = len(data) - process.write_link_memory_from_bytearray( - x, y, cpu, link, base_address, data, offset, n_bytes) - - def write_memory_flood( - self, base_address: int, data: Union[BinaryIO, bytes, int, str], *, - n_bytes: Optional[int] = None, offset: int = 0): - """ - Write to the SDRAM of all chips. - - :param int base_address: - The address in SDRAM where the region of memory is to be written - :param data: - The data that is to be written. Should be one of the following: - - * An instance of RawIOBase - * A byte-string - * A single integer - * A file name of a file to read - :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 or bytes, the length of the bytearray - will be used - * If `data` is an int, 4 will be used - * If `data` is a str, the size of the file will be used - :param int offset: - The offset where the valid data starts; if `data` is - an int, then the offset will be ignored and 0 is used. - :param bool is_filename: - True if `data` should be interpreted as a file name - :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 chips is not valid - * If `app_id` is an invalid application ID - * If a packet is received that has invalid parameters - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - process = WriteMemoryFloodProcess(self._scamp_connection_selector) - # Ensure only one flood fill occurs at any one time - with self._flood_write_lock: - # Start the flood fill - nearest_neighbour_id = self._get_next_nearest_neighbour_id() - if isinstance(data, io.RawIOBase): - if n_bytes is None: - raise SpinnmanInvalidParameterException( - "n_bytes", n_bytes, - "must not be None when an IO stream is used") - process.write_memory_from_reader( - nearest_neighbour_id, base_address, cast(BinaryIO, data), - n_bytes) - elif isinstance(data, str): - if n_bytes is None: - n_bytes = os.stat(data).st_size - with open(data, "rb") as reader: - process.write_memory_from_reader( - nearest_neighbour_id, base_address, reader, n_bytes) - elif isinstance(data, int): - data_to_write = _ONE_WORD.pack(data) - process.write_memory_from_bytearray( - nearest_neighbour_id, base_address, data_to_write) - else: - assert isinstance(data, (bytes, bytearray)) - if n_bytes is None: - n_bytes = len(data) - process.write_memory_from_bytearray( - nearest_neighbour_id, base_address, data, offset, n_bytes) - def read_memory( self, x: int, y: int, base_address: int, length: int, *, cpu: int = 0) -> bytes: @@ -1861,52 +1403,6 @@ def read_word( logger.info(self.__where_is_xy(x, y)) raise - def read_neighbour_memory( - self, x: int, y: int, link: int, base_address: int, length: int, *, - cpu: int = 0) -> bytes: - """ - Read some areas of memory on a neighbouring chip using a LINK_READ - SCP command. If sent to a BMP, this command can be used to - communicate with the FPGAs' debug registers. - - .. 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: - The x-coordinate of the chip whose neighbour is to be read from - :param int y: - The y-coordinate of the chip whose neighbour is to be read from - :param int link: - The link index to send the request to (or if BMP, the FPGA number) - :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 CPU to use, typically 0 (or if a BMP, the slot number) - :return: An iterable of chunks of data read in order - :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: - warn_once(logger, "The read_neighbour_memory method is deprecated " - "and untested due to no known use.") - 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)) - raise - def stop_application(self, app_id: int): """ Sends a stop request for an app_id. @@ -2062,7 +1558,7 @@ def send_signal(self, app_id: int, signal: Signal): Send a signal to an application. :param int app_id: The ID of the application to send to - :param Signal signal: The signal to send + :param ~spinnman.messages.scp.enums.Signal signal: The signal to send :raise SpinnmanIOException: If there is an error communicating with the board :raise SpinnmanInvalidPacketException: @@ -2076,37 +1572,7 @@ def send_signal(self, app_id: int, signal: Signal): """ self.__call(SendSignal(app_id, signal)) - def set_leds(self, x: int, y: int, cpu: int, led_states): - """ - Set LED states. - - .. warning:: - The set_leds is deprecated and untested due to no known use. - - :param int x: The x-coordinate of the chip on which to set the LEDs - :param int y: The x-coordinate of the chip on which to set the LEDs - :param int cpu: The CPU of the chip on which to set the LEDs - :param dict(int,int) led_states: - A dictionary mapping SetLED index to state with - 0 being off, 1 on and 2 inverted. - :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: - warn_once(logger, "The set_leds is deprecated and " - "untested due to no known use.") - self.__call(SetLED(x, y, cpu, led_states)) - except Exception: - logger.info(self.__where_is_xy(x, y)) - raise - - def locate_spinnaker_connection_for_board_address( + def _locate_spinnaker_connection_for_board_address( self, board_address: str) -> Optional[SCAMPConnection]: """ Find a connection that matches the given board IP address. @@ -2186,7 +1652,7 @@ def __get_connection_list( elif board_address is None: return self._scamp_connections - connection = self.locate_spinnaker_connection_for_board_address( + connection = self._locate_spinnaker_connection_for_board_address( board_address) if connection is None: return [] @@ -2318,52 +1784,6 @@ def malloc_sdram( logger.info(self.__where_is_xy(x, y)) raise - def free_sdram(self, x: int, y: int, base_address: int, app_id: int): - """ - Free allocated SDRAM. - - .. warning:: - This method is currently deprecated and likely to be removed. - - :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 base_address: The base address of the allocated memory - :param int app_id: The app ID of the allocated memory - """ - try: - warn_once(logger, "The free_sdram method is deprecated and " - "likely to be removed.") - 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)) - raise - - def free_sdram_by_app_id(self, x: int, y: int, app_id: int): - """ - Free all SDRAM allocated to a given app ID. - - .. 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: 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 app_id: The app ID of the allocated memory - :return: The number of blocks freed - :rtype: int - """ - try: - warn_once(logger, "The free_sdram_by_app_id method is deprecated " - "and untested due to no known use.") - process = DeAllocSDRAMProcess(self._scamp_connection_selector) - process.de_alloc_sdram(x, y, app_id) - return process.no_blocks_freed - except Exception: - logger.info(self.__where_is_xy(x, y)) - raise - def load_multicast_routes( self, x: int, y: int, routes: Collection[MulticastRoutingEntry], app_id: int): @@ -2543,7 +1963,7 @@ def set_router_diagnostic_filter( :param int position: The position in the list of filters where this filter is to be added. - :param DiagnosticFilter diagnostic_filter: + :param ~spinnman.model.DiagnosticFilter diagnostic_filter: The diagnostic filter being set in the placed, between 0 and 15. .. note:: @@ -2590,46 +2010,6 @@ def __set_router_diagnostic_filter( self.__call(WriteMemory( x, y, memory_position, _ONE_WORD.pack(data_to_send))) - def get_router_diagnostic_filter( - self, x: int, y: int, position: int) -> DiagnosticFilter: - """ - Gets a router diagnostic filter from a router. - - :param int x: - the X address of the router from which this filter is being - retrieved - :param int y: - the Y address of the router from which this filter is being - retrieved - :param int position: - the position in the list of filters to read the information from - :return: The diagnostic filter read - :rtype: DiagnosticFilter - :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 position is less than 0 or more than 15 - :raise SpinnmanUnexpectedResponseCodeException: - If a response indicates an error during the exchange - """ - try: - memory_position = ( - ROUTER_REGISTER_BASE_ADDRESS + ROUTER_FILTER_CONTROLS_OFFSET + - position * ROUTER_DIAGNOSTIC_FILTER_SIZE) - - response = self.__call(ReadMemory(x, y, memory_position, 4)) - return DiagnosticFilter.read_from_int(_ONE_WORD.unpack_from( - response.data, response.offset)[0]) - # pylint: disable=no-member - except Exception: - logger.info(self.__where_is_xy(x, y)) - raise - def clear_router_diagnostic_counters(self, x: int, y: int): """ Clear router diagnostic information on a chip. @@ -2654,71 +2034,17 @@ def clear_router_diagnostic_counters(self, x: int, y: int): logger.info(self.__where_is_xy(x, y)) raise - @property - def number_of_boards_located(self) -> int: - """ - The number of boards currently configured. - - .. warning:: - This property is currently deprecated and likely to be removed. - - :rtype: int - """ - warn_once(logger, "The number_of_boards_located method is deprecated " - "and likely to be removed.") - boards = 0 - for bmp_connection in self._bmp_connections: - boards += len(bmp_connection.boards) - - # if no BMPs are available, then there's still at least one board - return max(1, boards) - def close(self) -> None: """ Close the transceiver and any threads that are running. """ - if self._bmp_connections: + if self._bmp_connection is not None: if get_config_bool("Machine", "turn_off_machine"): self.power_off_machine() for connection in self._all_connections: connection.close() - @property - def bmp_connection(self) -> Dict[Tuple[int, int], FixedConnectionSelector]: - """ - The BMP connections. - - .. warning:: - This property is currently deprecated and likely to be removed. - - :rtype: dict(tuple(int,int),FixedConnectionSelector) - """ - warn_once(logger, "The bmp_connection property is deprecated and " - "likely to be removed.") - return self._bmp_connection_selectors - - def get_heap( - self, x: int, y: int, - heap: SystemVariableDefinition = ( - SystemVariableDefinition.sdram_heap_address) - ) -> Sequence[HeapElement]: - """ - Get the contents of the given heap on a given chip. - - :param int x: The x-coordinate of the chip - :param int y: The y-coordinate of the chip - :param SystemVariableDefinition heap: - The SystemVariableDefinition which is the heap to read - :rtype: list(HeapElement) - """ - try: - process = GetHeapProcess(self._scamp_connection_selector) - return process.get_heap((x, y), heap) - except Exception: - logger.info(self.__where_is_xy(x, y)) - raise - def control_sync(self, do_sync: bool): """ Control the synchronisation of the chips. diff --git a/spinnman/utilities/utility_functions.py b/spinnman/utilities/utility_functions.py index c81355a66..af0d85f87 100644 --- a/spinnman/utilities/utility_functions.py +++ b/spinnman/utilities/utility_functions.py @@ -46,7 +46,7 @@ def work_out_bmp_from_machine_details( board_range = range(number_of_boards) if number_of_boards else [0] # Assume a single board with no cabinet or frame specified - return BMPConnectionData(cabinet=0, frame=0, ip_address=bmp_ip_address, + return BMPConnectionData(ip_address=bmp_ip_address, boards=board_range, port_num=SCP_SCAMP_PORT) diff --git a/unittests/connection_tests/test_udp_connection.py b/unittests/connection_tests/test_udp_connection.py index ad4efa069..3aedd39b4 100644 --- a/unittests/connection_tests/test_udp_connection.py +++ b/unittests/connection_tests/test_udp_connection.py @@ -21,18 +21,17 @@ from spinnman.messages.scp.impl.get_version_response import GetVersionResponse from spinnman.board_test_configuration import BoardTestConfiguration -board_config = BoardTestConfiguration() - class TestUDPConnection(unittest.TestCase): def setUp(self): unittest_setup() + self.board_config = BoardTestConfiguration() def test_scp_version_request_and_response_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_req = GetVersion(0, 0, 0) scp_response = GetVersionResponse() connection.send_scp_request(scp_req) @@ -43,18 +42,18 @@ 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): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_link = ReadLink(0, 0, 0, 0x70000000, 250) connection.send_scp_request(scp_link) result, _, _, _ = connection.receive_scp_response() self.assertEqual(result, SCPResult.RC_OK) def test_scp_read_memory_request_and_response_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_link = ReadMemory(0, 0, 0x70000000, 256) connection.send_scp_request(scp_link) result, _, _, _ = connection.receive_scp_response() @@ -62,9 +61,9 @@ def test_scp_read_memory_request_and_response_board(self): def test_send_scp_request_to_nonexistent_host(self): with self.assertRaises(SpinnmanTimeoutException): - board_config.set_up_nonexistent_board() + self.board_config.set_up_nonexistent_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp = ReadMemory(0, 0, 0, 256) connection.send_scp_request(scp) _, _, _, _ = connection.receive_scp_response(2) diff --git a/unittests/data/test_data.py b/unittests/data/test_data.py index 4052471d4..29c6cf503 100644 --- a/unittests/data/test_data.py +++ b/unittests/data/test_data.py @@ -13,6 +13,7 @@ # limitations under the License. import unittest +from spinn_utilities.config_holder import set_config from spinn_utilities.exceptions import (DataNotYetAvialable) from spinnman.config_setup import unittest_setup from spinnman.data import SpiNNManDataView @@ -47,6 +48,7 @@ def test_setup(self): def test_mock(self): SpiNNManDataWriter.mock() + set_config("Machine", "version", 5) # check there is a # value not what it is SpiNNManDataView.get_machine() diff --git a/unittests/model_tests/test_abstract_process.py b/unittests/model_tests/test_abstract_process.py index b9705da20..ce2958226 100644 --- a/unittests/model_tests/test_abstract_process.py +++ b/unittests/model_tests/test_abstract_process.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from spinn_utilities.config_holder import set_config from spinnman.processes.abstract_multi_connection_process import ( AbstractMultiConnectionProcess) from spinnman.messages.scp.impl import ReadMemory @@ -39,6 +40,7 @@ def receive_scp_response(self, timeout=1.0): def test_error_print(): unittest_setup() + set_config("Machine", "version", 5) connection = MockConnection(0, 0) process = MockProcess(RoundRobinConnectionSelector([connection])) with pytest.raises(SpinnmanGenericProcessException): diff --git a/unittests/model_tests/test_iptag_model.py b/unittests/model_tests/test_iptag_model.py index 1ffcca225..e870b6424 100644 --- a/unittests/model_tests/test_iptag_model.py +++ b/unittests/model_tests/test_iptag_model.py @@ -17,8 +17,6 @@ from spinnman.board_test_configuration import BoardTestConfiguration from spinnman.config_setup import unittest_setup -board_config = BoardTestConfiguration() - class TestIptag(unittest.TestCase): @@ -26,6 +24,7 @@ def setUp(self): unittest_setup() def test_new_iptag(self): + board_config = BoardTestConfiguration() board_config.set_up_remote_board() ip = "8.8.8.8" port = 1337 diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 70cf4331a..76f4f2cd1 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -15,9 +15,12 @@ import unittest import struct from spinn_utilities.overrides import overrides +from spinn_utilities.config_holder import set_config from spinn_machine import virtual_machine from spinnman.config_setup import unittest_setup +from spinnman.data import SpiNNManDataView from spinnman.data.spinnman_data_writer import SpiNNManDataWriter +from spinnman.extended.extended_transceiver import ExtendedTransceiver from spinnman.transceiver import Transceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( @@ -25,9 +28,9 @@ from spinnman.connections.udp_packet_connections import ( BootConnection, SCAMPConnection) import spinnman.transceiver as transceiver +import spinnman.extended.extended_transceiver as extended from spinnman.board_test_configuration import BoardTestConfiguration -board_config = BoardTestConfiguration() ver = 5 # Guess? @@ -39,7 +42,9 @@ def __init__( self.written_memory = list() def get_machine_details(self): - return virtual_machine(2, 2) + version = SpiNNManDataView.get_machine_version() + width, height = version.board_shape + return virtual_machine(width, height) def _update_machine(self): self._machine = self.get_machine_details() @@ -58,75 +63,79 @@ def close(self): pass +class MockExtendedTransceiver(MockWriteTransceiver, ExtendedTransceiver): + pass + + class TestTransceiver(unittest.TestCase): def setUp(self): unittest_setup() + self.board_config = BoardTestConfiguration() def test_create_new_transceiver_to_board(self): - board_config.set_up_remote_board() - connections = list() - connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) + self.board_config.set_up_remote_board() + connections = [ + SCAMPConnection(remote_host=self.board_config.remotehost)] trans = transceiver.Transceiver(ver, connections=connections) trans.close() def test_create_new_transceiver_one_connection(self): - board_config.set_up_remote_board() - connections = {SCAMPConnection(remote_host=board_config.remotehost)} - with transceiver.Transceiver(ver, connections=connections) as trans: - assert trans.get_connections() == connections + self.board_config.set_up_remote_board() + connections = { + SCAMPConnection(remote_host=self.board_config.remotehost)} + with extended.ExtendedTransceiver( + ver, connections=connections) as trans: + assert trans._all_connections == connections def test_create_new_transceiver_from_list_connections(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) - board_config.set_up_local_virtual_board() - connections.append(BootConnection( - remote_host=board_config.remotehost)) + remote_host=self.board_config.remotehost)) + connections.append(BootConnection(remote_host="127.0.0.1")) with transceiver.Transceiver(ver, connections=connections) as trans: - instantiated_connections = trans.get_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): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) - board_config.set_up_local_virtual_board() - connections.append(BootConnection( - remote_host=board_config.remotehost)) + remote_host=self.board_config.remotehost)) + connections.append(BootConnection(remote_host="127.0.0.1")) with transceiver.Transceiver(ver, connections=connections) as trans: SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) - if board_config.board_version in (2, 3): - assert trans.get_machine_dimensions().width == 2 - assert trans.get_machine_dimensions().height == 2 - elif board_config.board_version in (4, 5): - assert trans.get_machine_dimensions().width == 8 - assert trans.get_machine_dimensions().height == 8 + 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() + size = trans._get_machine_dimensions() print(f"Unknown board with size {size.width} x {size.height}") - assert trans.is_connected() - print(trans.get_scamp_version()) + 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): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() with transceiver.create_transceiver_from_hostname( - board_config.remotehost, board_config.board_version) as trans: - # self.assertFalse(trans.is_connected()) - trans.boot_board() + self.board_config.remotehost, + self.board_config.board_version) as trans: + # self.assertFalse(trans.is_connected( unittest_setup())) + trans._boot_board() def test_set_watch_dog(self): + set_config("Machine", "version", 5) connections = [] connections.append(SCAMPConnection(remote_host=None)) - tx = MockWriteTransceiver(version=5, connections=connections) + tx = MockExtendedTransceiver(version=5, connections=connections) SpiNNManDataWriter.mock().set_machine(tx.get_machine_details()) # All chips tx.set_watch_dog(True) diff --git a/unittests/test_version.py b/unittests/test_version.py index fdac35f8f..c8f42fc87 100644 --- a/unittests/test_version.py +++ b/unittests/test_version.py @@ -28,31 +28,31 @@ def setUp(self): unittest_setup() def test_version_same(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2]))) def test_major_version_too_big(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0] + 1, 0, 0))) def test_major_version_too_small(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0] - 1, 0, 0))) def test_minor_version_bigger(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1] + 1, _SCAMP_VERSION[2]))) def test_minor_version_smaller(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1] - 1, _SCAMP_VERSION[2]))) def test_patch_version_bigger(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2] + 1))) def test_patch_version_smaller(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2] - 1))) def test_compare_versions(self):