def __init__(self, label, region_files_tuples): """ :param label: The label of the vertex :param region_files_dict: A dictionary of region id -> file name """ self._label = label self._send_buffers = dict() for (region_id, filename, max_size_of_buffer) in region_files_tuples: send_buffer = BufferedSendingRegion(max_size_of_buffer) reader = open(filename, "r") line = reader.readline() while line != "": bits = line.split(":") send_buffer.add_key(int(bits[0]), int(bits[1])) line = reader.readline() self._send_buffers[region_id] = send_buffer SendsBuffersFromHostPartitionedVertexPreBufferedImpl.__init__(self, self._send_buffers)
def __init__(self, label, region_files_tuples): """ :param label: The label of the vertex :param region_files_dict: A dictionary of region id -> file name """ self._label = label self._send_buffers = dict() for (region_id, filename, max_size_of_buffer) in region_files_tuples: send_buffer = BufferedSendingRegion(max_size_of_buffer) reader = open(filename, "r") line = reader.readline() while line != "": bits = line.split(":") send_buffer.add_key(int(bits[0]), int(bits[1])) line = reader.readline() self._send_buffers[region_id] = send_buffer SendsBuffersFromHostPreBufferedImpl.__init__(self, self._send_buffers)
def _get_spike_send_buffer(self, vertex_slice): """ spikeArray is a list with one entry per 'neuron'. The entry for one neuron is a list of times (in ms) when the neuron fires. We need to transpose this 'matrix' and get a list of firing neuron indices for each time tick: List can come in two formats (both now supported): 1) Official PyNN format - single list that is used for all neurons 2) SpiNNaker format - list of lists, one per neuron """ send_buffer = None key = (vertex_slice.lo_atom, vertex_slice.hi_atom) if key not in self._send_buffers: send_buffer = BufferedSendingRegion( self._max_on_chip_memory_usage_for_spikes) if hasattr(self._spike_times[0], "__len__"): # This is in SpiNNaker 'list of lists' format: for neuron in range(vertex_slice.lo_atom, vertex_slice.hi_atom + 1): for timeStamp in sorted(self._spike_times[neuron]): time_stamp_in_ticks = int( math.ceil((timeStamp * 1000.0) / self._machine_time_step)) send_buffer.add_key(time_stamp_in_ticks, neuron - vertex_slice.lo_atom) else: # This is in official PyNN format, all neurons use the # same list: neuron_list = range(vertex_slice.n_atoms) for timeStamp in sorted(self._spike_times): time_stamp_in_ticks = int( math.ceil((timeStamp * 1000.0) / self._machine_time_step)) # add to send_buffer collection send_buffer.add_keys(time_stamp_in_ticks, neuron_list) self._send_buffers[key] = send_buffer else: send_buffer = self._send_buffers[key] return send_buffer
def _get_spike_send_buffer(self, vertex_slice): """ spikeArray is a list with one entry per 'neuron'. The entry for one neuron is a list of times (in ms) when the neuron fires. We need to transpose this 'matrix' and get a list of firing neuron indices for each time tick: List can come in two formats (both now supported): 1) Official PyNN format - single list that is used for all neurons 2) SpiNNaker format - list of lists, one per neuron """ send_buffer = None key = (vertex_slice.lo_atom, vertex_slice.hi_atom) if key not in self._send_buffers: send_buffer = BufferedSendingRegion() if isinstance(self._spike_times[0], list): # This is in SpiNNaker 'list of lists' format: for neuron in range(vertex_slice.lo_atom, vertex_slice.hi_atom + 1): for timeStamp in sorted(self._spike_times[neuron]): time_stamp_in_ticks = int( math.ceil((timeStamp * 1000.0) / self._machine_time_step)) send_buffer.add_key(time_stamp_in_ticks, neuron - vertex_slice.lo_atom) else: # This is in official PyNN format, all neurons use the # same list: neuron_list = range(vertex_slice.n_atoms) for timeStamp in sorted(self._spike_times): time_stamp_in_ticks = int( math.ceil( (timeStamp * 1000.0) / self._machine_time_step)) # add to send_buffer collection send_buffer.add_keys(time_stamp_in_ticks, neuron_list) # Update the size total_size = 0 for timestamp in send_buffer.timestamps: n_keys = send_buffer.get_n_keys(timestamp) total_size += BufferManager.get_n_bytes(n_keys) total_size += EventStopRequest.get_min_packet_length() if total_size > self._max_on_chip_memory_usage_for_spikes: total_size = self._max_on_chip_memory_usage_for_spikes send_buffer.buffer_size = total_size self._send_buffers[key] = send_buffer else: send_buffer = self._send_buffers[key] return send_buffer
def __init__( self, n_keys, resources_required, machine_time_step, timescale_factor, label, constraints=None, # General input and output parameters board_address=None, # Live input parameters receive_port=None, receive_sdp_port=( constants.SDP_PORTS.INPUT_BUFFERING_SDP_PORT.value), receive_tag=None, # Key parameters virtual_key=None, prefix=None, prefix_type=None, check_keys=False, # Send buffer parameters send_buffer_times=None, send_buffer_max_space=( constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP), send_buffer_space_before_notify=640, send_buffer_notification_ip_address=None, send_buffer_notification_port=None, send_buffer_notification_tag=None): """ :param n_keys: The number of keys to be sent via this multicast source :param resources_required: The resources required by the vertex :param machine_time_step: The time step to be used on the machine :param timescale_factor: The time scaling to be used in the simulation :param label: The label of this vertex :param constraints: Any initial constraints to this vertex :param board_address: The IP address of the board on which to place\ this vertex if receiving data, either buffered or live (by\ default, any board is chosen) :param receive_port: The port on the board that will listen for\ incoming event packets (default is to disable this feature;\ set a value to enable it) :param receive_sdp_port: The SDP port to listen on for incoming event\ packets (defaults to 1) :param receive_tag: The IP tag to use for receiving live events\ (uses any by default) :param virtual_key: The base multicast key to send received events\ with (assigned automatically by default) :param prefix: The prefix to "or" with generated multicast keys\ (default is no prefix) :param prefix_type: Whether the prefix should apply to the upper or\ lower half of the multicast keys (default is upper half) :param check_keys: True if the keys of received events should be\ verified before sending (default False) :param send_buffer_times: An array of arrays of times at which keys\ should be sent (one array for each key, default disabled) :param send_buffer_max_space: The maximum amount of space to use of\ the SDRAM on the machine (default is 1MB) :param send_buffer_space_before_notify: The amount of space free in\ the sending buffer before the machine will ask the host for\ more data (default setting is optimised for most cases) :param send_buffer_notification_ip_address: The IP address of the host\ that will send new buffers (must be specified if a send buffer\ is specified) :param send_buffer_notification_port: The port that the host that will\ send new buffers is listening on (must be specified if a\ send buffer is specified) :param send_buffer_notification_tag: The IP tag to use to notify the\ host about space in the buffer (default is to use any tag) """ # Set up super types PartitionedVertex.__init__( self, resources_required, label, constraints) AbstractDataSpecableVertex.__init__( self, machine_time_step, timescale_factor) ProvidesProvenanceDataFromMachineImpl.__init__( self, self._REGIONS.PROVENANCE_REGION.value, 0) AbstractProvidesOutgoingPartitionConstraints.__init__(self) ReceiveBuffersToHostBasicImpl.__init__(self) # Set up for receiving live packets if receive_port is not None: self.add_constraint(TagAllocatorRequireReverseIptagConstraint( receive_port, receive_sdp_port, board_address, receive_tag)) # Work out if buffers are being sent self._first_machine_time_step = 0 self._send_buffer = None if send_buffer_times is None: self._send_buffer_times = None SendsBuffersFromHostPreBufferedImpl.__init__( self, None) else: self._send_buffer = BufferedSendingRegion(send_buffer_max_space) self._send_buffer_times = send_buffer_times self.add_constraint(TagAllocatorRequireIptagConstraint( send_buffer_notification_ip_address, send_buffer_notification_port, True, board_address, send_buffer_notification_tag)) SendsBuffersFromHostPreBufferedImpl.__init__( self, {self._REGIONS.SEND_BUFFER.value: self._send_buffer}) # buffered out parameters self._send_buffer_space_before_notify = send_buffer_space_before_notify self._send_buffer_notification_ip_address = \ send_buffer_notification_ip_address self._send_buffer_notification_port = send_buffer_notification_port self._send_buffer_notification_tag = send_buffer_notification_tag if self._send_buffer_space_before_notify > send_buffer_max_space: self._send_buffer_space_before_notify = send_buffer_max_space # Set up for recording (if requested) self._record_buffer_size = 0 self._buffer_size_before_receive = 0 # set flag for checking if in injection mode self._in_injection_mode = receive_port is not None # Sort out the keys to be used self._n_keys = n_keys self._virtual_key = virtual_key self._mask = None self._prefix = prefix self._prefix_type = prefix_type self._check_keys = check_keys # Work out the prefix details if self._prefix is not None: if self._prefix_type is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD if self._prefix_type == EIEIOPrefix.UPPER_HALF_WORD: self._prefix = prefix << 16 # If the user has specified a virtual key if self._virtual_key is not None: # check that virtual key is valid if self._virtual_key < 0: raise ConfigurationException( "Virtual keys must be positive") # Get a mask and maximum number of keys for the number of keys # requested self._mask, max_key = self._calculate_mask(n_keys) # Check that the number of keys and the virtual key don't interfere if n_keys > max_key: raise ConfigurationException( "The mask calculated from the number of keys will " "not work with the virtual key specified") if self._prefix is not None: # Check that the prefix doesn't change the virtual key in the # masked area masked_key = (self._virtual_key | self._prefix) & self._mask if self._virtual_key != masked_key: raise ConfigurationException( "The number of keys, virtual key and key prefix" " settings don't work together") else: # If no prefix was generated, generate one self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key
class ReverseIPTagMulticastSourcePartitionedVertex( PartitionedVertex, AbstractDataSpecableVertex, ProvidesProvenanceDataFromMachineImpl, AbstractProvidesOutgoingPartitionConstraints, SendsBuffersFromHostPreBufferedImpl, ReceiveBuffersToHostBasicImpl, AbstractRecordable): """ A model which allows events to be injected into spinnaker and\ converted in to multicast packets """ _REGIONS = Enum( value="_REGIONS", names=[('SYSTEM', 0), ('CONFIGURATION', 1), ('SEND_BUFFER', 2), ('RECORDING_BUFFER', 3), ('RECORDING_BUFFER_STATE', 4), ('PROVENANCE_REGION', 5)]) _CONFIGURATION_REGION_SIZE = 40 def __init__( self, n_keys, resources_required, machine_time_step, timescale_factor, label, constraints=None, # General input and output parameters board_address=None, # Live input parameters receive_port=None, receive_sdp_port=( constants.SDP_PORTS.INPUT_BUFFERING_SDP_PORT.value), receive_tag=None, # Key parameters virtual_key=None, prefix=None, prefix_type=None, check_keys=False, # Send buffer parameters send_buffer_times=None, send_buffer_max_space=( constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP), send_buffer_space_before_notify=640, send_buffer_notification_ip_address=None, send_buffer_notification_port=None, send_buffer_notification_tag=None): """ :param n_keys: The number of keys to be sent via this multicast source :param resources_required: The resources required by the vertex :param machine_time_step: The time step to be used on the machine :param timescale_factor: The time scaling to be used in the simulation :param label: The label of this vertex :param constraints: Any initial constraints to this vertex :param board_address: The IP address of the board on which to place\ this vertex if receiving data, either buffered or live (by\ default, any board is chosen) :param receive_port: The port on the board that will listen for\ incoming event packets (default is to disable this feature;\ set a value to enable it) :param receive_sdp_port: The SDP port to listen on for incoming event\ packets (defaults to 1) :param receive_tag: The IP tag to use for receiving live events\ (uses any by default) :param virtual_key: The base multicast key to send received events\ with (assigned automatically by default) :param prefix: The prefix to "or" with generated multicast keys\ (default is no prefix) :param prefix_type: Whether the prefix should apply to the upper or\ lower half of the multicast keys (default is upper half) :param check_keys: True if the keys of received events should be\ verified before sending (default False) :param send_buffer_times: An array of arrays of times at which keys\ should be sent (one array for each key, default disabled) :param send_buffer_max_space: The maximum amount of space to use of\ the SDRAM on the machine (default is 1MB) :param send_buffer_space_before_notify: The amount of space free in\ the sending buffer before the machine will ask the host for\ more data (default setting is optimised for most cases) :param send_buffer_notification_ip_address: The IP address of the host\ that will send new buffers (must be specified if a send buffer\ is specified) :param send_buffer_notification_port: The port that the host that will\ send new buffers is listening on (must be specified if a\ send buffer is specified) :param send_buffer_notification_tag: The IP tag to use to notify the\ host about space in the buffer (default is to use any tag) """ # Set up super types PartitionedVertex.__init__( self, resources_required, label, constraints) AbstractDataSpecableVertex.__init__( self, machine_time_step, timescale_factor) ProvidesProvenanceDataFromMachineImpl.__init__( self, self._REGIONS.PROVENANCE_REGION.value, 0) AbstractProvidesOutgoingPartitionConstraints.__init__(self) ReceiveBuffersToHostBasicImpl.__init__(self) # Set up for receiving live packets if receive_port is not None: self.add_constraint(TagAllocatorRequireReverseIptagConstraint( receive_port, receive_sdp_port, board_address, receive_tag)) # Work out if buffers are being sent self._first_machine_time_step = 0 self._send_buffer = None if send_buffer_times is None: self._send_buffer_times = None SendsBuffersFromHostPreBufferedImpl.__init__( self, None) else: self._send_buffer = BufferedSendingRegion(send_buffer_max_space) self._send_buffer_times = send_buffer_times self.add_constraint(TagAllocatorRequireIptagConstraint( send_buffer_notification_ip_address, send_buffer_notification_port, True, board_address, send_buffer_notification_tag)) SendsBuffersFromHostPreBufferedImpl.__init__( self, {self._REGIONS.SEND_BUFFER.value: self._send_buffer}) # buffered out parameters self._send_buffer_space_before_notify = send_buffer_space_before_notify self._send_buffer_notification_ip_address = \ send_buffer_notification_ip_address self._send_buffer_notification_port = send_buffer_notification_port self._send_buffer_notification_tag = send_buffer_notification_tag if self._send_buffer_space_before_notify > send_buffer_max_space: self._send_buffer_space_before_notify = send_buffer_max_space # Set up for recording (if requested) self._record_buffer_size = 0 self._buffer_size_before_receive = 0 # set flag for checking if in injection mode self._in_injection_mode = receive_port is not None # Sort out the keys to be used self._n_keys = n_keys self._virtual_key = virtual_key self._mask = None self._prefix = prefix self._prefix_type = prefix_type self._check_keys = check_keys # Work out the prefix details if self._prefix is not None: if self._prefix_type is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD if self._prefix_type == EIEIOPrefix.UPPER_HALF_WORD: self._prefix = prefix << 16 # If the user has specified a virtual key if self._virtual_key is not None: # check that virtual key is valid if self._virtual_key < 0: raise ConfigurationException( "Virtual keys must be positive") # Get a mask and maximum number of keys for the number of keys # requested self._mask, max_key = self._calculate_mask(n_keys) # Check that the number of keys and the virtual key don't interfere if n_keys > max_key: raise ConfigurationException( "The mask calculated from the number of keys will " "not work with the virtual key specified") if self._prefix is not None: # Check that the prefix doesn't change the virtual key in the # masked area masked_key = (self._virtual_key | self._prefix) & self._mask if self._virtual_key != masked_key: raise ConfigurationException( "The number of keys, virtual key and key prefix" " settings don't work together") else: # If no prefix was generated, generate one self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key @staticmethod def n_regions_to_allocate(send_buffering, recording): """ Get the number of regions that will be allocated """ if recording and send_buffering: return 5 elif recording or send_buffering: return 4 return 3 @property def send_buffer_times(self): return self._send_buffer_times @send_buffer_times.setter def send_buffer_times(self, send_buffer_times): self._send_buffer_times = send_buffer_times @property def first_machine_time_step(self): return self._first_machine_time_step @first_machine_time_step.setter def first_machine_time_step(self, first_machine_time_step): self._first_machine_time_step = first_machine_time_step self._fill_send_buffer() def _is_in_range(self, time_stamp_in_ticks): return ( (self._no_machine_time_steps is None) or ( self._first_machine_time_step <= time_stamp_in_ticks <= self._no_machine_time_steps)) def _fill_send_buffer(self): """ Fill the send buffer with keys to send """ # Skip if the virtual key is not yet defined key_to_send = self._virtual_key if self._virtual_key is None: key_to_send = 0 if self._send_buffer is not None: self._send_buffer.clear() if (self._send_buffer_times is not None and len(self._send_buffer_times) != 0): if hasattr(self._send_buffer_times[0], "__len__"): # Works with a list-of-lists for key in range(self._n_keys): for timeStamp in sorted(self._send_buffer_times[key]): time_stamp_in_ticks = int(math.ceil( float(int(timeStamp * 1000.0)) / self._machine_time_step)) if self._is_in_range(time_stamp_in_ticks): self._send_buffer.add_key( time_stamp_in_ticks, key_to_send + key) else: # Work with a single list key_list = [ key + key_to_send for key in range(self._n_keys)] for timeStamp in sorted(self._send_buffer_times): time_stamp_in_ticks = int(math.ceil( float(int(timeStamp * 1000.0)) / self._machine_time_step)) # add to send_buffer collection if self._is_in_range(time_stamp_in_ticks): self._send_buffer.add_keys( time_stamp_in_ticks, key_list) @staticmethod def _generate_prefix(virtual_key, prefix_type): if prefix_type == EIEIOPrefix.LOWER_HALF_WORD: return virtual_key & 0xFFFF return (virtual_key >> 16) & 0xFFFF @staticmethod def _calculate_mask(n_neurons): if n_neurons == 1: return 0xFFFFFFFF, 1 temp_value = int(math.ceil(math.log(n_neurons, 2))) max_key = (int(math.pow(2, temp_value)) - 1) mask = 0xFFFFFFFF - max_key return mask, max_key def enable_recording( self, buffering_ip_address, buffering_port, board_address=None, notification_tag=None, record_buffer_size=constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP, buffer_size_before_receive=(constants. DEFAULT_BUFFER_SIZE_BEFORE_RECEIVE), minimum_sdram_for_buffering=0, buffered_sdram_per_timestep=0): self.activate_buffering_output( buffering_ip_address, buffering_port, board_address, notification_tag, minimum_sdram_for_buffering, buffered_sdram_per_timestep) self._record_buffer_size = record_buffer_size self._buffer_size_before_receive = buffer_size_before_receive def _reserve_regions(self, spec): # Reserve system and configuration memory regions: spec.reserve_memory_region( region=self._REGIONS.SYSTEM.value, size=((constants.DATA_SPECABLE_BASIC_SETUP_INFO_N_WORDS * 4) + self.get_recording_data_size(1)), label='SYSTEM') spec.reserve_memory_region( region=self._REGIONS.CONFIGURATION.value, size=self._CONFIGURATION_REGION_SIZE, label='CONFIGURATION') # Reserve send buffer region if required if self._send_buffer_times is not None: max_buffer_size = self.get_max_buffer_size_possible( self._REGIONS.SEND_BUFFER.value) spec.reserve_memory_region( region=self._REGIONS.SEND_BUFFER.value, size=max_buffer_size, label="SEND_BUFFER", empty=True) # Reserve recording buffer regions if required self.reserve_buffer_regions( spec, self._REGIONS.RECORDING_BUFFER_STATE.value, [self._REGIONS.RECORDING_BUFFER.value], [self._record_buffer_size]) self.reserve_provenance_data_region(spec) def _update_virtual_key(self, routing_info, partitioned_graph): if self._virtual_key is None: partitions = \ partitioned_graph.outgoing_edges_partitions_from_vertex(self) for partition in partitions.values(): key_and_mask = routing_info.get_keys_and_masks_from_partition( partition)[0] self._mask = key_and_mask.mask self._virtual_key = key_and_mask.key if self._prefix is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key def _write_configuration(self, spec, routing_info, sub_graph, ip_tags): spec.switch_write_focus(region=self._REGIONS.CONFIGURATION.value) # Write apply_prefix and prefix and prefix_type if self._prefix is None: spec.write_value(data=0) spec.write_value(data=0) spec.write_value(data=0) else: spec.write_value(data=1) spec.write_value(data=self._prefix) spec.write_value(data=self._prefix_type.value) # Write check if self._check_keys: spec.write_value(data=1) else: spec.write_value(data=0) # Write if you have a key to transmit write it and the mask, # otherwise write flags to fill in space if self._virtual_key is None: spec.write_value(data=0) spec.write_value(data=0) spec.write_value(data=0) else: spec.write_value(data=1) spec.write_value(data=self._virtual_key) spec.write_value(data=self._mask) # Write send buffer data if self._send_buffer_times is not None: this_tag = None for tag in ip_tags: if (tag.ip_address == self._send_buffer_notification_ip_address and tag.port == self._send_buffer_notification_port): this_tag = tag if this_tag is None: raise Exception("Could not find tag for send buffering") buffer_space = self.get_max_buffer_size_possible( self._REGIONS.SEND_BUFFER.value) spec.write_value(data=buffer_space) spec.write_value(data=self._send_buffer_space_before_notify) spec.write_value(data=this_tag.tag) else: spec.write_value(data=0) spec.write_value(data=0) spec.write_value(data=0) def generate_data_spec( self, subvertex, placement, sub_graph, graph, routing_info, hostname, graph_subgraph_mapper, report_folder, ip_tags, reverse_ip_tags, write_text_specs, application_run_time_folder): # Create new DataSpec for this processor: data_writer, report_writer = \ self.get_data_spec_file_writers( placement.x, placement.y, placement.p, hostname, report_folder, write_text_specs, application_run_time_folder) spec = DataSpecificationGenerator(data_writer, report_writer) self._update_virtual_key(routing_info, sub_graph) self._fill_send_buffer() # Reserve regions self._reserve_regions(spec) # Write the system region self._write_basic_setup_info(spec, self._REGIONS.SYSTEM.value) # Write the additional recording information self.write_recording_data( spec, ip_tags, [self._record_buffer_size], self._buffer_size_before_receive) # Write the configuration information self._write_configuration(spec, routing_info, sub_graph, ip_tags) # End spec spec.end_specification() data_writer.close() return data_writer.filename def get_binary_file_name(self): return "reverse_iptag_multicast_source.aplx" def get_outgoing_partition_constraints(self, partition, graph_mapper): if self._virtual_key is not None: return list([KeyAllocatorFixedKeyAndMaskConstraint( [BaseKeyAndMask(self._virtual_key, self._mask)])]) return list() def is_data_specable(self): return True @property def virtual_key(self): return self._virtual_key @property def mask(self): return self._mask @property def is_in_injection_mode(self): return self._in_injection_mode def is_recording(self): return self._record_buffer_size > 0
def __init__( self, n_keys, resources_required, machine_time_step, timescale_factor, label, constraints=None, # General input and output parameters board_address=None, # Live input parameters receive_port=None, receive_sdp_port=( constants.SDP_PORTS.INPUT_BUFFERING_SDP_PORT.value), receive_tag=None, # Key parameters virtual_key=None, prefix=None, prefix_type=None, check_keys=False, # Send buffer parameters send_buffer_times=None, send_buffer_max_space=( constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP), send_buffer_space_before_notify=640, send_buffer_notification_ip_address=None, send_buffer_notification_port=None, send_buffer_notification_tag=None): """ :param n_keys: The number of keys to be sent via this multicast source :param resources_required: The resources required by the vertex :param machine_time_step: The time step to be used on the machine :param timescale_factor: The time scaling to be used in the simulation :param label: The label of this vertex :param constraints: Any initial constraints to this vertex :param board_address: The IP address of the board on which to place\ this vertex if receiving data, either buffered or live (by\ default, any board is chosen) :param receive_port: The port on the board that will listen for\ incoming event packets (default is to disable this feature;\ set a value to enable it) :param receive_sdp_port: The SDP port to listen on for incoming event\ packets (defaults to 1) :param receive_tag: The IP tag to use for receiving live events\ (uses any by default) :param virtual_key: The base multicast key to send received events\ with (assigned automatically by default) :param prefix: The prefix to "or" with generated multicast keys\ (default is no prefix) :param prefix_type: Whether the prefix should apply to the upper or\ lower half of the multicast keys (default is upper half) :param check_keys: True if the keys of received events should be\ verified before sending (default False) :param send_buffer_times: An array of arrays of times at which keys\ should be sent (one array for each key, default disabled) :param send_buffer_max_space: The maximum amount of space to use of\ the SDRAM on the machine (default is 1MB) :param send_buffer_space_before_notify: The amount of space free in\ the sending buffer before the machine will ask the host for\ more data (default setting is optimised for most cases) :param send_buffer_notification_ip_address: The IP address of the host\ that will send new buffers (must be specified if a send buffer\ is specified) :param send_buffer_notification_port: The port that the host that will\ send new buffers is listening on (must be specified if a\ send buffer is specified) :param send_buffer_notification_tag: The IP tag to use to notify the\ host about space in the buffer (default is to use any tag) """ # Set up super types AbstractDataSpecableVertex.__init__( self, machine_time_step, timescale_factor) PartitionedVertex.__init__( self, resources_required, label, constraints) AbstractProvidesOutgoingEdgeConstraints.__init__(self) ReceiveBuffersToHostBasicImpl.__init__(self) # Set up for receiving live packets if receive_port is not None: self.add_constraint(TagAllocatorRequireReverseIptagConstraint( receive_port, receive_sdp_port, board_address, receive_tag)) # Work out if buffers are being sent self._first_machine_time_step = 0 self._send_buffer = None if send_buffer_times is None: self._send_buffer_times = None SendsBuffersFromHostPartitionedVertexPreBufferedImpl.__init__( self, None) else: self._send_buffer = BufferedSendingRegion(send_buffer_max_space) self._send_buffer_times = send_buffer_times self.add_constraint(TagAllocatorRequireIptagConstraint( send_buffer_notification_ip_address, send_buffer_notification_port, True, board_address, send_buffer_notification_tag)) SendsBuffersFromHostPartitionedVertexPreBufferedImpl.__init__( self, {self._REGIONS.SEND_BUFFER.value: self._send_buffer}) # buffered out parameters self._send_buffer_space_before_notify = send_buffer_space_before_notify self._send_buffer_notification_ip_address = \ send_buffer_notification_ip_address self._send_buffer_notification_port = send_buffer_notification_port self._send_buffer_notification_tag = send_buffer_notification_tag if self._send_buffer_space_before_notify > send_buffer_max_space: self._send_buffer_space_before_notify = send_buffer_max_space # Set up for recording (if requested) self._record_buffer_size = 0 self._buffer_size_before_receive = 0 # Sort out the keys to be used self._n_keys = n_keys self._virtual_key = virtual_key self._mask = None self._prefix = prefix self._prefix_type = prefix_type self._check_keys = check_keys # Work out the prefix details if self._prefix is not None: if self._prefix_type is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD if self._prefix_type == EIEIOPrefix.UPPER_HALF_WORD: self._prefix = prefix << 16 # If the user has specified a virtual key if self._virtual_key is not None: # check that virtual key is valid if self._virtual_key < 0: raise ConfigurationException( "Virtual keys must be positive") # Get a mask and maximum number of keys for the number of keys # requested self._mask, max_key = self._calculate_mask(n_keys) # Check that the number of keys and the virtual key don't interfere if n_keys > max_key: raise ConfigurationException( "The mask calculated from the number of keys will " "not work with the virtual key specified") if self._prefix is not None: # Check that the prefix doesn't change the virtual key in the # masked area masked_key = (self._virtual_key | self._prefix) & self._mask if self._virtual_key != masked_key: raise ConfigurationException( "The number of keys, virtual key and key prefix" " settings don't work together") else: # If no prefix was generated, generate one self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key
class ReverseIPTagMulticastSourcePartitionedVertex( AbstractDataSpecableVertex, PartitionedVertex, AbstractProvidesOutgoingEdgeConstraints, SendsBuffersFromHostPartitionedVertexPreBufferedImpl, ReceiveBuffersToHostBasicImpl): """ A model which allows events to be injected into spinnaker and\ converted in to multicast packets """ _REGIONS = Enum( value="_REGIONS", names=[('SYSTEM', 0), ('CONFIGURATION', 1), ('SEND_BUFFER', 2), ('RECORDING_BUFFER', 3), ('RECORDING_BUFFER_STATE', 4)]) _CONFIGURATION_REGION_SIZE = 36 def __init__( self, n_keys, resources_required, machine_time_step, timescale_factor, label, constraints=None, # General input and output parameters board_address=None, # Live input parameters receive_port=None, receive_sdp_port=( constants.SDP_PORTS.INPUT_BUFFERING_SDP_PORT.value), receive_tag=None, # Key parameters virtual_key=None, prefix=None, prefix_type=None, check_keys=False, # Send buffer parameters send_buffer_times=None, send_buffer_max_space=( constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP), send_buffer_space_before_notify=640, send_buffer_notification_ip_address=None, send_buffer_notification_port=None, send_buffer_notification_tag=None): """ :param n_keys: The number of keys to be sent via this multicast source :param resources_required: The resources required by the vertex :param machine_time_step: The time step to be used on the machine :param timescale_factor: The time scaling to be used in the simulation :param label: The label of this vertex :param constraints: Any initial constraints to this vertex :param board_address: The IP address of the board on which to place\ this vertex if receiving data, either buffered or live (by\ default, any board is chosen) :param receive_port: The port on the board that will listen for\ incoming event packets (default is to disable this feature;\ set a value to enable it) :param receive_sdp_port: The SDP port to listen on for incoming event\ packets (defaults to 1) :param receive_tag: The IP tag to use for receiving live events\ (uses any by default) :param virtual_key: The base multicast key to send received events\ with (assigned automatically by default) :param prefix: The prefix to "or" with generated multicast keys\ (default is no prefix) :param prefix_type: Whether the prefix should apply to the upper or\ lower half of the multicast keys (default is upper half) :param check_keys: True if the keys of received events should be\ verified before sending (default False) :param send_buffer_times: An array of arrays of times at which keys\ should be sent (one array for each key, default disabled) :param send_buffer_max_space: The maximum amount of space to use of\ the SDRAM on the machine (default is 1MB) :param send_buffer_space_before_notify: The amount of space free in\ the sending buffer before the machine will ask the host for\ more data (default setting is optimised for most cases) :param send_buffer_notification_ip_address: The IP address of the host\ that will send new buffers (must be specified if a send buffer\ is specified) :param send_buffer_notification_port: The port that the host that will\ send new buffers is listening on (must be specified if a\ send buffer is specified) :param send_buffer_notification_tag: The IP tag to use to notify the\ host about space in the buffer (default is to use any tag) """ # Set up super types AbstractDataSpecableVertex.__init__( self, machine_time_step, timescale_factor) PartitionedVertex.__init__( self, resources_required, label, constraints) AbstractProvidesOutgoingEdgeConstraints.__init__(self) ReceiveBuffersToHostBasicImpl.__init__(self) # Set up for receiving live packets if receive_port is not None: self.add_constraint(TagAllocatorRequireReverseIptagConstraint( receive_port, receive_sdp_port, board_address, receive_tag)) # Work out if buffers are being sent self._first_machine_time_step = 0 self._send_buffer = None if send_buffer_times is None: self._send_buffer_times = None SendsBuffersFromHostPartitionedVertexPreBufferedImpl.__init__( self, None) else: self._send_buffer = BufferedSendingRegion(send_buffer_max_space) self._send_buffer_times = send_buffer_times self.add_constraint(TagAllocatorRequireIptagConstraint( send_buffer_notification_ip_address, send_buffer_notification_port, True, board_address, send_buffer_notification_tag)) SendsBuffersFromHostPartitionedVertexPreBufferedImpl.__init__( self, {self._REGIONS.SEND_BUFFER.value: self._send_buffer}) # buffered out parameters self._send_buffer_space_before_notify = send_buffer_space_before_notify self._send_buffer_notification_ip_address = \ send_buffer_notification_ip_address self._send_buffer_notification_port = send_buffer_notification_port self._send_buffer_notification_tag = send_buffer_notification_tag if self._send_buffer_space_before_notify > send_buffer_max_space: self._send_buffer_space_before_notify = send_buffer_max_space # Set up for recording (if requested) self._record_buffer_size = 0 self._buffer_size_before_receive = 0 # Sort out the keys to be used self._n_keys = n_keys self._virtual_key = virtual_key self._mask = None self._prefix = prefix self._prefix_type = prefix_type self._check_keys = check_keys # Work out the prefix details if self._prefix is not None: if self._prefix_type is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD if self._prefix_type == EIEIOPrefix.UPPER_HALF_WORD: self._prefix = prefix << 16 # If the user has specified a virtual key if self._virtual_key is not None: # check that virtual key is valid if self._virtual_key < 0: raise ConfigurationException( "Virtual keys must be positive") # Get a mask and maximum number of keys for the number of keys # requested self._mask, max_key = self._calculate_mask(n_keys) # Check that the number of keys and the virtual key don't interfere if n_keys > max_key: raise ConfigurationException( "The mask calculated from the number of keys will " "not work with the virtual key specified") if self._prefix is not None: # Check that the prefix doesn't change the virtual key in the # masked area masked_key = (self._virtual_key | self._prefix) & self._mask if self._virtual_key != masked_key: raise ConfigurationException( "The number of keys, virtual key and key prefix" " settings don't work together") else: # If no prefix was generated, generate one self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key @staticmethod def n_regions_to_allocate(send_buffering, recording): """ Get the number of regions that will be allocated """ if recording and send_buffering: return 5 elif recording or send_buffering: return 4 return 3 @property def send_buffer_times(self): return self._send_buffer_times @send_buffer_times.setter def send_buffer_times(self, send_buffer_times): self._send_buffer_times = send_buffer_times @property def first_machine_time_step(self): return self._first_machine_time_step @first_machine_time_step.setter def first_machine_time_step(self, first_machine_time_step): self._first_machine_time_step = first_machine_time_step self._fill_send_buffer() def _is_in_range(self, time_stamp_in_ticks): return ( (self._no_machine_time_steps is None) or ( self._first_machine_time_step <= time_stamp_in_ticks <= self._no_machine_time_steps)) def _fill_send_buffer(self): """ Fill the send buffer with keys to send """ # Skip if the virtual key is not yet defined if self._virtual_key is None: return if self._send_buffer is not None: self._send_buffer.clear() if (self._send_buffer_times is not None and len(self._send_buffer_times) != 0): if hasattr(self._send_buffer_times[0], "__len__"): # Works with a list-of-lists for key in range(self._n_keys): for timeStamp in sorted(self._send_buffer_times[key]): time_stamp_in_ticks = int(math.ceil( float(int(timeStamp * 1000.0)) / self._machine_time_step)) if self._is_in_range(time_stamp_in_ticks): self._send_buffer.add_key( time_stamp_in_ticks, self._virtual_key + key) else: # Work with a single list key_list = [ key + self._virtual_key for key in range(self._n_keys)] for timeStamp in sorted(self._send_buffer_times): time_stamp_in_ticks = int(math.ceil( float(int(timeStamp * 1000.0)) / self._machine_time_step)) # add to send_buffer collection if self._is_in_range(time_stamp_in_ticks): self._send_buffer.add_keys( time_stamp_in_ticks, key_list) @staticmethod def _generate_prefix(virtual_key, prefix_type): if prefix_type == EIEIOPrefix.LOWER_HALF_WORD: return virtual_key & 0xFFFF return (virtual_key >> 16) & 0xFFFF @staticmethod def _calculate_mask(n_neurons): temp_value = int(math.ceil(math.log(n_neurons, 2))) max_key = (int(math.pow(2, temp_value)) - 1) mask = 0xFFFFFFFF - max_key return mask, max_key def enable_recording( self, buffering_ip_address, buffering_port, board_address=None, notification_tag=None, record_buffer_size=constants.MAX_SIZE_OF_BUFFERED_REGION_ON_CHIP, buffer_size_before_receive=(constants. DEFAULT_BUFFER_SIZE_BEFORE_RECEIVE)): self.set_buffering_output( buffering_ip_address, buffering_port, board_address, notification_tag) self._record_buffer_size = record_buffer_size self._buffer_size_before_receive = buffer_size_before_receive def _reserve_regions(self, spec): # Reserve system and configuration memory regions: spec.reserve_memory_region( region=self._REGIONS.SYSTEM.value, size=((constants.DATA_SPECABLE_BASIC_SETUP_INFO_N_WORDS * 4) + self.get_recording_data_size(1)), label='SYSTEM') spec.reserve_memory_region( region=self._REGIONS.CONFIGURATION.value, size=self._CONFIGURATION_REGION_SIZE, label='CONFIGURATION') # Reserve send buffer region if required if self._send_buffer_times is not None: max_buffer_size = self.get_max_buffer_size_possible( self._REGIONS.SEND_BUFFER.value) spec.reserve_memory_region( region=self._REGIONS.SEND_BUFFER.value, size=max_buffer_size, label="SEND_BUFFER", empty=True) # Reserve recording buffer regions if required self.reserve_buffer_regions( spec, self._REGIONS.RECORDING_BUFFER_STATE.value, [self._REGIONS.RECORDING_BUFFER.value], [self._record_buffer_size]) def _update_virtual_key(self, routing_info, sub_graph): if self._virtual_key is None: subedge_routing_info = \ routing_info.get_subedge_information_from_subedge( sub_graph.outgoing_subedges_from_subvertex(self)[0]) key_and_mask = subedge_routing_info.keys_and_masks[0] self._mask = key_and_mask.mask self._virtual_key = key_and_mask.key if self._prefix is None: self._prefix_type = EIEIOPrefix.UPPER_HALF_WORD self._prefix = self._virtual_key def _write_configuration(self, spec, routing_info, sub_graph, ip_tags): spec.switch_write_focus(region=self._REGIONS.CONFIGURATION.value) # Write apply_prefix and prefix and prefix_type if self._prefix is None: spec.write_value(data=0) spec.write_value(data=0) spec.write_value(data=0) else: spec.write_value(data=1) spec.write_value(data=self._prefix) spec.write_value(data=self._prefix_type.value) # Write check if self._check_keys: spec.write_value(data=1) else: spec.write_value(data=0) # Write key and mask spec.write_value(data=self._virtual_key) spec.write_value(data=self._mask) # Write send buffer data if self._send_buffer_times is not None: this_tag = None for tag in ip_tags: if (tag.ip_address == self._send_buffer_notification_ip_address and tag.port == self._send_buffer_notification_port): this_tag = tag if this_tag is None: raise Exception("Could not find tag for send buffering") buffer_space = self.get_max_buffer_size_possible( self._REGIONS.SEND_BUFFER.value) spec.write_value(data=buffer_space) spec.write_value(data=self._send_buffer_space_before_notify) spec.write_value(data=this_tag.tag) else: spec.write_value(data=0) spec.write_value(data=0) spec.write_value(data=0) def generate_data_spec( self, subvertex, placement, sub_graph, graph, routing_info, hostname, graph_subgraph_mapper, report_folder, ip_tags, reverse_ip_tags, write_text_specs, application_run_time_folder): # Create new DataSpec for this processor: data_writer, report_writer = \ self.get_data_spec_file_writers( placement.x, placement.y, placement.p, hostname, report_folder, write_text_specs, application_run_time_folder) spec = DataSpecificationGenerator(data_writer, report_writer) self._update_virtual_key(routing_info, sub_graph) self._fill_send_buffer() # Reserve regions self._reserve_regions(spec) # Write the system region self._write_basic_setup_info(spec, self._REGIONS.SYSTEM.value) # Write the additional recording information self.write_recording_data( spec, ip_tags, [self._record_buffer_size], self._buffer_size_before_receive) # Write the configuration information self._write_configuration(spec, routing_info, sub_graph, ip_tags) # End spec spec.end_specification() data_writer.close() return [data_writer.filename] def get_binary_file_name(self): return "reverse_iptag_multicast_source.aplx" def get_outgoing_edge_constraints(self, partitioned_edge, graph_mapper): if self._virtual_key is not None: return list([KeyAllocatorFixedKeyAndMaskConstraint( [BaseKeyAndMask(self._virtual_key, self._mask)])]) return list() def is_data_specable(self): return True @property def virtual_key(self): return self._virtual_key @property def mask(self): return self._mask