Skip to content

Commit

Permalink
Merge pull request #245 from SpiNNakerManchester/gfe_live_fixes
Browse files Browse the repository at this point in the history
Gfe live fixes
  • Loading branch information
rowleya authored Jul 12, 2023
2 parents 7e9651e + 2d04aa2 commit 1b5ac18
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 27 deletions.
2 changes: 1 addition & 1 deletion gfe_examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

BUILD_DIRS = hello_world hello_world_untimed Conways template sync_test
BUILD_DIRS = hello_world hello_world_untimed Conways template sync_test live_io
all: $(BUILD_DIRS)
for d in $(BUILD_DIRS); do (cd $$d; "$(MAKE)") || exit $$?; done

Expand Down
25 changes: 25 additions & 0 deletions gfe_examples/live_io/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2023 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# If SPINN_DIRS is not defined, this is an error!
ifndef SPINN_DIRS
$(error SPINN_DIRS is not set. Please define SPINN_DIRS (possibly by running "source setup" in the spinnaker package folder))
endif

APP = live_io
SOURCES = live_io.c

APP_OUTPUT_DIR := $(abspath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))/

include $(SPINN_DIRS)/make/local.mk
13 changes: 13 additions & 0 deletions gfe_examples/live_io/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2023 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
100 changes: 100 additions & 0 deletions gfe_examples/live_io/live_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright (c) 2023 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from time import sleep
from random import randint
from pacman.model.graphs.machine.machine_edge import MachineEdge
from spinn_front_end_common.data.fec_data_view import FecDataView
from spinn_front_end_common.utilities.connections import LiveEventConnection
from spinn_front_end_common.utility_models import (
LivePacketGatherMachineVertex, ReverseIPTagMulticastSourceMachineVertex)
from spinn_front_end_common.utilities.utility_objs import (
LivePacketGatherParameters)
import spinnaker_graph_front_end as front_end
from gfe_examples.live_io.live_io_vertex import LiveIOVertex

n_receivers = 20
receiver_label = "Receiver"
sender_label = "Sender"
sender_partition = "Send"
n_sender_keys = 32
sends_per_cycle = 10
lpg_label = "LPGReceiver"
running = True


def start_sending(label, c):
# pylint: disable=unused-argument
sleep(0.5)
while running:
for _ in range(sends_per_cycle):
key = randint(0, n_sender_keys - 1)
print(f"Sending {key}")
c.send_event(sender_label, key)
sleep(0.1)


def end_sim(label, c):
# pylint: disable=unused-argument
global running
running = False


def receive(label, time, keys):
print(f"Received from {label} at time {time}: {keys}")


# Make a connection to send and receive data
conn = LiveEventConnection(
live_packet_gather_label=lpg_label,
receive_labels=[receiver_label + f" {x}" for x in range(n_receivers)],
send_labels=[sender_label], local_port=None)
conn.add_start_resume_callback(sender_label, start_sending)
conn.add_pause_stop_callback(sender_label, end_sim)
for x in range(n_receivers):
conn.add_receive_callback(receiver_label + f" {x}", receive)

front_end.setup(
n_chips_required=1, model_binary_folder=os.path.dirname(__file__))
front_end.add_socket_address(None, None, conn.local_port)

# Add a sender
sender = ReverseIPTagMulticastSourceMachineVertex(
n_keys=n_sender_keys, label="Sender",
injection_partition_id=sender_partition)
front_end.add_machine_vertex_instance(sender)

live_out = LivePacketGatherMachineVertex(
LivePacketGatherParameters(tag=1, port=10000, hostname="localhost"),
label=lpg_label)
front_end.add_machine_vertex_instance(live_out)


# Put LiveIOVertex on some cores
for x in range(n_receivers):
vertex = LiveIOVertex(
n_keys=n_sender_keys, send_partition=sender_partition,
label=receiver_label + f" {x}")
front_end.add_machine_vertex_instance(vertex)
front_end.add_machine_edge_instance(
MachineEdge(sender, vertex), sender_partition)
front_end.add_machine_edge_instance(
MachineEdge(vertex, live_out), sender_partition)
live_out.add_incoming_source(vertex, sender_partition)
FecDataView.add_live_output_vertex(vertex, sender_partition)

front_end.run(10000)

front_end.stop()
78 changes: 78 additions & 0 deletions gfe_examples/live_io/live_io_vertex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright (c) 2023 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import IntEnum
import logging
from spinn_utilities.log import FormatAdapter
from spinn_utilities.overrides import overrides
from pacman.model.graphs.common import Slice
from pacman.model.graphs.machine import MachineVertex
from pacman.model.resources import ConstantSDRAM
from spinn_front_end_common.utilities.constants import (
SYSTEM_BYTES_REQUIREMENT, BYTES_PER_WORD)
from spinn_front_end_common.abstract_models.impl import (
MachineDataSpecableVertex)
from spinn_front_end_common.data.fec_data_view import FecDataView
from spinnaker_graph_front_end.utilities import SimulatorVertex

logger = FormatAdapter(logging.getLogger(__name__))
N_KEY_DATA_BYTES = 3 * BYTES_PER_WORD


class DataRegions(IntEnum):
SYSTEM = 0
KEY_DATA = 1


class LiveIOVertex(
SimulatorVertex, MachineDataSpecableVertex):

