sPyNNaker neural_modelling  7.4.2
spike_processing.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 The University of Manchester
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
19 #include "spike_processing.h"
21 #include "synapse_row.h"
22 #include "synapses.h"
24 #include <simulation.h>
25 #include <debug.h>
26 #include <common/in_spikes.h>
27 #include <recording.h>
28 
31 typedef struct dma_buffer {
34 
38 
41 
43  uint32_t colour;
44 
46  uint32_t colour_mask;
47 
50 } dma_buffer;
51 
53 #define N_DMA_BUFFERS 2
54 
61 };
62 
64 extern uint32_t time;
65 
67 static volatile bool dma_busy;
68 
71 
73 static uint32_t next_buffer_to_fill;
74 
76 static uint32_t buffer_being_read;
77 
79 static volatile uint32_t rewires_to_do = 0;
80 
84 static uint32_t dma_n_rewires;
85 
89 static uint32_t dma_n_spikes;
90 
92 static uint32_t dma_complete_count;
93 
95 static uint32_t spike_processing_count;
96 
98 static uint32_t n_successful_rewires;
99 
103 
106 
110 
112 static struct {
113  uint32_t time;
114  uint32_t packets_this_time_step;
116 
118 static uint32_t p_per_ts_region;
119 
120 /* PRIVATE FUNCTIONS - static for inlining */
121 
125 static inline void do_dma_read(
126  spike_t spike, pop_table_lookup_result_t *result) {
127  // Write the SDRAM address of the plastic region and the
128  // Key of the originating spike to the beginning of DMA buffer
129  dma_buffer *next_buffer = &dma_buffers[next_buffer_to_fill];
130  next_buffer->sdram_writeback_address = result->row_address;
131  next_buffer->originating_spike = spike;
132  next_buffer->n_bytes_transferred = result->n_bytes_to_transfer;
133  next_buffer->colour = result->colour;
134  next_buffer->colour_mask = result->colour_mask;
135 
136  // Start a DMA transfer to fetch this synaptic row into current
137  // buffer
139  while (!spin1_dma_transfer(
140  DMA_TAG_READ_SYNAPTIC_ROW, result->row_address, next_buffer->row, DMA_READ,
141  result->n_bytes_to_transfer)) {
142  // Do Nothing
143  }
145 }
146 
153 static inline bool is_something_to_do(
154  spike_t *spike, pop_table_lookup_result_t *result, uint32_t *n_rewire,
155  uint32_t *n_process_spike) {
156  // Disable interrupts here as dma_busy modification is a critical section
157  uint cpsr = spin1_int_disable();
158 
159  // Check for synaptic rewiring
160  while (rewires_to_do) {
161  rewires_to_do--;
162  spin1_mode_restore(cpsr);
163  if (synaptogenesis_dynamics_rewire(time, spike, result)) {
164  *n_rewire += 1;
165  return true;
166  }
167  cpsr = spin1_int_disable();
168  }
169 
170  // Is there another address in the population table?
171  spin1_mode_restore(cpsr);
172  if (population_table_get_next_address(spike, result)) {
173  *n_process_spike += 1;
174  return true;
175  }
176  cpsr = spin1_int_disable();
177 
178  // track for provenance
179  uint32_t input_buffer_filled_size = in_spikes_size();
180  if (biggest_fill_size_of_input_buffer < input_buffer_filled_size) {
181  biggest_fill_size_of_input_buffer = input_buffer_filled_size;
182  }
183 
184  // Are there any more spikes to process?
185  while (in_spikes_get_next_spike(spike)) {
186  // Enable interrupts while looking up in the master pop table,
187  // as this can be slow
188  spin1_mode_restore(cpsr);
189  if (population_table_get_first_address(*spike, result)) {
191  *n_process_spike += 1;
192  return true;
193  }
194 
195  // Disable interrupts before checking if there is another spike
196  cpsr = spin1_int_disable();
197  }
198 
199  // If nothing to do, the DMA is not busy
200  dma_busy = false;
201 
202  // Restore interrupts
203  spin1_mode_restore(cpsr);
204  return false;
205 }
206 
220 static bool setup_synaptic_dma_read(dma_buffer *current_buffer,
221  uint32_t *n_rewires, uint32_t *n_synapse_processes) {
222  // Set up to store the DMA location and size to read
223  spike_t spike;
225  dma_n_spikes = 0;
226  dma_n_rewires = 0;
227 
228  // Keep looking if there is something to do until a DMA can be done
229  bool setup_done = false;
230  while (!setup_done && is_something_to_do(
231  &spike, &result, &dma_n_rewires, &dma_n_spikes)) {
232  if (current_buffer != NULL &&
233  current_buffer->sdram_writeback_address == result.row_address) {
234  // If we can reuse the row, add on what we can use it for
235  // Note that only one of these will have a value of 1 with the
236  // other being set to 0, but we add both as it is simple
237  *n_rewires += dma_n_rewires;
238  *n_synapse_processes += dma_n_spikes;
239  dma_n_rewires = 0;
240  dma_n_spikes = 0;
241  // Although we can reuse the buffer, the colour might be different
242  current_buffer->colour = result.colour;
243  } else {
244  // If the row is in SDRAM, set up the transfer and we are done
245  do_dma_read(spike, &result);
246  setup_done = true;
247  }
248 
249  // needs to be here to ensure that its only recording actual spike
250  // processing and not the surplus DMA requests.
252  }
253  return setup_done;
254 }
255 
260 static inline void setup_synaptic_dma_write(
261  uint32_t dma_buffer_index, bool plastic_only) {
262  // Get pointer to current buffer
263  dma_buffer *buffer = &dma_buffers[dma_buffer_index];
264 
265  // Get the number of plastic bytes and the write back address from the
266  // synaptic row
267  size_t write_size = buffer->n_bytes_transferred;
268  void *sdram_start_address = buffer->sdram_writeback_address;
269  void *dtcm_start_address = buffer->row;
270  if (plastic_only) {
271  write_size = synapse_row_plastic_size(buffer->row) * sizeof(uint32_t);
272  sdram_start_address = synapse_row_plastic_region(
273  buffer->sdram_writeback_address);
274  dtcm_start_address = synapse_row_plastic_region(buffer->row);
275  }
276 
277  // Start transfer
278  while (!spin1_dma_transfer(DMA_TAG_WRITE_PLASTIC_REGION, sdram_start_address,
279  dtcm_start_address, DMA_WRITE, write_size)) {
280  // Do Nothing
281  }
282 }
283 
285 static inline void start_dma_loop(void) {
286  // If we're not already processing synaptic DMAs,
287  // flag pipeline as busy and trigger a feed event
288  // NOTE: locking is not used here because this is assumed to be FIQ
289  if (!dma_busy) {
290  // Only set busy if successful.
291  // NOTE: Counts when unsuccessful are handled by the API
292  if (spin1_trigger_user_event(0, 0)) {
293  dma_busy = true;
294  }
295  }
296 }
297 
301 static void multicast_packet_received_callback(uint key, UNUSED uint unused) {
302  p_per_ts_struct.packets_this_time_step += 1;
303  if (in_spikes_add_spike(key)) {
304  start_dma_loop();
305  }
306 }
307 
311 static void multicast_packet_pl_received_callback(uint key, uint payload) {
312  p_per_ts_struct.packets_this_time_step += 1;
313 
314  // cycle through the packet insertion
315  bool added = false;
316  for (uint count = payload; count > 0; count--) {
317  added = in_spikes_add_spike(key);
318  }
319  if (added) {
320  start_dma_loop();
321  }
322 }
323 
327 static void dma_complete_callback(UNUSED uint unused, UNUSED uint tag) {
328 
329  // increment the dma complete count for provenance generation
331 
332  // Get pointer to current buffer
333  uint32_t current_buffer_index = buffer_being_read;
334  dma_buffer *current_buffer = &dma_buffers[current_buffer_index];
335 
336  // Start the next DMA transfer and get a count of the rewires and spikes
337  // that can be done on this row now (there might be more while the DMA
338  // was in progress). Note that either dma_n_rewires or dma_n_spikes is set
339  // to 1 here, with the other being 0. We take a copy of the count and this
340  // is the value added to for this processing, as setup_synaptic_dma will
341  // count repeats of the current spike
342  uint32_t n_rewires = dma_n_rewires;
343  uint32_t n_spikes = dma_n_spikes;
344  setup_synaptic_dma_read(current_buffer, &n_rewires, &n_spikes);
345 
346  // Assume no write back but assume any write back is plastic only
347  bool write_back = false;
348  bool plastic_only = true;
349 
350  // If rewiring, do rewiring first
351  for (uint32_t i = n_rewires; i > 0; i--) {
352  if (synaptogenesis_row_restructure(time, current_buffer->row)) {
353  write_back = true;
354  plastic_only = false;
356  }
357  }
358 
359  // Process synaptic row repeatedly for any upcoming spikes
360  while (n_spikes > 0) {
361 
362  // Process synaptic row, writing it back if it's the last time
363  // it's going to be processed
364  bool write_back_now = false;
366  time, current_buffer->colour, current_buffer->colour_mask,
367  current_buffer->row, &write_back_now)) {
368  log_error(
369  "Error processing spike 0x%.8x for address 0x%.8x"
370  " (local=0x%.8x)",
371  current_buffer->originating_spike,
372  current_buffer->sdram_writeback_address,
373  current_buffer->row);
374 
375  // Print out the row for debugging
376  address_t row = (address_t) current_buffer->row;
377  for (uint32_t i = 0;
378  i < (current_buffer->n_bytes_transferred >> 2); i++) {
379  log_error("%u: 0x%08x", i, row[i]);
380  }
381  rt_error(RTE_SWERR);
382  }
383 
384  write_back |= write_back_now;
385  n_spikes--;
386  }
387 
388  if (write_back) {
389  setup_synaptic_dma_write(current_buffer_index, plastic_only);
390  }
391 }
392 
396 void user_event_callback(UNUSED uint unused0, UNUSED uint unused1) {
397  // Reset the counters as this is a new process
398  dma_n_rewires = 0;
399  dma_n_spikes = 0;
400 
402  // If the DMA buffer is full of valid data, attempt to reuse it on the
403  // next data to be used, as this might be able to make use of the buffer
404  // without transferring data
406  } else {
407  // If the DMA buffer is invalid, just do the first transfer possible
408  setup_synaptic_dma_read(NULL, NULL, NULL);
409  }
410 }
411 
412 /* INTERFACE FUNCTIONS - cannot be static */
414  uint32_t n_spikes = in_spikes_size();
416  spin1_dma_flush();
417  in_spikes_clear();
418  dma_busy = false;
419  }
420 
421  // Record the number of packets received last timer tick
422  p_per_ts_struct.time = time;
423  recording_record(p_per_ts_region, &p_per_ts_struct, sizeof(p_per_ts_struct));
424  p_per_ts_struct.packets_this_time_step = 0;
425 
426  // Record the count whether clearing or not for provenance
428 }
429 
431  size_t row_max_n_words, uint mc_packet_callback_priority,
432  uint user_event_priority, uint incoming_spike_buffer_size,
433  bool clear_input_buffers_of_late_packets_init,
434  uint32_t packets_per_timestep_region) {
435  // Allocate the DMA buffers
436  for (uint32_t i = 0; i < N_DMA_BUFFERS; i++) {
437  dma_buffers[i].row = spin1_malloc(row_max_n_words * sizeof(uint32_t));
438  if (dma_buffers[i].row == NULL) {
439  log_error("Could not initialise DMA buffers with %u words", row_max_n_words);
440  return false;
441  }
442  }
443  dma_busy = false;
445  clear_input_buffers_of_late_packets_init;
448  p_per_ts_region = packets_per_timestep_region;
449 
450  // Allocate incoming spike buffer
451  if (!in_spikes_initialize_spike_buffer(incoming_spike_buffer_size)) {
452  return false;
453  }
454 
455  // Set up the callbacks
456  spin1_callback_on(MC_PACKET_RECEIVED,
457  multicast_packet_received_callback, mc_packet_callback_priority);
458  spin1_callback_on(MCPL_PACKET_RECEIVED,
459  multicast_packet_pl_received_callback, mc_packet_callback_priority);
460  simulation_dma_transfer_done_callback_on(
462  spin1_callback_on(USER_EVENT, user_event_callback, user_event_priority);
463 
464  return true;
465 }
466 
474 }
475 
476 bool spike_processing_do_rewiring(int number_of_rewires) {
477  // disable interrupts
478  uint cpsr = spin1_int_disable();
479  rewires_to_do += number_of_rewires;
480  start_dma_loop();
481  // enable interrupts
482  spin1_mode_restore(cpsr);
483  return true;
484 }
static uint32_t key
Base multicast key for sending messages.
Functions for immediate handling of incoming spikes.
static bool in_spikes_get_next_spike(spike_t *spike)
Retrieves a spike from the input spike buffer.
Definition: in_spikes.h:66
static circular_buffer buffer
Buffer for quickly taking spikes received by a fast interrupt and queueing them for later processing ...
Definition: in_spikes.h:28
static bool in_spikes_initialize_spike_buffer(uint32_t size)
This function initialises the input spike buffer.
Definition: in_spikes.h:51
static void in_spikes_clear(void)
clears the input spike buffer.
Definition: in_spikes.h:126
static uint32_t in_spikes_size(void)
get the size of the input spike buffer
Definition: in_spikes.h:121
static counter_t in_spikes_get_n_buffer_overflows(void)
Get the number of times that the input spike buffer overflowed.
Definition: in_spikes.h:80
static bool in_spikes_add_spike(spike_t spike)
Adds a spike to the input spike buffer.
Definition: in_spikes.h:59
uint32_t n_spikes[2]
Spike buffer counters.
struct synaptic_row * synaptic_row_t
The type of a synaptic row.
uint32_t spike_t
The type of a spike.
Master pop(ulation) table API.
bool population_table_get_next_address(spike_t *spike, pop_table_lookup_result_t *result)
Get the next row data for a previously given spike. If no spike has been given, return False.
bool population_table_get_first_address(spike_t spike, pop_table_lookup_result_t *result)
Get the first row data for the given input spike.
A structure to hold a response to a population table lookup.
#define N_DMA_BUFFERS
The number of DMA Buffers to use.
static void start_dma_loop(void)
Start the DMA processing loop if not already running.
void user_event_callback(uint unused0, uint unused1)
Called when a user event is received.
static uint32_t spike_processing_count
the number of spikes that were processed (used in provenance generation)
bool spike_processing_initialise(size_t row_max_n_words, uint mc_packet_callback_priority, uint user_event_priority, uint incoming_spike_buffer_size, bool clear_input_buffers_of_late_packets_init, uint32_t packets_per_timestep_region)
Initialise the spike processing system.
static bool setup_synaptic_dma_read(dma_buffer *current_buffer, uint32_t *n_rewires, uint32_t *n_synapse_processes)
Set up a new synaptic DMA read.
static uint32_t dma_n_spikes
The number of spikes to do when the DMA completes.
static void dma_complete_callback(uint unused, uint tag)
Called when a DMA completes.
bool spike_processing_do_rewiring(int number_of_rewires)
Set the number of times spike_processing has to attempt rewiring.
static uint32_t n_successful_rewires
The number of successful rewires.
static uint32_t next_buffer_to_fill
The index of the next buffer to be filled by a DMA.
static uint32_t biggest_fill_size_of_input_buffer
tracker of how full the input buffer got.
static uint32_t buffer_being_read
The index of the buffer currently being filled by a DMA read.
static volatile uint32_t rewires_to_do
Number of outstanding synaptogenic rewires.
static void multicast_packet_received_callback(uint key, uint unused)
Called when a multicast packet is received.
static bool clear_input_buffers_of_late_packets
Whether if we should clear packets from the input buffer at the end of a timer tick.
static dma_buffer dma_buffers[N_DMA_BUFFERS]
The DTCM buffers for the synapse rows.
static volatile bool dma_busy
True if the DMA "loop" is currently running.
static void do_dma_read(spike_t spike, pop_table_lookup_result_t *result)
Perform a DMA read of a synaptic row.
static void multicast_packet_pl_received_callback(uint key, uint payload)
Called when a multicast packet is received.
static uint32_t p_per_ts_region
the region to record the packets per timestep in
void spike_processing_clear_input_buffer(timer_t time)
clears the input buffer of packets
static bool is_something_to_do(spike_t *spike, pop_table_lookup_result_t *result, uint32_t *n_rewire, uint32_t *n_process_spike)
Check if there is anything to do. If not, DMA is not busy.
void spike_processing_store_provenance(struct spike_processing_provenance *prov)
Get provenance data for Spike processing.
spike_processing_dma_tags
DMA tags.
@ DMA_TAG_READ_SYNAPTIC_ROW
Tag of a DMA read of a full synaptic row.
@ DMA_TAG_WRITE_PLASTIC_REGION
Tag of a DMA write of the plastic region of a synaptic row.
static void setup_synaptic_dma_write(uint32_t dma_buffer_index, bool plastic_only)
Set up a DMA write of synaptic data.
static uint32_t dma_complete_count
the number of DMA completes (used in provenance generation)
uint32_t time
The current timer tick value.
Definition: c_main.c:94
static struct @13 p_per_ts_struct
the number of packets received this time step
static uint32_t count_input_buffer_packets_late
How many packets were lost from the input buffer because of late arrival.
static uint32_t dma_n_rewires
The number of rewires to do when the DMA completes.
Spike processing API.
uint32_t n_spikes_processed
The number of spikes received and processed.
uint32_t n_input_buffer_overflows
A count of the times that the synaptic input circular buffers overflowed.
uint32_t n_rewires
The number of rewires performed.
uint32_t max_filled_input_buffer_size
The maximum size of the input buffer.
uint32_t n_packets_dropped_from_lateness
The number of packets that were cleared at the end of timesteps.
uint32_t n_dmas_complete
The number of DMAs performed.
Provenance for spike processing.
spike_t originating_spike
Key of originating spike.
synaptic_row_t row
Row data.
uint32_t n_bytes_transferred
Number of bytes transferred in the read.
synaptic_row_t sdram_writeback_address
Address in SDRAM to write back plastic region to.
uint32_t colour
Spike colour.
uint32_t colour_mask
Spike colour mask.
implementation for handling the processing of synapse rows.
static synapse_row_plastic_data_t * synapse_row_plastic_region(synaptic_row_t row)
Get the address of the plastic region.
Definition: synapse_row.h:138
static size_t synapse_row_plastic_size(const synaptic_row_t row)
Get the size of the plastic region.
Definition: synapse_row.h:129
bool synapses_process_synaptic_row(uint32_t time, uint32_t spike_colour, uint32_t colour_mask, synaptic_row_t row, bool *write_back)
process a synaptic row
Definition: synapses.c:351
Operations on synapses.
This file contains the main interface for structural plasticity.
bool synaptogenesis_dynamics_rewire(uint32_t time, spike_t *spike, pop_table_lookup_result_t *result)
Trigger the process of synaptic rewiring.
void synaptogenesis_spike_received(uint32_t time, spike_t spike)
Indicates that a spike has been received.
bool synaptogenesis_row_restructure(uint32_t time, synaptic_row_t row)
Perform the actual restructuring of a row.