def _send_messages(self, size, vertex, region, sequence_no):
        """ Send a set of messages
        """

        # Get the sent messages for the vertex
        if vertex not in self._sent_messages:
            self._sent_messages[vertex] = BuffersSentDeque(region)
        sent_messages = self._sent_messages[vertex]

        # If the sequence number is outside the window, return no messages
        if not sent_messages.update_last_received_sequence_number(sequence_no):
            return list()

        # Remote the existing packets from the size available
        bytes_to_go = size
        for message in sent_messages.messages:
            if isinstance(message.eieio_data_message, EIEIODataMessage):
                bytes_to_go -= message.eieio_data_message.size
            else:
                bytes_to_go -= (message.eieio_data_message
                                .get_min_packet_length())

        # Add messages up to the limits
        while (vertex.is_next_timestamp(region) and
                not sent_messages.is_full and bytes_to_go > 0):

            space_available = min(
                bytes_to_go,
                constants.UDP_MESSAGE_MAX_SIZE -
                HostSendSequencedData.get_min_packet_length())
            # logger.debug(
            #     "Bytes to go {}, space available {}".format(
            #         bytes_to_go, space_available))
            next_message = self._create_message_to_send(
                space_available, vertex, region)
            if next_message is None:
                break
            sent_messages.add_message_to_send(next_message)
            bytes_to_go -= next_message.size
            # logger.debug("Adding additional buffer of {} bytes".format(
            #     next_message.size))

        # If the vertex is empty, send the stop messages if there is space
        if (not sent_messages.is_full and
                not vertex.is_next_timestamp(region) and
                bytes_to_go >= EventStopRequest.get_min_packet_length()):
            sent_messages.send_stop_message()

        # If there are no more messages, turn off requests for more messages
        if not vertex.is_next_timestamp(region) and sent_messages.is_empty():
            # logger.debug("Sending stop")
            self._send_request(vertex, StopRequests())

        # Send the messages
        for message in sent_messages.messages:
            # logger.debug("Sending message with sequence {}".format(
            #     message.sequence_no))
            self._send_request(vertex, message)
Exemple #2
0
    def _send_messages(self, size, vertex, region, sequence_no):
        """ Send a set of messages
        """

        # Get the sent messages for the vertex
        if vertex not in self._sent_messages:
            self._sent_messages[vertex] = BuffersSentDeque(region)
        sent_messages = self._sent_messages[vertex]

        # If the sequence number is outside the window, return no messages
        if not sent_messages.update_last_received_sequence_number(sequence_no):
            return list()

        # Remote the existing packets from the size available
        bytes_to_go = size
        for message in sent_messages.messages:
            if isinstance(message.eieio_data_message, EIEIODataMessage):
                bytes_to_go -= message.eieio_data_message.size
            else:
                bytes_to_go -= (
                    message.eieio_data_message.get_min_packet_length())

        # Add messages up to the limits
        while (vertex.is_next_timestamp(region) and not sent_messages.is_full
               and bytes_to_go > 0):

            space_available = min(
                bytes_to_go, constants.UDP_MESSAGE_MAX_SIZE -
                HostSendSequencedData.get_min_packet_length())
            # logger.debug(
            #     "Bytes to go {}, space available {}".format(
            #         bytes_to_go, space_available))
            next_message = self._create_message_to_send(
                space_available, vertex, region)
            if next_message is None:
                break
            sent_messages.add_message_to_send(next_message)
            bytes_to_go -= next_message.size
            # logger.debug("Adding additional buffer of {} bytes".format(
            #     next_message.size))

        # If the vertex is empty, send the stop messages if there is space
        if (not sent_messages.is_full and not vertex.is_next_timestamp(region)
                and bytes_to_go >= EventStopRequest.get_min_packet_length()):
            sent_messages.send_stop_message()

        # If there are no more messages, turn off requests for more messages
        if not vertex.is_next_timestamp(region) and sent_messages.is_empty():
            # logger.debug("Sending stop")
            self._send_request(vertex, StopRequests())

        # Send the messages
        for message in sent_messages.messages:
            # logger.debug("Sending message with sequence {}".format(
            #     message.sequence_no))
            self._send_request(vertex, message)
