diff --git a/quick_tests/quick_tests.py b/quick_tests/quick_tests.py index 293cad6f5..89ae9315f 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 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 96% rename from spinnman/messages/scp/impl/bmp_set_led.py rename to spinnman/extended/bmp_set_led.py index c40c98225..c76f2d220 100644 --- a/spinnman/messages/scp/impl/bmp_set_led.py +++ b/spinnman/extended/bmp_set_led.py @@ -17,7 +17,7 @@ from spinnman.messages.scp.abstract_messages import ( AbstractSCPRequest, BMPRequest) from spinnman.messages.scp.enums import SCPCommand -from .check_ok_response import CheckOKResponse +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse class BMPSetLed(BMPRequest): diff --git a/spinnman/processes/de_alloc_sdram_process.py b/spinnman/extended/de_alloc_sdram_process.py similarity index 95% rename from spinnman/processes/de_alloc_sdram_process.py rename to spinnman/extended/de_alloc_sdram_process.py index 53b100828..916a2500d 100644 --- a/spinnman/processes/de_alloc_sdram_process.py +++ b/spinnman/extended/de_alloc_sdram_process.py @@ -14,7 +14,8 @@ # limitations under the License. from spinnman.messages.scp.impl import SDRAMDeAlloc -from .abstract_multi_connection_process import AbstractMultiConnectionProcess +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) class DeAllocSDRAMProcess(AbstractMultiConnectionProcess): diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py new file mode 100644 index 000000000..44b8178c3 --- /dev/null +++ b/spinnman/extended/extended_transceiver.py @@ -0,0 +1,830 @@ +# 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, cabinet, frame): + """ + 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.") + process = SendSingleCommandProcess( + self._bmp_connection(cabinet, frame)) + process.execute(BMPSetLed(led, action, board)) + + def read_adc_data(self, board, cabinet, frame): + """ + 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.") + process = SendSingleCommandProcess( + self._bmp_connection(cabinet, frame)) + 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.") + 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) + + @property + def bmp_connection(self): + """ + The BMP connections. + + .. warning:: + This property is currently deprecated and likely to be removed. + + :rtype: dict(tuple(int,int),MostDirectConnectionSelector) + """ + warn_once(logger, "The bmp_connection property is deprecated and " + "likely to be removed.") + return self._bmp_connection_selectors + + 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 100% rename from spinnman/messages/scp/impl/read_adc.py rename to spinnman/extended/read_adc.py diff --git a/spinnman/messages/scp/impl/set_led.py b/spinnman/extended/set_led.py similarity index 96% rename from spinnman/messages/scp/impl/set_led.py rename to spinnman/extended/set_led.py index 046be4a47..220ae5695 100644 --- a/spinnman/messages/scp/impl/set_led.py +++ b/spinnman/extended/set_led.py @@ -17,7 +17,7 @@ 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): diff --git a/spinnman/processes/write_memory_flood_process.py b/spinnman/extended/write_memory_flood_process.py similarity index 97% rename from spinnman/processes/write_memory_flood_process.py rename to spinnman/extended/write_memory_flood_process.py index fbe3b68b1..4ef491bf7 100644 --- a/spinnman/processes/write_memory_flood_process.py +++ b/spinnman/extended/write_memory_flood_process.py @@ -15,7 +15,8 @@ import math from spinnman.messages.scp.impl import ( FloodFillEnd, FloodFillStart, FloodFillData) -from .abstract_multi_connection_process import AbstractMultiConnectionProcess +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) from spinnman.constants import UDP_MESSAGE_MAX_SIZE 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/processes/__init__.py b/spinnman/processes/__init__.py index a14231574..3e11e3b14 100644 --- a/spinnman/processes/__init__.py +++ b/spinnman/processes/__init__.py @@ -17,7 +17,7 @@ AbstractMultiConnectionProcessConnectionSelector) 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 __all__ = ["AbstractMultiConnectionProcessConnectionSelector", @@ -47,7 +46,7 @@ "RoundRobinConnectionSelector", "AbstractMultiConnectionProcess", "ApplicationRunProcess", "ApplicationCopyRunProcess", - "DeAllocSDRAMProcess", "GetCPUInfoProcess", + "GetCPUInfoProcess", "GetExcludeCPUInfoProcess", "GetIncludeCPUInfoProcess", "GetHeapProcess", "GetMachineProcess", "GetMultiCastRoutesProcess", "GetTagsProcess", @@ -55,5 +54,4 @@ "LoadMultiCastRoutesProcess", "MallocSDRAMProcess", "ReadFixedRouteRoutingEntryProcess", "ReadIOBufProcess", "ReadMemoryProcess", "ReadRouterDiagnosticsProcess", - "SendSingleCommandProcess", "WriteMemoryFloodProcess", - "WriteMemoryProcess"] + "SendSingleCommandProcess", "WriteMemoryProcess"] diff --git a/spinnman/transceiver.py b/spinnman/transceiver.py index ec62af2a4..3e410781a 100644 --- a/spinnman/transceiver.py +++ b/spinnman/transceiver.py @@ -17,7 +17,7 @@ import os import random import struct -from threading import Condition, RLock +from threading import Condition from collections import defaultdict from contextlib import contextmanager, suppress import logging @@ -42,10 +42,9 @@ SpinnmanTimeoutException, SpinnmanGenericProcessException, SpinnmanUnexpectedResponseCodeException, SpiNNManCoresNotInStateException) -from spinnman.model import DiagnosticFilter, MachineDimensions +from spinnman.model import MachineDimensions from spinnman.model.enums import ( CPUState, SDP_PORTS, SDP_RUNNING_MESSAGE_CODES) -from spinnman.messages.scp.enums import Signal from spinnman.messages.scp.impl.get_chip_info import GetChipInfo from spinnman.messages.sdp import SDPFlag, SDPHeader, SDPMessage from spinnman.messages.spinnaker_boot import ( @@ -53,19 +52,19 @@ from spinnman.messages.scp.enums import PowerCommand from spinnman.messages.scp.abstract_messages import AbstractSCPRequest from spinnman.messages.scp.impl import ( - BMPSetLed, BMPGetVersion, SetPower, ReadADC, ReadFPGARegister, - WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, ReadMemory, - CountState, WriteMemory, SetLED, ApplicationRun, SendSignal, AppStop, + BMPGetVersion, SetPower, ReadFPGARegister, + WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, + CountState, WriteMemory, SendSignal, AppStop, IPTagSet, IPTagClear, RouterClear, DoSync) from spinnman.connections.udp_packet_connections import ( BMPConnection, BootConnection, SCAMPConnection) 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) @@ -181,13 +180,10 @@ class Transceiver(AbstractContextManager): "_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", @@ -246,14 +242,6 @@ def __init__( 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() - # A lock against single chip executions (entry is (x, y)) # The condition should be acquired before the locks are # checked or updated @@ -380,38 +368,6 @@ def _check_connection( break return None - @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() - @contextmanager def _flood_execute_lock(self): """ @@ -438,31 +394,6 @@ def _get_random_connection(connections): return None return connections[random.randint(0, len(connections) - 1)] - def send_scp_message(self, message, connection=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) - connection.send_scp_request(message) - def send_sdp_message(self, message, connection=None): """ Sends an SDP message using one of the connections. @@ -573,17 +504,6 @@ def add_scamp_connections(self, connections): self._scamp_connection_selector = MostDirectConnectionSelector( self._scamp_connections) - 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 get_machine_dimensions(self): """ Get the maximum chip X-coordinate and maximum chip Y-coordinate of @@ -646,21 +566,6 @@ def get_machine_details(self): logger.info(f"Detected {machine.summary_string()}") return machine - 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 get_scamp_version( self, chip_x=AbstractSCPRequest.DEFAULT_DEST_X_COORD, chip_y=AbstractSCPRequest.DEFAULT_DEST_Y_COORD, @@ -1081,87 +986,6 @@ def get_iobuf(self, core_subsets=None): process = ReadIOBufProcess(self._scamp_connection_selector) return process.read_iobuf(self._iobuf_size, core_subsets) - 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)) - def get_core_state_count(self, app_id, state): """ Get a count of the number of cores which have a given state. @@ -1186,74 +1010,6 @@ def get_core_state_count(self, app_id, state): response = process.execute(CountState(app_id, state)) return response.count # pylint: disable=no-member - 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 _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 execute_flood( self, core_subsets, executable, app_id, n_bytes=None, wait=False, is_filename=False): @@ -1321,44 +1077,6 @@ def execute_flood( self._scamp_connection_selector) process.run(n_bytes, app_id, core_subsets, chksum, 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 power_on_machine(self): """ Power on the whole machine. @@ -1452,33 +1170,6 @@ def _power(self, power_command, boards=0, cabinet=0, frame=0): if not self._machine_off: time.sleep(BMP_POST_POWER_ON_SLEEP_TIME) - def set_led(self, led, action, board, cabinet, frame): - """ - 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.") - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) - process.execute(BMPSetLed(led, action, board)) - def read_fpga_register(self, fpga_num, register, cabinet, frame, board): """ Read a register on a FPGA of a board. The meaning of the @@ -1520,28 +1211,6 @@ def write_fpga_register(self, fpga_num, register, value, cabinet, frame, process.execute( WriteFPGARegister(fpga_num, register, value, board)) - def read_adc_data(self, board, cabinet, frame): - """ - 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.") - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) - response = process.execute(ReadADC(board)) - return response.adc_info # pylint: disable=no-member - def read_bmp_version(self, board, cabinet, frame): """ Read the BMP version. @@ -1652,141 +1321,6 @@ def write_user(self, x, y, p, user, value): addr = self.__get_user_register_address_from_core(p, user) self.write_memory(x, y, addr, int(value)) - 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 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 read_memory(self, x, y, base_address, length, cpu=0): """ Read some areas of memory (usually SDRAM) from the board. @@ -1854,50 +1388,6 @@ def read_word(self, x, y, base_address, cpu=0): logger.info(self.__where_is_xy(x, y)) raise - 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 stop_application(self, app_id): """ Sends a stop request for an app_id. @@ -2042,7 +1532,7 @@ def send_signal(self, app_id, signal): Send a signal to an application. :param int app_id: The ID of the application to send to - :param 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: @@ -2057,37 +1547,6 @@ def send_signal(self, app_id, signal): process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(SendSignal(app_id, signal)) - 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 locate_spinnaker_connection_for_board_address(self, board_address): """ Find a connection that matches the given board IP address. @@ -2298,52 +1757,6 @@ def malloc_sdram(self, x, y, size, app_id, tag=None): 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 load_multicast_routes(self, x, y, routes, app_id): """ Load a set of multicast routes on to a chip. @@ -2517,7 +1930,7 @@ def set_router_diagnostic_filter(self, x, y, position, 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:: @@ -2564,47 +1977,6 @@ def __set_router_diagnostic_filter( process.execute(WriteMemory( x, y, memory_position, _ONE_WORD.pack(data_to_send))) - 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 - def clear_router_diagnostic_counters(self, x, y): """ Clear router diagnostic information on a chip. @@ -2630,25 +2002,6 @@ def clear_router_diagnostic_counters(self, x, y): 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.") - 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): """ Close the transceiver and any threads that are running. @@ -2674,23 +2027,6 @@ def bmp_connection(self): "likely to be removed.") return self._bmp_connection_selectors - 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 - def control_sync(self, do_sync): """ Control the synchronisation of the chips. diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 0aa55c7f1..a6c1f3b9d 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -18,6 +18,7 @@ from spinn_machine import virtual_machine from spinnman.config_setup import unittest_setup from spinnman.data.spinnman_data_writer import SpiNNManDataWriter +from spinnman.extended.extended_transceiver import ExtendedTransceiver from spinnman.transceiver import Transceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( @@ -25,6 +26,7 @@ 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() @@ -55,6 +57,10 @@ def close(self): pass +class MockExtendedTransceiver(MockWriteTransceiver, ExtendedTransceiver): + pass + + class TestTransceiver(unittest.TestCase): def setUp(self): @@ -74,7 +80,8 @@ def test_create_new_transceiver_one_connection(self): connections = set() connections.add(SCAMPConnection( remote_host=board_config.remotehost)) - with transceiver.Transceiver(ver, connections=connections) as trans: + with extended.ExtendedTransceiver( + ver, connections=connections) as trans: assert trans.get_connections() == connections def test_create_new_transceiver_from_list_connections(self): @@ -126,7 +133,7 @@ def test_boot_board(self): def test_set_watch_dog(self): 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)