def __init__(self, version_data, offset=0): """ :param bytes version_data: bytes from an SCP packet containing version information :param int offset: the offset in the bytes from an SCP packet containing version information :raise SpinnmanInvalidParameterException: If the message does not contain valid version information """ (self._p, self._physical_cpu_id, self._y, self._x, _, version_no, self._build_date) = _VERSION_PATTERN.unpack_from( memoryview(version_data), offset) version_data = version_data[offset + 12:-1].decode("utf-8") if version_no < 0xFFFF: try: self._version_number = (version_no // 100, version_no % 100, 0) self._name, self._hardware = version_data.split("/") self._version_string = version_data except ValueError as exception: raise SpinnmanInvalidParameterException( "version_data", version_data, "Incorrect format: {}".format(exception)) from exception else: name_hardware, _, version = version_data.partition("\0") self._version_string = version matches = re.match(r"(\d+)\.(\d+)\.(\d+)", version) if matches is None: raise SpinnmanInvalidParameterException( "version", version, "Cannot be parsed") self._version_number = tuple(map(int, matches.group(1, 2, 3))) self._name, self._hardware = name_hardware.rstrip("\0").split("/")
def get_bytestring(self, eieio_type): if eieio_type.payload_bytes == 0: raise SpinnmanInvalidParameterException( "eieio_type", eieio_type, "The type specifies no payload, but this element has a " "payload") if eieio_type == EIEIOType.KEY_PAYLOAD_16_BIT: return _TWO_SHORTS.pack(self._key, self._payload) elif eieio_type == EIEIOType.KEY_PAYLOAD_32_BIT: return _TWO_WORDS.pack(self._key, self._payload) else: raise SpinnmanInvalidParameterException( "eieio_type", eieio_type, "Unknown type")
def get_bytestring(self, eieio_type): if eieio_type.payload_bytes != 0: raise SpinnmanInvalidParameterException( "eieio_type", eieio_type, "The type specifies a payload, but this element has no" " payload") if eieio_type == EIEIOType.KEY_16_BIT: return struct.pack("<H", self._key) elif eieio_type == EIEIOType.KEY_32_BIT: return struct.pack("<I", self._key) else: raise SpinnmanInvalidParameterException("eieio_type", eieio_type, "Unknown type")
def __init__(self, x, y, n_entries, table_address, base_address, app_id): """ :param x: The x-coordinate of the chip, between 0 and 255, \ this is not checked due to speed restrictions :type x: int :param y: The y-coordinate of the chip, between 0 and 255\ this is not checked due to speed restrictions :type y: int :param n_entries: The number of entries in the table, more than 0 :type n_entries: int :param table_address: The allocated table address :type table_address: int :param base_address: The base_address containing the entries :type base_address: int :param app_id: The id of the application with which to associate the\ routes. If not specified, defaults to 0. :type app_id: int :raise spinnman.exceptions.SpinnmanInvalidParameterException:\ * If x is out of range * If y is out of range * If n_entries is 0 or less * If table_address is not positive * If base_address is not positive """ if n_entries < 1: raise SpinnmanInvalidParameterException("n_entries", str(n_entries), "Must be more than 0") if base_address < 0: raise SpinnmanInvalidParameterException( "base_address", str(base_address), "Must be a positive integer") if table_address < 0: raise SpinnmanInvalidParameterException( "table_address", str(table_address), "Must be a positive integer") super(SCPRouterInitRequest, self).__init__( SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, destination_chip_y=y), SCPRequestHeader(command=SCPCommand.CMD_RTR), argument_1=((n_entries << 16) | (app_id << 8) | 2), argument_2=table_address, argument_3=base_address)
def malloc_sdram(self, x, y, size, app_id, tag): space_required = size + 8 heap = self._get_heap(x, y, SystemVariableDefinition.sdram_heap_address) space = None index = None for i, element in enumerate(heap): if element.is_free and element.size >= space_required: space = element index = i break if space is None: raise SpinnmanInvalidParameterException( "SDRAM Allocation response base address", 0, "Could not allocate {} bytes of SDRAM".format(size)) free = 0xFFFF0000 | (app_id << 8) | tag next_space = None if index + 1 < len(heap): next_space = heap[index + 1] else: next_space = HeapElement(space.next_address, space.next_address, 0xFFFFFFFF) heap.pop(index) if space.size > space_required: new_space = HeapElement(space.block_address + space_required, space.next_address, 0x00000000) next_space = new_space heap.insert(index, new_space) heap.insert( index, HeapElement(space.block_address, next_space.block_address, free)) return space.block_address
def __init__(self, opcode, operand_1, operand_2, operand_3, data=None, offset=0): """ :param SpinnakerBootOpCode opcode: The operation of this packet :param int operand_1: The first operand :param int operand_2: The second operand :param int operand_3: The third operand :param data: The optional data, up to 256 words :type data: bytes or bytearray :param int offset: The offset of the valid data :raise SpinnmanInvalidParameterException: If the opcode is not a valid value """ # pylint: disable=too-many-arguments if data is not None and len(data) > (256 * 4): raise SpinnmanInvalidParameterException( "len(data)", str(len(data)), "A boot packet can contain at most 256 words of data") self._opcode = opcode self._operand_1 = operand_1 self._operand_2 = operand_2 self._operand_3 = operand_3 self._data = data self._offset = offset
def __init__(self, opcode, operand_1, operand_2, operand_3, data=None, offset=0): """ :param opcode: The operation of this packet :type opcode:\ :py:class:`spinnman.messages.spinnaker_boot.spinnaker_boot_op_code.SpinnakerBootOpCode` :param operand_1: The first operand :type operand_1: int :param operand_2: The second operand :type operand_2: int :param operand_3: The third operand :type operand_3: int :param data: The optional data, up to 256 words :type data: bytestring :param offset: The offset of the valid data :type offset: int :raise spinnman.exceptions.SpinnmanInvalidParameterException: If the\ opcode is not a valid value """ if data is not None and len(data) > (256 * 4): raise SpinnmanInvalidParameterException( "len(data)", str(len(data)), "A boot packet can contain at most 256 words of data") self._opcode = opcode self._operand_1 = operand_1 self._operand_2 = operand_2 self._operand_3 = operand_3 self._data = data self._offset = offset
def __init__(self, app_id, signal): """ :param app_id: The id of the application, between 0 and 255 :type app_id: int :param signal: The signal to send :type signal: :py:class:`spinnman.messages.scp.scp_signal.SCPSignal` :raise spinnman.exceptions.SpinnmanInvalidParameterException: If\ app_id is out of range """ if app_id < 0 or app_id > 255: raise SpinnmanInvalidParameterException( "app_id", str(app_id), "Must be between 0 and 255") super(SCPSendSignalRequest, self).__init__(SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=0, destination_chip_y=0), SCPRequestHeader(command=SCPCommand.CMD_SIG), argument_1=signal.signal_type.value, argument_2=_get_data(app_id, signal), argument_3=_ALL_CORE_MASK)
def __init__(self, command): if isinstance(command, Enum): command = command.value if command < 0 or command >= 16384: raise SpinnmanInvalidParameterException( "command", command, "parameter command is outside the allowed range (0 to 16383)") self._command = command
def __init__(self, eieio_header, data=None, offset=0): EIEIODataMessage.__init__(self, eieio_header, data, offset) if eieio_header.eieio_type.payload_bytes == 0: raise SpinnmanInvalidParameterException( "eieio_header", eieio_header, "This message should have a payload, but the header indicates" " that it doesn't")
def add_key_and_payload(self, key, payload): """ Adds a key and payload to the packet :param int key: The key to add :param int payload: The payload to add :raise SpinnmanInvalidParameterException: If the key or payload is too big for the format, or the format doesn't expect a payload """ if key > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) if payload > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "payload", payload, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) self.add_element( KeyPayloadDataElement(key, payload, self._header.is_time))
def __init__(self, x, y, n_entries, table_address, base_address, app_id): """ :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 :param int n_entries: The number of entries in the table, more than 0 :param int table_address: The allocated table address :param int base_address: The base_address containing the entries :param int app_id: The ID of the application with which to associate the routes. If not specified, defaults to 0. :raise SpinnmanInvalidParameterException: * If x is out of range * If y is out of range * If n_entries is 0 or less * If table_address is not positive * If base_address is not positive """ # pylint: disable=too-many-arguments if n_entries < 1: raise SpinnmanInvalidParameterException("n_entries", str(n_entries), "Must be more than 0") if base_address < 0: raise SpinnmanInvalidParameterException( "base_address", str(base_address), "Must be a positive integer") if table_address < 0: raise SpinnmanInvalidParameterException( "table_address", str(table_address), "Must be a positive integer") super().__init__(SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, destination_chip_y=y), SCPRequestHeader(command=SCPCommand.CMD_RTR), argument_1=((n_entries << 16) | (app_id << 8) | 2), argument_2=table_address, argument_3=base_address)
def read_data_bytestring(self, data, offset): result = self.scp_response_header.result if result != SCPResult.RC_OK: raise SpinnmanUnexpectedResponseCodeException( "SDRAM Allocation", "CMD_ALLOC", result.name) self._base_address = _ONE_WORD.unpack_from(data, offset)[0] # check that the base address is not null (0 in python case) as # this reflects a issue in the command on SpiNNaker side if self._base_address == 0: raise SpinnmanInvalidParameterException( "SDRAM Allocation response base address", self._base_address, "Could not allocate {} bytes of SDRAM".format(self._size))
def add_key(self, key): """ Add a key to the packet :param key: The key to add :type key: int :raise SpinnmanInvalidParameterException: If the key is too\ big for the format """ if key > self._eieio_header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._eieio_header.eieio_type.max_value)) EIEIODataMessage.add_element(self, EIEIOKeyDataElement(key))
def add_key(self, key): """ Add a key to the packet :param int key: The key to add :raise SpinnmanInvalidParameterException: If the key is too big for the format, or the format expects a payload """ if key > self._header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) self.add_element(KeyDataElement(key))
def add_key_and_payload(self, key, payload): """ Adds a key and payload to the packet :param key: The key to add :type key: int :param payload: The payload to add :type payload: int :raise SpinnmanInvalidParameterException: If the key or payload is too\ big for the format """ if key > self._eieio_header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "key", key, "Larger than the maximum allowed of {}".format( self._eieio_header.eieio_type.max_value)) if payload > self._eieio_header.eieio_type.max_value: raise SpinnmanInvalidParameterException( "payload", payload, "Larger than the maximum allowed of {}".format( self._eieio_header.eieio_type.max_value)) EIEIODataMessage.add_element( self, EIEIOKeyPayloadDataElement(key, payload, self._eieio_header.is_time))
def read_data_bytestring(self, data, offset): """ See\ :py:meth:`spinnman.messages.scp.abstract_scp_response.AbstractSCPResponse.read_data_bytestring` """ result = self.scp_response_header.result if result != SCPResult.RC_OK: raise SpinnmanUnexpectedResponseCodeException( "SDRAM Allocation", "CMD_ALLOC", result.name) self._base_address = struct.unpack_from("<I", data, offset)[0] # check that the base address is not null (0 in python case) as # this reflects a issue in the command on spinnaker side if self._base_address == 0: raise SpinnmanInvalidParameterException( "SDRAM Allocation response base address", self._base_address, "Could not allocate {} bytes of SDRAM".format(self._size))
def load_routes(self, x, y, routes, app_id): """ :param int x: :param int y: :param list(~spinn_machine.MulticastRoutingEntry) routes: :param int app_id: """ # Create the routing data - 16 bytes per entry plus one for the end # entry routing_data = bytearray(16 * (len(routes) + 1)) n_entries = 0 for route in routes: route_entry = \ Router.convert_routing_table_entry_to_spinnaker_route(route) _ROUTE_PATTERN.pack_into( routing_data, n_entries * 16, n_entries, route_entry, route.routing_entry_key, route.mask) n_entries += 1 # Add an entry to mark the end _END_PATTERN.pack_into( routing_data, n_entries * 16, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) # Upload the data process = WriteMemoryProcess(self._conn_selector) process.write_memory_from_bytearray( x, y, 0, _TABLE_ADDRESS, routing_data, 0, len(routing_data)) # Allocate space in the router table self._send_request(RouterAlloc(x, y, app_id, n_entries), self.__handle_router_alloc_response) self._finish() self.check_for_error() if self._base_address == 0: raise SpinnmanInvalidParameterException( "Allocation base address", str(self._base_address), "Not enough space to allocate the entries") # Load the entries self._send_request( RouterInit( x, y, n_entries, _TABLE_ADDRESS, self._base_address, app_id)) self._finish() self.check_for_error()
def add_core_subset(self, core_subset): """ Add a core subset to the set :param core_subset: The core subset to add :type core_subset: :py:class:`spinnman.model.core_subset.CoreSubset` :return: Nothing is returned :rtype: None :raise spinnman.exceptions.SpinnmanInvalidParameterException: If there\ is already a subset with the same core x and y\ coordinates """ if (core_subset.x, core_subset.y) in self._core_subsets: raise SpinnmanInvalidParameterException( "core_subset.(x, y)", "{}, {}".format(core_subset.x, core_subset.y), "There can be only one set of cores for each chip") self._core_subsets[(core_subset.x, core_subset.y)] = core_subset
def known(self, binary, chip_x, chip_y, chip_p): """ :param str binary: :param int chip_x: :param int chip_y: :param int chip_p: :rtype: bool """ if not self._all_core_subsets.is_core(chip_x, chip_y, chip_p): return False # OK if and only if the chip is in this binary already if binary in self._targets: if self._targets[binary].is_core(chip_x, chip_y, chip_p): return True parameter = "x:{} y:{} p:{}".format(chip_x, chip_y, chip_p) problem = "Already associated with a different binary" raise SpinnmanInvalidParameterException(parameter, binary, problem)
def get_cpu_info(self, chip_info, core_subsets): for core_subset in core_subsets: x = core_subset.x y = core_subset.y this_chip_info = chip_info[(x, y)] for p in core_subset.processor_ids: if p not in this_chip_info.virtual_core_ids: raise SpinnmanInvalidParameterException( "p", p, "Not a valid core on chip {}, {}".format(x, y)) base_address = (this_chip_info.cpu_information_base_address + (constants.CPU_INFO_BYTES * p)) self._send_request( SCPReadMemoryRequest(x, y, base_address, constants.CPU_INFO_BYTES), functools.partial(self.handle_response, x, y, p)) self._finish() self.check_for_error() return self._cpu_info
def __init__(self, version_data, offset=0): """ :param version_data: bytes from an SCP packet containing version\ information :param offset: the offset in the bytes from an SCP packet containing version information :type version_data: bytearray :raise spinnman.exceptions.SpinnmanInvalidParameterException: If the\ message does not contain valid version information """ (self._p, self._y, self._x, version_no, self._build_date) = \ struct.unpack_from("<BxBB2xHI", version_data, offset) self._version_number = version_no / 100.0 self._version_string = version_data[offset + 12:-1].decode("ascii") try: self._name, self._hardware = self._version_string.split("/") except ValueError as exception: raise SpinnmanInvalidParameterException( "version_string", self._version_string, "Incorrect format: {}".format(exception))
def __init__(self, x, y, app_id, size, tag=None, retry_tag=True): """ :param int x: The x-coordinate of the chip to allocate on, between 0 and 255 :param int y: The y-coordinate of the chip to allocate on, between 0 and 255 :param int app_id: The ID of the application, between 0 and 255 :param int size: The size in bytes of memory to be allocated :param int tag: The tag for the SDRAM, a 8-bit (chip-wide) tag that can be looked up by a SpiNNaker application to discover the address of the allocated block. If `0` then no tag is applied. :param bool retry_tag: If a tag is used, add a safety check to retry the tag. This can avoid issues with re-allocating memory on a retry message. """ # pylint: disable=too-many-arguments extra_flag = 0 if retry_tag and tag is not None: extra_flag = FLAG_RETRY_TAG if tag is None: tag = 0 elif not(0 <= tag < 256): raise SpinnmanInvalidParameterException( "tag", "The tag param needs to be between 0 and 255, or None (in " "which case 0 will be used by default)", str(tag)) super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, destination_chip_y=y), SCPRequestHeader(command=SCPCommand.CMD_ALLOC), argument_1=( (extra_flag << 16) | (app_id << 8) | AllocFree.ALLOC_SDRAM.value), # @UndefinedVariable argument_2=size, argument_3=tag) self._size = size
def __init__(self, control_register, error_status, register_values): """ :param int control_register: The value of the control register :param int error_status: The value of the error_status :param list(int) register_values: The values of the 16 router registers :raise SpinnmanInvalidParameterException: If the number of register values is not 16 """ if len(register_values) != 16: raise SpinnmanInvalidParameterException( "len(register_values)", str(len(ROUTER_REGISTER_REGISTERS)), "There must be exactly 16 register values") self._mon = (control_register >> 8) & 0x1F self._wait_1 = (control_register >> 16) & 0xFF self._wait_2 = (control_register >> 24) & 0xFF self._error_status = error_status self._register_values = register_values
def __init__(self, app_id, signal): """ :param int app_id: The ID of the application, between 0 and 255 :param Signal signal: The signal to send :raise SpinnmanInvalidParameterException: If app_id is out of range """ if app_id < 0 or app_id > 255: raise SpinnmanInvalidParameterException( "app_id", str(app_id), "Must be between 0 and 255") super().__init__(SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=self.DEFAULT_DEST_X_COORD, destination_chip_y=self.DEFAULT_DEST_Y_COORD), SCPRequestHeader(command=SCPCommand.CMD_SIG), argument_1=signal.signal_type.value, argument_2=_get_data(app_id, signal), argument_3=_ALL_CORE_MASK)
def __init__(self, control_register, error_status, register_values): """ :param control_register: The value of the control register :type control_register: int :param error_status: The value of the error_status :type error_status: int :param register_values: The values of the 16 router registers :type register_values: iterable of int :raise spinnman.exceptions.SpinnmanInvalidParameterException: If the\ number of register values is not 16 """ if len(register_values) != 16: raise SpinnmanInvalidParameterException( "len(register_values)", str(len(constants.ROUTER_REGISTER_REGISTERS)), "There must be exactly 16 register values") self._mon = (control_register >> 8) & 0x1F self._wait_1 = (control_register >> 16) & 0xFF self._wait_2 = (control_register >> 8) & 0xFF self._error_status = error_status self._register_values = register_values
def __init__(self, board_version=None, extra_boot_values=None): """ Builds the boot messages needed to boot the SpiNNaker machine :param int board_version: The version of the board to be booted :param extra_boot_values: Any additional values to be set during boot :type extra_boot_values: dict(SystemVariableDefinition, object) :raise SpinnmanInvalidParameterException: If the board version is not supported :raise SpinnmanIOException: If there is an error assembling the packets """ if (board_version is not None and board_version not in spinnaker_boot_values): raise SpinnmanInvalidParameterException("board_version", str(board_version), "Unknown board version") # Get the boot packet values if board_version is not None: spinnaker_boot_value = spinnaker_boot_values[board_version] else: spinnaker_boot_value = SystemVariableBootValues() current_time = int(time.time()) spinnaker_boot_value.set_value(SystemVariableDefinition.unix_timestamp, current_time) spinnaker_boot_value.set_value(SystemVariableDefinition.boot_signature, current_time) spinnaker_boot_value.set_value(SystemVariableDefinition.is_root_chip, 1) # Set any additional values if extra_boot_values is not None: for variable, value in extra_boot_values.items(): spinnaker_boot_value.set_value(variable, value) # Get the data as an array, to be used later spinnaker_boot_data = array.array("I", spinnaker_boot_value.bytestring) # Find the data file and size boot_data_file, boot_data_size = self._get_boot_image_file() # Compute how many packets to send n_words_to_read = boot_data_size // 4 self._no_data_packets = int( math.ceil(float(boot_data_size) / _BOOT_MESSAGE_DATA_BYTES)) # Read the data boot_data = array.array("I") with open(boot_data_file, "rb") as f: boot_data.fromfile(f, n_words_to_read) # Replace the appropriate part with the custom boot options offset = _BOOT_STRUCT_REPLACE_OFFSET length = _BOOT_STRUCT_REPLACE_LENGTH boot_data[offset:offset + length] = spinnaker_boot_data[0:length] # Byte swap and store the data for later use boot_data.byteswap() self._boot_data = boot_data.tobytes() self._n_bytes_to_read = n_words_to_read * 4
def __init__( self, board_version, width, height, number_of_boards): """ builds the boot messages needed to boot the spinnaker machine :param board_version: The version of the board to be booted :type board_version: int :param width: The width of the machine in chips :type width: int or None :param height: The height of the machine in chips :type height: int or None :param number_of_boards: the number of boards that this spinnaker machine is built up from :type number_of_boards: int :raise spinnman.exceptions.SpinnmanInvalidParameterException: If the\ board version is not supported :raise spinnman.exceptions.SpinnmanIOException: If there is an error\ assembling the packets """ if board_version not in variable_boot_values.spinnaker_boot_values: raise SpinnmanInvalidParameterException( "board_version", str(board_version), "Unknown board version") # Get the boot packet values spinnaker_boot_value = \ variable_boot_values.spinnaker_boot_values[board_version] current_time = int(time.time()) spinnaker_boot_value.set_value( SystemVariableDefinition.unix_timestamp, current_time) spinnaker_boot_value.set_value( SystemVariableDefinition.boot_signature, current_time) spinnaker_boot_value.set_value( SystemVariableDefinition.is_root_chip, 1) spinnaker_boot_value.set_value( SystemVariableDefinition.x_size, int(width)) spinnaker_boot_value.set_value( SystemVariableDefinition.y_size, int(height)) # add any updates for multi-board systems for multi_board_n_boards, extra_variables in ( variable_boot_values .spinnaker_multi_board_extra_configs.iteritems()): if number_of_boards >= multi_board_n_boards: for extra_variable, extra_value in extra_variables.iteritems(): spinnaker_boot_value.set_value(extra_variable, extra_value) # Get the data as an array, to be used later self._spinnaker_boot_data = array.array( "I", spinnaker_boot_value.bytestring) # Find the data file and size this_dir, _ = os.path.split(__file__) boot_data_file_name = os.path.join( this_dir, "boot_data", _BOOT_DATA_FILE_NAME) boot_data_file_size = os.stat(boot_data_file_name).st_size if boot_data_file_size > _BOOT_IMAGE_MAX_BYTES: raise SpinnmanIOException( "The boot file is too big at {} bytes (" "only files up to 32KB are acceptable".format( boot_data_file_size)) elif boot_data_file_size % 4 != 0: raise SpinnmanIOException( "The boot file size of {} bytes must" " be divisible by 4".format(boot_data_file_size)) # Compute how many packets to send self._boot_data_file = open(boot_data_file_name, "rb") self._no_data_packets = int(math.ceil( float(boot_data_file_size) / float(_BOOT_MESSAGE_DATA_BYTES))) self._n_words_to_read = boot_data_file_size / 4