def read_eieio_command_message(data, offset):
    command_header = EIEIOCommandHeader.from_bytestring(data, offset)
    command_number = command_header.command

    if command_number == constants.EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION.value:
        return DatabaseConfirmation.from_bytestring(command_header, data, offset + 2)

    # Fill in buffer area with padding
    elif command_number == constants.EIEIO_COMMAND_IDS.EVENT_PADDING.value:
        return PaddingRequest()

    # End of all buffers, stop execution
    elif command_number == constants.EIEIO_COMMAND_IDS.EVENT_STOP.value:
        return EventStopRequest()

    # Stop complaining that there is sdram free space for buffers
    elif command_number == constants.EIEIO_COMMAND_IDS.STOP_SENDING_REQUESTS.value:
        return StopRequests()

    # Start complaining that there is sdram free space for buffers
    elif command_number == constants.EIEIO_COMMAND_IDS.START_SENDING_REQUESTS.value:
        return StartRequests()

    # Spinnaker requesting new buffers for spike source population
    elif command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_BUFFERS.value:
        return SpinnakerRequestBuffers.from_bytestring(command_header, data, offset + 2)

    # Buffers being sent from host to SpiNNaker
    elif command_number == constants.EIEIO_COMMAND_IDS.HOST_SEND_SEQUENCED_DATA.value:
        return HostSendSequencedData.from_bytestring(command_header, data, offset + 2)

    # Buffers available to be read from a buffered out vertex
    elif command_number == constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_READ_DATA.value:
        return SpinnakerRequestReadData.from_bytestring(command_header, data, offset + 2)

    # Host confirming data being read form SpiNNaker memory
    elif command_number == constants.EIEIO_COMMAND_IDS.HOST_DATA_READ.value:
        return HostDataRead.from_bytestring(command_header, data, offset + 2)
    return EIEIOCommandMessage(command_header, data, offset + 2)
    def add_message_to_send(self, message):
        """ Add a message to send.  The message is converted to a sequenced\
            message.

        :param message: The message to be added
        :type message:\
                    :py:class:`spinnman.messages.eieio.abstract_messages.AbstractEIEIOMessage`
        """

        # If full, raise an exception
        if self.is_full:
            raise exceptions.SpinnFrontEndException("The buffer is full")

        # Create a sequenced message and update the sequence number
        self._sequence_lock.acquire()
        sequenced_message = HostSendSequencedData(self._region,
                                                  self._sequence_number,
                                                  message)
        self._sequence_number = ((self._sequence_number + 1) % _N_SEQUENCES)
        self._sequence_lock.release()

        # Add the sequenced message to the buffers
        self._buffers_sent.append(sequenced_message)
