# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pacman.model.graphs.application import ApplicationEdge
from spinnman.messages.eieio import EIEIOType
from spinn_front_end_common.utilities import helpful_functions
from spinn_front_end_common.utilities.globals_variables import get_simulator
from spinn_front_end_common.utility_models import (
ReverseIpTagMultiCastSource)
from spinn_front_end_common.utilities.notification_protocol import (
SocketAddress)
from spinn_front_end_common.utilities.utility_objs import (
LivePacketGatherParameters)
from spynnaker.pyNN.utilities import constants
from spynnaker.pyNN.models.pynn_population_common import PyNNPopulationCommon
[docs]class SpynnakerExternalDevicePluginManager(object):
""" User-level interface for the external device plugin manager.
"""
__slots__ = []
[docs] @staticmethod
def add_database_socket_address(
database_notify_host, database_notify_port_num,
database_ack_port_num):
config = get_simulator().config
if database_notify_port_num is None:
database_notify_port_num = helpful_functions.read_config_int(
config, "Database", "notify_port")
if database_notify_host is None:
database_notify_host = helpful_functions.read_config(
config, "Database", "notify_hostname")
elif database_notify_host == "0.0.0.0":
database_notify_host = "localhost"
if database_ack_port_num is None:
database_ack_port_num = helpful_functions.read_config_int(
config, "Database", "listen_port")
# build the database socket address used by the notification interface
database_socket = SocketAddress(
listen_port=database_ack_port_num,
notify_host_name=database_notify_host,
notify_port_no=database_notify_port_num)
# update socket interface with new demands.
SpynnakerExternalDevicePluginManager.add_socket_address(
database_socket)
[docs] @staticmethod
def activate_live_output_for(
population, database_notify_host=None,
database_notify_port_num=None,
database_ack_port_num=None, board_address=None, port=None,
host=None, tag=None, strip_sdp=True, use_prefix=False,
key_prefix=None,
prefix_type=None, message_type=EIEIOType.KEY_32_BIT,
right_shift=0, payload_as_time_stamps=True, notify=True,
use_payload_prefix=True, payload_prefix=None,
payload_right_shift=0, number_of_packets_sent_per_time_step=0):
""" Output the spikes from a given population from SpiNNaker as they\
occur in the simulation.
:param population: The population to activate the live output for
:type population: \
:py:class:`spynnaker.pyNN.models.pynn_population_common.PyNNPopulationCommon`
:param database_notify_host: \
The hostname for the device which is listening to the database\
notification.
:type database_notify_host: str
:param database_ack_port_num: \
The port number to which a external device will acknowledge that\
they have finished reading the database and are ready for it to\
start execution
:type database_ack_port_num: int
:param database_notify_port_num: \
The port number to which a external device will receive the\
database is ready command
:type database_notify_port_num: int
:param board_address: \
A fixed board address required for the tag, or None if any\
address is OK
:type board_address: str
:param key_prefix: the prefix to be applied to the key
:type key_prefix: int or None
:param prefix_type: if the prefix type is 32 bit or 16 bit
:param message_type: \
If the message is a EIEIO command message, or an EIEIO data\
message with 16 bit or 32 bit keys.
:param payload_as_time_stamps:
:param right_shift:
:param use_payload_prefix:
:param notify:
:param payload_prefix:
:param payload_right_shift:
:param number_of_packets_sent_per_time_step:
:param port: \
The UDP port to which the live spikes will be sent. If not\
specified, the port will be taken from the "live_spike_port"\
parameter in the "Recording" section of the sPyNNaker\
configuration file.
:type port: int
:param host: \
The host name or IP address to which the live spikes will be\
sent. If not specified, the host will be taken from the\
"live_spike_host" parameter in the "Recording" section of the\
sPyNNaker configuration file.
:type host: str
:param tag: \
The IP tag to be used for the spikes. If not specified, one will\
be automatically assigned
:type tag: int
:param strip_sdp: \
Determines if the SDP headers will be stripped from the\
transmitted packet.
:type strip_sdp: bool
:param use_prefix: \
Determines if the spike packet will contain a common prefix for\
the spikes
:type use_prefix: bool
:param label: The label of the gatherer vertex
:type label: str
:param partition_ids: The names of the partitions to create edges for
:type partition_ids: list(str)
"""
# pylint: disable=too-many-arguments, too-many-locals, protected-access
config = get_simulator().config
# get default params if none set
if port is None:
port = config.getint("Recording", "live_spike_port")
if host is None:
host = str(config.get("Recording", "live_spike_host"))
# add new edge and vertex if required to SpiNNaker graph
SpynnakerExternalDevicePluginManager.update_live_packet_gather_tracker(
population._vertex, "LiveSpikeReceiver", port, host, board_address,
tag, strip_sdp, use_prefix, key_prefix, prefix_type,
message_type, right_shift, payload_as_time_stamps,
use_payload_prefix, payload_prefix, payload_right_shift,
number_of_packets_sent_per_time_step,
partition_ids=[constants.SPIKE_PARTITION_ID])
if notify:
SpynnakerExternalDevicePluginManager.add_database_socket_address(
database_notify_host, database_notify_port_num,
database_ack_port_num)
[docs] @staticmethod
def activate_live_output_to(population, device):
""" Activate the output of spikes from a population to an external\
device. Note that all spikes will be sent to the device.
:param population: \
The pyNN population object from which spikes will be sent.
:type population: \
:py:class:`spynnaker.pyNN.models.pynn_population_common.PyNNPopulationCommon`
:param device: \
The pyNN population or external device to which the spikes will be\
sent.
:type device: \
:py:class:`spynnaker.pyNN.models.pynn_population_common.PyNNPopulationCommon`\
or :py:class:`pacman.model.graphs.application.ApplicationVertex`
"""
device_vertex = device
# pylint: disable=protected-access
if isinstance(device, PyNNPopulationCommon):
device_vertex = device._get_vertex
SpynnakerExternalDevicePluginManager.add_edge(
population._get_vertex, device_vertex,
constants.SPIKE_PARTITION_ID)
[docs] @staticmethod
def add_socket_address(socket_address):
""" Add a socket address to the list to be checked by the\
notification protocol.
:param socket_address: the socket address
:type socket_address:
:rtype: None:
"""
get_simulator().add_socket_address(socket_address)
[docs] @staticmethod
def update_live_packet_gather_tracker(
vertex_to_record_from, lpg_label, port=None, hostname=None,
board_address=None, tag=None, strip_sdp=True, use_prefix=False,
key_prefix=None, prefix_type=None,
message_type=EIEIOType.KEY_32_BIT,
right_shift=0, payload_as_time_stamps=True,
use_payload_prefix=True, payload_prefix=None,
payload_right_shift=0, number_of_packets_sent_per_time_step=0,
partition_ids=None):
""" Add an edge from a vertex to the live packet gatherer, builds as\
needed and has all the parameters for the creation of the live\
packet gatherer if needed.
"""
# pylint: disable=too-many-arguments, too-many-locals
params = LivePacketGatherParameters(
port=port, hostname=hostname, tag=tag, board_address=board_address,
strip_sdp=strip_sdp, use_prefix=use_prefix, key_prefix=key_prefix,
prefix_type=prefix_type, message_type=message_type,
right_shift=right_shift, payload_prefix=payload_prefix,
payload_as_time_stamps=payload_as_time_stamps,
use_payload_prefix=use_payload_prefix,
payload_right_shift=payload_right_shift,
number_of_packets_sent_per_time_step=(
number_of_packets_sent_per_time_step),
label=lpg_label)
# add to the tracker
get_simulator().add_live_packet_gatherer_parameters(
params, vertex_to_record_from, partition_ids)
[docs] @staticmethod
def add_poisson_live_rate_control(
poisson_population, control_label_extension="_control",
receive_port=None, database_notify_host=None,
database_notify_port_num=None,
database_ack_port_num=None, notify=True,
reserve_reverse_ip_tag=False):
""" Add a live rate controller to a Poisson population.
:param poisson_population: The population to control
:type poisson_population: \
:py:class:`spynnaker.pyNN.models.pynn_population_common.PyNNPopulationCommon`
:param control_label_extension:\
An extension to add to the label of the Poisson source. Must\
match up with the equivalent in the\
SpynnakerPoissonControlConnection
:type control_label_extension: str
:param receive_port:\
The port that the SpiNNaker board should listen on
:type receive_port: int
:param database_notify_host: the hostname for the device which is\
listening to the database notification.
:type database_notify_host: str
:param database_ack_port_num: the port number to which a external \
device will acknowledge that they have finished reading the \
database and are ready for it to start execution
:type database_ack_port_num: int
:param database_notify_port_num: The port number to which a external\
device will receive the database is ready command
:type database_notify_port_num: int
:param reserve_reverse_ip_tag: True if a reverse ip tag is to be\
used, False if SDP is to be used (default)
:type reserve_reverse_ip_tag: bool
"""
# pylint: disable=too-many-arguments, protected-access
vertex = poisson_population._get_vertex
control_label = "{}{}".format(vertex.label, control_label_extension)
controller = ReverseIpTagMultiCastSource(
n_keys=vertex.n_atoms, label=control_label,
receive_port=receive_port,
reserve_reverse_ip_tag=reserve_reverse_ip_tag)
SpynnakerExternalDevicePluginManager.add_application_vertex(controller)
SpynnakerExternalDevicePluginManager.add_edge(
controller, vertex, constants.LIVE_POISSON_CONTROL_PARTITION_ID)
if notify:
SpynnakerExternalDevicePluginManager.add_database_socket_address(
database_notify_host, database_notify_port_num,
database_ack_port_num)
[docs] @staticmethod
def add_edge(vertex, device_vertex, partition_id):
""" Add an edge between two vertices (often a vertex and a external\
device) on a given partition.
:param vertex: the pre vertex to connect the edge from
:param device_vertex: the post vertex to connect the edge to
:param partition_id: the partition identifier for making nets
:rtype: None
"""
_spinnaker = get_simulator()
edge = ApplicationEdge(vertex, device_vertex)
_spinnaker.add_application_edge(edge, partition_id)
[docs] @staticmethod
def add_application_vertex(vertex):
get_simulator().add_application_vertex(vertex)
[docs] @staticmethod
def machine_time_step():
return get_simulator().machine_time_step
[docs] @staticmethod
def time_scale_factor():
return get_simulator().timescale_factor