from enum import Enum
from collections import Counter
from pacman.model.decorators import overrides
from pacman.model.graphs.machine import MachineVertex
from spinn_front_end_common.abstract_models import AbstractHasAssociatedBinary
from spinn_front_end_common.interface.provenance \
import ProvidesProvenanceDataFromMachineImpl
from spinn_front_end_common.interface.simulation.simulation_utilities \
import get_simulation_header_array
from spinn_front_end_common.utilities.constants \
import SYSTEM_BYTES_REQUIREMENT
from spinn_front_end_common.utilities.utility_objs import ExecutableStartType
[docs]class CommandSenderMachineVertex(
MachineVertex, ProvidesProvenanceDataFromMachineImpl,
AbstractHasAssociatedBinary):
# Regions for populations
DATA_REGIONS = Enum(
value="DATA_REGIONS",
names=[('SYSTEM_REGION', 0),
('SETUP', 1),
('COMMANDS_WITH_ARBITRARY_TIMES', 2),
('COMMANDS_AT_START_RESUME', 3),
('COMMANDS_AT_STOP_PAUSE', 4),
('PROVENANCE_REGION', 5)])
# 4 for key, 4 for has payload, 4 for payload 4 for repeats, 4 for delays
_COMMAND_WITH_PAYLOAD_SIZE = 20
# 4 for the time stamp
_COMMAND_TIMESTAMP_SIZE = 4
# 4 for the int to represent the number of commands
_N_COMMANDS_SIZE = 4
# bool for if the command has a payload (true = 1)
_HAS_PAYLOAD = 1
# bool for if the command does not have a payload (false = 0)
_HAS_NO_PAYLOAD = 0
# Setup data size (one word)
_SETUP_DATA_SIZE = 4
# the number of malloc requests used by the dsg
TOTAL_REQUIRED_MALLOCS = 5
# The name of the binary file
BINARY_FILE_NAME = 'command_sender_multicast_source.aplx'
def __init__(
self, constraints, resources_required, label,
commands_at_start_resume, commands_at_pause_stop, timed_commands):
ProvidesProvenanceDataFromMachineImpl.__init__(self)
MachineVertex.__init__(self, label, constraints)
# container of different types of command
self._timed_commands = timed_commands
self._commands_at_start_resume = commands_at_start_resume
self._commands_at_pause_stop = commands_at_pause_stop
self._resources = resources_required
@property
@overrides(ProvidesProvenanceDataFromMachineImpl._provenance_region_id)
def _provenance_region_id(self):
return self.DATA_REGIONS.PROVENANCE_REGION.value
@property
@overrides(ProvidesProvenanceDataFromMachineImpl._n_additional_data_items)
def _n_additional_data_items(self):
return 0
@property
@overrides(MachineVertex.resources_required)
def resources_required(self):
return self._resources
[docs] def generate_data_specification(
self, spec, placement, machine_time_step, time_scale_factor,
n_machine_time_steps):
timed_commands_size = \
self.get_timed_commands_bytes(self._timed_commands)
start_resume_commands_size = \
self.get_n_command_bytes(self._commands_at_start_resume)
pause_stop_commands_size = \
self.get_n_command_bytes(self._commands_at_pause_stop)
# reverse memory regions
self._reserve_memory_regions(
spec, timed_commands_size, start_resume_commands_size,
pause_stop_commands_size, placement.vertex)
# Write system region
spec.comment("\n*** Spec for multicast source ***\n\n")
spec.switch_write_focus(
CommandSenderMachineVertex.DATA_REGIONS.SYSTEM_REGION.value)
spec.write_array(get_simulation_header_array(
self.get_binary_file_name(), machine_time_step,
time_scale_factor))
# Write setup region
# Find the maximum number of commands per timestep
max_n_commands = 0
if len(self._timed_commands) > 0:
counter = Counter(self._timed_commands)
max_n_commands = counter.most_common(1)[0][1]
max_n_commands = max([
max_n_commands, len(self._commands_at_start_resume),
len(self._commands_at_pause_stop)])
time_between_commands = 0
if max_n_commands > 0:
time_between_commands = (
(machine_time_step * time_scale_factor / 2) / max_n_commands)
spec.switch_write_focus(
CommandSenderMachineVertex.DATA_REGIONS.SETUP.value)
spec.write_value(time_between_commands)
# write commands
spec.switch_write_focus(
region=CommandSenderMachineVertex.DATA_REGIONS.
COMMANDS_WITH_ARBITRARY_TIMES.value)
# write commands to spec for timed commands
self._write_timed_commands(self._timed_commands, spec)
# write commands fired off during a start or resume
spec.switch_write_focus(
region=CommandSenderMachineVertex.DATA_REGIONS.
COMMANDS_AT_START_RESUME.value)
self._write_basic_commands(self._commands_at_start_resume, spec)
# write commands fired off during a pause or end
spec.switch_write_focus(
region=CommandSenderMachineVertex.DATA_REGIONS.
COMMANDS_AT_STOP_PAUSE.value)
self._write_basic_commands(self._commands_at_pause_stop, spec)
# End-of-Spec:
spec.end_specification()
def _write_basic_commands(self, commands, spec):
# number of commands
spec.write_value(len(commands))
# write commands to region
for command in commands:
self._write_command(command, spec)
def _write_timed_commands(self, timed_commands, spec):
spec.write_value(len(timed_commands))
# write commands
for command in timed_commands:
spec.write_value(command.time)
self._write_command(command, spec)
@staticmethod
def _write_command(command, spec):
spec.write_value(command.key)
spec.write_value(CommandSenderMachineVertex._HAS_PAYLOAD)
spec.write_value(command.payload if command.is_payload else 0)
spec.write_value(command.repeat)
spec.write_value(command.delay_between_repeats)
@staticmethod
def _reserve_memory_regions(
spec, time_command_size, start_command_size, end_command_size,
vertex):
"""
Reserve SDRAM space for memory areas:
1) Area for information on what data to record
2) area for start commands
3) area for end commands
"""
spec.comment("\nReserving memory space for data regions:\n\n")
# Reserve memory:
spec.reserve_memory_region(
region=CommandSenderMachineVertex.DATA_REGIONS.SYSTEM_REGION.value,
size=SYSTEM_BYTES_REQUIREMENT, label='system')
spec.reserve_memory_region(
region=CommandSenderMachineVertex.DATA_REGIONS.SETUP.value,
size=CommandSenderMachineVertex._SETUP_DATA_SIZE, label='setup')
spec.reserve_memory_region(
region=CommandSenderMachineVertex.
DATA_REGIONS.COMMANDS_WITH_ARBITRARY_TIMES.value,
size=time_command_size, label='commands with arbitrary times')
spec.reserve_memory_region(
region=CommandSenderMachineVertex.
DATA_REGIONS.COMMANDS_AT_START_RESUME.value,
size=start_command_size, label='commands with start resume times')
spec.reserve_memory_region(
region=CommandSenderMachineVertex.
DATA_REGIONS.COMMANDS_AT_STOP_PAUSE.value,
size=end_command_size, label='commands with stop pause times')
vertex.reserve_provenance_data_region(spec)
[docs] @staticmethod
def get_timed_commands_bytes(timed_commands):
n_bytes = CommandSenderMachineVertex._N_COMMANDS_SIZE
n_bytes += (
(CommandSenderMachineVertex._COMMAND_TIMESTAMP_SIZE +
CommandSenderMachineVertex._COMMAND_WITH_PAYLOAD_SIZE) *
len(timed_commands)
)
return n_bytes
[docs] @staticmethod
def get_n_command_bytes(commands):
n_bytes = CommandSenderMachineVertex._N_COMMANDS_SIZE
n_bytes += (
CommandSenderMachineVertex._COMMAND_WITH_PAYLOAD_SIZE *
len(commands)
)
return n_bytes
[docs] @overrides(AbstractHasAssociatedBinary.get_binary_file_name)
def get_binary_file_name(self):
"""
Return a string representation of the models binary
"""
return self.BINARY_FILE_NAME
[docs] @overrides(AbstractHasAssociatedBinary.get_binary_start_type)
def get_binary_start_type(self):
return ExecutableStartType.USES_SIMULATION_INTERFACE
[docs] @staticmethod
def get_number_of_mallocs_used_by_dsg():
return CommandSenderMachineVertex.TOTAL_REQUIRED_MALLOCS