class BufferedSendingRegion(object):
    """ A set of keys to be sent at given timestamps for a given region of\
        data.  Note that keys must be added in timestamp order or else an\
        exception will be raised
    """

    _HEADER_SIZE = EIEIODataHeader.get_header_size(EIEIOType.KEY_32_BIT,
                                                   is_payload_base=True)

    # The number of bytes in each key to be sent
    _N_BYTES_PER_KEY = EIEIOType.KEY_32_BIT.key_bytes  # @UndefinedVariable

    # The number of keys allowed (different from the actual number as there is
    #  an additional header)
    _N_KEYS_PER_MESSAGE = (constants.UDP_MESSAGE_MAX_SIZE -
                           (HostSendSequencedData.get_min_packet_length() +
                            _HEADER_SIZE)) / _N_BYTES_PER_KEY

    def __init__(self, max_buffer_size):
        self._max_size_of_buffer = max_buffer_size
        self.clear()

    @property
    def buffer_size(self):
        """
        property method for getting the max size of this buffer
        :return:
        """
        if self._buffer_size is None:
            self._calculate_sizes()
        return self._buffer_size

    @property
    def total_region_size(self):
        """ Get the max size of this region
        :return:
        """
        if self._total_region_size is None:
            self._calculate_sizes()
        return self._total_region_size

    @property
    def max_buffer_size_possible(self):
        """ Get the max possible size of a buffer from this region
        :return:
        """
        return self._max_size_of_buffer

    def _calculate_sizes(self):
        """ Deduce how big the buffer and the region needs to be
        :return:
        """
        size = 0
        for timestamp in self._timestamps:
            n_keys = self.get_n_keys(timestamp)
            size += self.get_n_bytes(n_keys)
        size += EventStopRequest.get_min_packet_length()
        if size > self._max_size_of_buffer:
            self._buffer_size = self._max_size_of_buffer
        else:
            self._buffer_size = size
        self._total_region_size = size

    def get_n_bytes(self, n_keys):
        """ Get the number of bytes used by a given number of keys

        :param n_keys: The number of keys
        :type n_keys: int
        """

        # Get the total number of messages
        n_messages = int(math.ceil(float(n_keys) / self._N_KEYS_PER_MESSAGE))

        # Add up the bytes
        return ((self._HEADER_SIZE * n_messages) +
                (n_keys * self._N_BYTES_PER_KEY))

    def add_key(self, timestamp, key):
        """ Add a key to be sent at a given time

        :param timestamp: The time at which the key is to be sent
        :type timestamp: int
        :param key: The key to send
        :type key: int
        """
        if timestamp not in self._buffer:
            bisect.insort(self._timestamps, timestamp)
            self._buffer[timestamp] = list()
        self._buffer[timestamp].append(key)
        self._total_region_size = None
        self._buffer_size = None

    def add_keys(self, timestamp, keys):
        """ Add a set of keys to be sent at the given time

        :param timestamp: The time at which the keys are to be sent
        :type timestamp: int
        :param keys: The keys to send
        :type keys: iterable of int
        """
        for key in keys:
            self.add_key(timestamp, key)

    @property
    def n_timestamps(self):
        """ The number of timestamps available

        :rtype: int
        """
        return len(self._timestamps)

    @property
    def timestamps(self):
        """ The timestamps for which there are keys

        :rtype: iterable of int
        """
        return self._timestamps

    def get_n_keys(self, timestamp):
        """ Get the number of keys for a given timestamp

        :param timestamp: the time stamp to check if there's still keys to\
                transmit
        """
        if timestamp in self._buffer:
            return len(self._buffer[timestamp])
        return 0

    @property
    def is_next_timestamp(self):
        """ Determines if the region is empty
        :return: True if the region is empty, false otherwise
        :rtype: bool
        """
        return self._current_timestamp_pos < len(self._timestamps)

    @property
    def next_timestamp(self):
        """ The next timestamp of the data to be sent, or None if no more data

        :rtype: int or None
        """
        if self.is_next_timestamp:
            return self._timestamps[self._current_timestamp_pos]
        return None

    def is_next_key(self, timestamp):
        """ Determine if there is another key for the given timestamp

        :param timestamp: the time stamp to check if there's still keys to\
                transmit
        :rtype: bool
        """
        if timestamp in self._buffer:
            return len(self._buffer[timestamp]) > 0
        return False

    @property
    def next_key(self):
        """ The next key to be sent

        :rtype: int
        """
        next_timestamp = self.next_timestamp
        keys = self._buffer[next_timestamp]
        key = keys.pop()
        if len(keys) == 0:
            del self._buffer[next_timestamp]
            self._current_timestamp_pos += 1
        return key

    @property
    def current_timestamp(self):
        """ Get the current timestamp in the iterator
        """
        return self._current_timestamp_pos

    def rewind(self):
        """ Rewind the buffer to initial position.
        """
        self._current_timestamp_pos = 0

    def clear(self):
        """ Clears the buffer
        """

        # A dictionary of timestamp -> list of keys
        self._buffer = dict()

        # A list of timestamps
        self._timestamps = list()

        # The current position in the list of timestamps
        self._current_timestamp_pos = 0

        self._buffer_size = None

        self._total_region_size = None