def __init__(self, n_keys, send_partition="LiveOut", label=None):
super().__init__(label, "live_io.aplx")
self.__n_keys = n_keys
self.__send_partition = send_partition
self._vertex_slice = Slice(0, n_keys - 1)

@property
@overrides(MachineVertex.sdram_required)
def sdram_required(self):
return ConstantSDRAM(
SYSTEM_BYTES_REQUIREMENT + N_KEY_DATA_BYTES)

def get_n_keys_for_partition(self, partition_id):
return self.__n_keys

@overrides(MachineDataSpecableVertex.generate_machine_data_specification)
def generate_machine_data_specification(
self, spec, placement, iptags, reverse_iptags):
# Generate the system data region for simulation .c requirements
self.generate_system_region(spec)

spec.reserve_memory_region(DataRegions.KEY_DATA, N_KEY_DATA_BYTES)
spec.switch_write_focus(DataRegions.KEY_DATA)

routing_infos = FecDataView().get_routing_infos()
r_info = routing_infos.get_routing_info_from_pre_vertex(
placement.vertex, self.__send_partition)
if r_info is None:
spec.write_value(0)
spec.write_value(0)
spec.write_value(0)
else:
spec.write_value(1)
spec.write_value(r_info.key)
spec.write_value((~r_info.mask) & 0xFFFFFFFF)

# End-of-Spec:
spec.end_specification()
164 changes: 164 additions & 0 deletions gfe_examples/live_io/src/live_io.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2023 The University of Manchester
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

//! imports
#include <spin1_api.h>
#include "common-typedefs.h"
#include <data_specification.h>
#include <recording.h>
#include <simulation.h>
#include <debug.h>

//! control value, which says how many timer ticks to run for before exiting
static uint32_t simulation_ticks = 0;
static uint32_t infinite_run = 0;
static uint32_t time = 0;

//! int as a bool to represent if this simulation should run forever
static uint32_t infinite_run;

//! The key to send data with
static uint32_t send_key;

//! Whether the data should be sent
static uint32_t do_send;

//! The mask to apply to the key to extract any data before sending
static uint32_t key_mask;

//! human readable definitions of each region in SDRAM
typedef enum regions_e {
SYSTEM_REGION,
KEY_DATA_REGION,
} regions_e;

//! values for the priority for each callback
typedef enum callback_priorities {
MC_PACKET = -1,
SDP = 0,
DMA = 1,
TIMER = 2,
USER = 3
} callback_priorities;

// -------------------------------------------------------------------

static void receive_data_pl(uint key, uint payload) {
log_info("Key=%u Payload=%u", key, payload);
if (do_send) {
uint key_to_send = send_key | (key & key_mask);
spin1_send_mc_packet(key_to_send, payload, 1);
}
}

static void receive_data(uint key, UNUSED uint unused) {
log_info("Key=%u", key);
if (do_send) {
uint key_to_send = send_key | (key & key_mask);
spin1_send_mc_packet(key_to_send, 0, 0);
}
}

static void update(uint ticks, uint b) {
use(b);
use(ticks);

time++;

// check that the run time hasn't already elapsed and thus needs to be
// killed
if ((infinite_run != TRUE) && (time >= simulation_ticks)) {

// fall into the pause resume mode of operating
simulation_handle_pause_resume(NULL);

// switch to state where host is ready to read
simulation_ready_to_read();

return;
}
}

static bool initialize(uint32_t *timer_period) {
log_info("Initialise: started\n");

// Get the address this core's DTCM data starts at from SRAM
data_specification_metadata_t *data = data_specification_get_data_address();

// Read the header
if (!data_specification_read_header(data)) {
log_error("failed to read the data spec header");
return false;
}

// Get the timing details and set up the simulation interface
if (!simulation_initialise(
data_specification_get_region(SYSTEM_REGION, data),
APPLICATION_NAME_HASH, timer_period, &simulation_ticks,
&infinite_run, &time, SDP, DMA)) {
return false;
}

// Get key data
uint *key_data = data_specification_get_region(KEY_DATA_REGION, data);
do_send = key_data[0];
send_key = key_data[1];
key_mask = key_data[2];

if (do_send) {
log_info("Re-sending received keys with key 0x%08x and mask 0x%08x",
send_key, key_mask);
} else {
log_info("Not re-sending received keys");
}

return true;
}

/****f*
*
* SUMMARY
* This function is called at application start-up.
* It is used to register event callbacks and begin the simulation.
*
* SYNOPSIS
* int c_main()
*
* SOURCE
*/
void c_main(void) {

// Load DTCM data
uint32_t timer_period;

// initialise the model
if (!initialize(&timer_period)) {
rt_error(RTE_SWERR);
}

// set timer tick value to configured value
spin1_set_timer_tick(timer_period);

// register callbacks
spin1_callback_on(MCPL_PACKET_RECEIVED, receive_data_pl, MC_PACKET);
spin1_callback_on(MC_PACKET_RECEIVED, receive_data, MC_PACKET);
spin1_callback_on(TIMER_TICK, update, TIMER);

// Start the time at "-1" so that the first tick will be 0
time = UINT32_MAX;

simulation_run();
}
Loading

0 comments on commit 1b5ac18

Please sign in to comment.