Exemple #6
0
def read_eieio_command_message(data, offset):
    """ Reads the content of an EIEIO command message and returns an object\
        identifying the command which was contained in the packet, including\
        any parameter, if required by the command

    :param data: data received from the network
    :type data: bytestring
    :param offset: offset at which the parsing operation should start
    :type offset: int
    :return: an object which inherits from EIEIOCommandMessage which contains\
            parsed data received from the network
    :rtype: \
            :py:class:`spinnman.messages.eieio.command_messages.eieio_command_message.EIEIOCommandMessage`
    """
    command_header = EIEIOCommandHeader.from_bytestring(data, offset)
    command_number = command_header.command

    if (command_number ==
            constants.EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION.value):
        return DatabaseConfirmation.from_bytestring(command_header, data,
                                                    offset + 2)

    # Fill in buffer area with padding
    elif (command_number == constants.EIEIO_COMMAND_IDS.EVENT_PADDING.value):
        return PaddingRequest()

    # End of all buffers, stop execution
    elif (command_number == constants.EIEIO_COMMAND_IDS.EVENT_STOP.value):
        return EventStopRequest()

    # Stop complaining that there is sdram free space for buffers
    elif (command_number ==
          constants.EIEIO_COMMAND_IDS.STOP_SENDING_REQUESTS.value):
        return StopRequests()

    # Start complaining that there is sdram free space for buffers
    elif (command_number ==
          constants.EIEIO_COMMAND_IDS.START_SENDING_REQUESTS.value):
        return StartRequests()

    # Spinnaker requesting new buffers for spike source population
    elif (command_number ==
          constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_BUFFERS.value):
        return SpinnakerRequestBuffers.from_bytestring(command_header, data,
                                                       offset + 2)

    # Buffers being sent from host to SpiNNaker
    elif (command_number ==
          constants.EIEIO_COMMAND_IDS.HOST_SEND_SEQUENCED_DATA.value):
        return HostSendSequencedData.from_bytestring(command_header, data,
                                                     offset + 2)

    # Buffers available to be read from a buffered out vertex
    elif (command_number ==
          constants.EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_READ_DATA.value):
        return SpinnakerRequestReadData.from_bytestring(
            command_header, data, offset + 2)

    # Host confirming data being read form SpiNNaker memory
    elif (command_number == constants.EIEIO_COMMAND_IDS.HOST_DATA_READ.value):
        return HostDataRead.from_bytestring(command_header, data, offset + 2)
    return EIEIOCommandMessage(command_header, data, offset + 2)
Exemple #7
0
# The minimum size of any message - this is the headers plus one entry
_MIN_MESSAGE_SIZE = (EIEIO32BitTimedPayloadPrefixDataMessage
                     .get_min_packet_length())

# The size of the header of a message
_HEADER_SIZE = EIEIODataHeader.get_header_size(EIEIOType.KEY_32_BIT,
                                               is_payload_base=True)

# The number of bytes in each key to be sent
_N_BYTES_PER_KEY = EIEIOType.KEY_32_BIT.key_bytes

# The number of keys allowed (different from the actual number as there is an
# additional header)
_N_KEYS_PER_MESSAGE = (constants.UDP_MESSAGE_MAX_SIZE -
                       (HostSendSequencedData.get_min_packet_length() +
                        _HEADER_SIZE) / _N_BYTES_PER_KEY)


class BufferManager(object):
    """ Manager of send buffers
    """

    def __init__(self, placements, routing_info, tags, transceiver):
        """

        :param placements: The placements of the vertices
        :type placements:\
                    :py:class:`pacman.model.placements.placements.Placements`
        :param routing_infos: The routing keys of the vertices
        :type routing_infos:\