sPyNNaker neural_modelling  7.4.2
neuron_recording.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 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 
20 #ifndef _NEURON_RECORDING_H_
21 #define _NEURON_RECORDING_H_
22 
23 #include <common/neuron-typedefs.h>
24 #include <bit_field.h>
25 #include <recording.h>
26 #include <wfi.h>
27 #include <stddef.h>
28 
30 // Note data is just bytes here but actual type is used on writing
31 typedef struct recording_values_t {
32  uint32_t time;
33  uint8_t data[];
35 
37 typedef struct bitfield_values_t {
38  uint32_t time;
39  uint32_t bits[];
41 
43 typedef struct recording_info_t {
44  uint32_t element_size;
45  uint32_t rate;
46  uint32_t count;
47  uint32_t increment;
48  uint32_t size;
49  recording_values_t *values;
51 
53 typedef struct bitfield_info_t {
54  uint32_t rate;
55  uint32_t count;
56  uint32_t increment;
57  uint32_t size;
58  uint32_t n_words;
59  bitfield_values_t *values;
61 
63 static uint16_t **neuron_recording_indexes;
64 
66 static uint16_t **bitfield_recording_indexes;
67 
70 
73 
75 static uint8_t **recording_values;
76 
78 static uint32_t **bitfield_values;
79 
81 static volatile uint32_t n_recordings_outstanding = 0;
82 
84 static void *reset_address;
85 
87 #define FLOOR_TO_2 0xFFFFFFFE
88 
90 #define CEIL_TO_2 1
91 
97 static inline void neuron_recording_record_value(
98  uint32_t var_index, uint32_t neuron_index, void *value) {
99  uint32_t index = neuron_recording_indexes[var_index][neuron_index];
100  uint32_t size = recording_info[var_index].element_size;
101  uint32_t p = size * index;
102  spin1_memcpy(&recording_values[var_index][p], value, size);
103 }
104 
110 static inline void neuron_recording_record_accum(
111  uint32_t var_index, uint32_t neuron_index, accum value) {
112  uint16_t index = neuron_recording_indexes[var_index][neuron_index];
113  accum *data = (accum *) recording_values[var_index];
114  data[index] = value;
115 }
116 
123  uint32_t var_index, uint32_t neuron_index, double value) {
124  uint16_t index = neuron_recording_indexes[var_index][neuron_index];
125  double *data = (double *) recording_values[var_index];
126  data[index] = value;
127 }
128 
134 static inline void neuron_recording_record_float(
135  uint32_t var_index, uint32_t neuron_index, float value) {
136  uint16_t index = neuron_recording_indexes[var_index][neuron_index];
137  float *data = (float *) recording_values[var_index];
138  data[index] = value;
139 }
140 
146 static inline void neuron_recording_record_int32(
147  uint32_t var_index, uint32_t neuron_index, int32_t value) {
148  uint16_t index = neuron_recording_indexes[var_index][neuron_index];
149  int32_t *data = (int32_t *) recording_values[var_index];
150  data[index] = value;
151 }
152 
157 static inline void neuron_recording_record_bit(
158  uint32_t var_index, uint32_t neuron_index) {
159  // Record the bit
160  uint32_t index = bitfield_recording_indexes[var_index][neuron_index];
161  bit_field_set(bitfield_values[var_index], index);
162 }
163 
166 static inline void neuron_recording_record(uint32_t time) {
167  // go through all recordings
168 
169  for (uint32_t i = N_RECORDED_VARS; i > 0; i--) {
170  recording_info_t *rec_info = &recording_info[i - 1];
171  // if the rate says record, record now
172  if (rec_info->count == rec_info->rate) {
173  // Reset the count
174  rec_info->count = 1;
175  // Set the time and record the data
176  rec_info->values->time = time;
177  recording_record(i - 1, rec_info->values, rec_info->size);
178  } else {
179 
180  // Not recording this time, so increment by specified amount
181  rec_info->count += rec_info->increment;
182  }
183  }
184 
185  for (uint32_t i = N_BITFIELD_VARS; i > 0; i--) {
186  bitfield_info_t *bf_info = &bitfield_info[i - 1];
187  // if the rate says record, record now
188  if (bf_info->count == bf_info->rate) {
189  // Reset the count
190  bf_info->count = 1;
191  // Skip empty bitfields
192  if (empty_bit_field(bf_info->values->bits, bf_info->n_words)) {
193  continue;
194  }
195  // Set the time and record the data (note index is after recorded_vars)
196  bf_info->values->time = time;
197  recording_record(i + N_RECORDED_VARS - 1, bf_info->values, bf_info->size);
198  } else {
199 
200  // Not recording this time, so increment by specified amount
201  bf_info->count += bf_info->increment;
202  }
203  }
204 }
205 
208  // Reset the bitfields before starting if a beginning of recording
209  for (uint32_t i = N_BITFIELD_VARS; i > 0; i--) {
210  bitfield_info_t *b_info = &bitfield_info[i - 1];
211  if (b_info->count == 1) {
212  clear_bit_field(b_info->values->bits, b_info->n_words);
213  }
214  }
215 }
216 
218 static void reset_record_counter(void) {
219  for (uint32_t i = 0; i < N_RECORDED_VARS; i++) {
220  if (recording_info[i].rate == 0) {
221  // Setting increment to zero means count will never equal rate
222  recording_info[i].increment = 0;
223 
224  // Count is not rate so does not record, but not 1 so it does not reset!
225  recording_info[i].count = 2;
226  } else {
227  // Increase one each call so count gets to rate
228  recording_info[i].increment = 1;
229 
230  // Using rate here so that the zero time is recorded
231  recording_info[i].count = recording_info[i].rate;
232  }
233  }
234 
235  // clear the bitfields
236  for (uint32_t i = 0; i < N_BITFIELD_VARS; i++) {
237  if (bitfield_info[i].rate == 0) {
238  // Setting increment to zero means count will never equal rate
239  bitfield_info[i].increment = 0;
240 
241  // Count is not rate so does not record, but not 1 so it does not reset!
242  bitfield_info[i].count = 2;
243  } else {
244  // Increase one each call so count gets to rate
245  bitfield_info[i].increment = 1;
246 
247  // Using rate here so that the zero time is recorded
248  bitfield_info[i].count = bitfield_info[i].rate;
249  clear_bit_field(bitfield_info[i].values->bits,
250  bitfield_info[i].n_words);
251  }
252  }
253 }
254 
258 static inline uint32_t bitfield_data_size(uint32_t n_neurons) {
259  return sizeof(bitfield_values_t) + (get_bit_field_size(n_neurons) * sizeof(uint32_t));
260 }
261 
267  void *recording_address, uint32_t n_neurons) {
268  // Round up the number of bytes to align at a word boundary i.e. round to
269  // the next multiple of 2
270  uint32_t ceil_n_entries = (n_neurons + CEIL_TO_2) & FLOOR_TO_2;
271 
272 
273  // GCC lets you define a struct like this!
274  typedef struct neuron_recording_data {
275  uint32_t rate;
276  uint32_t n_neurons_recording;
277  uint32_t element_size;
278  uint16_t indices[ceil_n_entries];
279  } neuron_recording_data_t;
280 
281  neuron_recording_data_t *data = recording_address;
282 
283  for (uint32_t i = 0; i < N_RECORDED_VARS; i++) {
284  recording_info[i].rate = data[i].rate;
285  uint32_t n_neurons_rec = data[i].n_neurons_recording;
286  recording_info[i].element_size = data[i].element_size;
287  recording_info[i].size = sizeof(recording_values_t)
288  + (n_neurons_rec * recording_info[i].element_size);
289  // There is an extra "neuron" in the data used when one of the neurons
290  // is *not* recording, to avoid a check
291  uint32_t alloc_size = recording_info[i].size +
292  recording_info[i].element_size;
293 
294  // allocate memory for the recording
295  if (recording_info[i].values == NULL) {
296  recording_info[i].values = spin1_malloc(alloc_size);
297  if (recording_info[i].values == NULL) {
298  log_error("couldn't allocate recording data space %u for %d",
299  alloc_size, i);
300  return false;
301  }
302  recording_values[i] = recording_info[i].values->data;
303  }
304 
305  // copy over the indexes
306  spin1_memcpy(neuron_recording_indexes[i], data[i].indices,
307  n_neurons * sizeof(uint16_t));
308  }
309 
310  typedef struct bitfield_recording_data {
311  uint32_t rate;
312  uint32_t n_neurons_recording;
313  uint16_t indices[ceil_n_entries];
314  } bitfield_recording_data_t;
315 
316  bitfield_recording_data_t *bitfield_data =
317  (bitfield_recording_data_t *) &data[N_RECORDED_VARS];
318 
319  for (uint32_t i = 0; i < N_BITFIELD_VARS; i++) {
320  bitfield_info[i].rate = bitfield_data[i].rate;
321  uint32_t n_neurons_rec = bitfield_data[i].n_neurons_recording;
322  bitfield_info[i].size = bitfield_data_size(n_neurons_rec);
323  // There is an extra "neuron" in the data used when one of the neurons
324  // is *not* recording, to avoid a check
325  uint32_t alloc_size = bitfield_data_size(n_neurons_rec + 1);
326 
327  // allocate memory for the recording
328  if (bitfield_info[i].values == NULL) {
329  bitfield_info[i].values = spin1_malloc(alloc_size);
330  if (bitfield_info[i].values == NULL) {
331  log_error("couldn't allocate bitfield recording data space for %d", i);
332  return false;
333  }
334  // There is an extra "neuron" in the data used when one of the
335  // neurons is *not* recording, to avoid a check
336  bitfield_info[i].n_words = get_bit_field_size(n_neurons_rec + 1);
337  bitfield_values[i] = bitfield_info[i].values->bits;
338  }
339 
340  // copy over the indexes
341  spin1_memcpy(bitfield_recording_indexes[i], bitfield_data[i].indices,
342  n_neurons * sizeof(uint16_t));
343  }
344  return true;
345 }
346 
352  log_error("failed to reread in the new elements after reset");
353  return false;
354  }
355  return true;
356 }
357 
361 static inline bool allocate_word_dtcm(uint32_t n_neurons) {
362  recording_info = spin1_malloc(N_RECORDED_VARS * sizeof(recording_info_t));
363  if (recording_info == NULL) {
364  log_error("Could not allocated space for recording_info");
365  return false;
366  }
367 
368  // allocate dtcm for the overall holder for indexes
370  spin1_malloc(N_RECORDED_VARS * sizeof(uint16_t *));
371  if (neuron_recording_indexes == NULL) {
372  log_error("Could not allocate space for var_recording_indexes");
373  return false;
374  }
375 
376  recording_values = spin1_malloc(N_RECORDED_VARS * sizeof(uint8_t *));
377  if (recording_values == NULL) {
378  log_error("Could not allocate space for recording_values");
379  return false;
380  }
381 
382  for (uint32_t i = 0; i < N_RECORDED_VARS; i++) {
383  // clear recorded values pointer
384  recording_info[i].values = NULL;
385 
386  // allocate dtcm for indexes for each recording region
387  neuron_recording_indexes[i] = spin1_malloc(n_neurons * sizeof(uint16_t));
388  if (neuron_recording_indexes[i] == NULL) {
389  log_error("failed to allocate memory for recording index %d", i);
390  return false;
391  }
392  }
393 
394  // successfully allocated all DTCM.
395  return true;
396 }
397 
401 static inline bool allocate_bitfield_dtcm(uint32_t n_neurons) {
402  bitfield_info = spin1_malloc(N_BITFIELD_VARS * sizeof(bitfield_info_t));
403  if (bitfield_info == NULL) {
404  log_error("Failed to allocate space for bitfield_info");
405  return false;
406  }
407 
408  // allocate dtcm for the overall holder for indexes
410  spin1_malloc(N_BITFIELD_VARS * sizeof(uint16_t *));
411  if (bitfield_recording_indexes == NULL) {
412  log_error("Could not allocate space for bitfield_recording_indexes");
413  return false;
414  }
415 
416  bitfield_values = spin1_malloc(N_BITFIELD_VARS * sizeof(uint32_t *));
417  if (bitfield_values == NULL) {
418  log_error("Could not allocate space for bitfield_values");
419  return false;
420  }
421 
422  for (uint32_t i = 0; i < N_BITFIELD_VARS; i++) {
423  // clear recorded values pointer
424  bitfield_info[i].values = NULL;
425 
426  // allocate dtcm for indexes for each recording region
428  spin1_malloc(n_neurons * sizeof(uint16_t));
429  if (bitfield_recording_indexes[i] == NULL) {
430  log_error("failed to allocate memory for bitfield index %d", i);
431  return false;
432  }
433  }
434 
435  // successfully allocated all DTCM.
436  return true;
437 }
438 
440 typedef struct neuron_recording_header {
442  uint32_t n_recorded_vars;
444  uint32_t n_bitfield_vars;
446 
454  void *recording_address, uint32_t n_neurons,
455  uint32_t *n_rec_regions_used) {
456  // boot up the basic recording
457  void *data_addr = recording_address;
458 
459  // Verify the number of recording and bitfield elements
460  neuron_recording_header_t *header = data_addr;
461  if (header->n_recorded_vars != N_RECORDED_VARS) {
462  log_error("Data spec number of recording variables %d != "
463  "neuron implementation number of recorded variables %d",
464  header->n_recorded_vars, N_RECORDED_VARS);
465  return false;
466  }
467  if (header->n_bitfield_vars != N_BITFIELD_VARS) {
468  log_error("Data spec number of bitfield variables %d != "
469  "neuron implementation number of bitfield variables %d",
470  header->n_bitfield_vars, N_BITFIELD_VARS);
471  return false;
472  }
473  // Copy the number of regions used
474  *n_rec_regions_used = header->n_recorded_vars + header->n_bitfield_vars;
475  data_addr = &header[1];
476 
478  log_error("failed to allocate DTCM for the neuron recording structs.");
479  return false;
480  }
482  log_error("failed to allocate DTCM for the bitfield recording structs");
483  return false;
484  }
485 
486  // read in the sdram params into the allocated data objects
487  reset_address = data_addr;
488  if (!neuron_recording_read_in_elements(data_addr, n_neurons)) {
489  log_error("failed to read in the elements");
490  return false;
491  }
492 
493  // reset stuff
495 
496  return true;
497 }
498 
499 #endif //_NEURON_RECORDING_H_
static uint32_t time
Simulation time.
Data type definitions for SpiNNaker Neuron-modelling.
static uint32_t n_neurons
The number of neurons on the core.
Definition: neuron.c:45
static void neuron_recording_record_float(uint32_t var_index, uint32_t neuron_index, float value)
stores a recording of a float variable only; this is faster than neuron_recording_record_value for th...
static bitfield_info_t * bitfield_info
An array of bitfield information structures.
#define FLOOR_TO_2
When bitwise anded with a number will floor to the nearest multiple of 2.
static void neuron_recording_record_value(uint32_t var_index, uint32_t neuron_index, void *value)
stores a recording of a value of any type, except bitfield; use the functions below for common types ...
static void reset_record_counter(void)
resets all states back to start state.
static void neuron_recording_record_accum(uint32_t var_index, uint32_t neuron_index, accum value)
stores a recording of an accum variable only; this is faster than neuron_recording_record_value for t...
static volatile uint32_t n_recordings_outstanding
The number of recordings outstanding.
bool neuron_recording_reset(uint32_t n_neurons)
reads recording data from sdram on reset.
static void neuron_recording_setup_for_next_recording(void)
sets up state for next recording.
static uint16_t ** neuron_recording_indexes
The index to record each variable to for each neuron.
static bool allocate_word_dtcm(uint32_t n_neurons)
handles all the DTCM allocations for recording words
static uint16_t ** bitfield_recording_indexes
The index to record each bitfield variable to for each neuron.
static void neuron_recording_record(uint32_t time)
does the recording process of handing over to basic recording
static recording_info_t * recording_info
An array of recording information structures.
#define CEIL_TO_2
Add to a number before applying floor to 2 to turn it into a ceil operation.
static bool neuron_recording_read_in_elements(void *recording_address, uint32_t n_neurons)
reads recording data from SDRAM
static void neuron_recording_record_double(uint32_t var_index, uint32_t neuron_index, double value)
stores a recording of a double variable only; this is faster than neuron_recording_record_value for t...
static uint32_t ** bitfield_values
An array of spaces into which bitfields can be written.
uint32_t n_recorded_vars
The number of word-sized variables to record.
static uint8_t ** recording_values
An array of spaces into which recording values can be written.
static void * reset_address
The address of the recording region to read on reset.
uint32_t n_bitfield_vars
The number of bitfield variables to record.
bool neuron_recording_initialise(void *recording_address, uint32_t n_neurons, uint32_t *n_rec_regions_used)
sets up the recording stuff
static uint32_t bitfield_data_size(uint32_t n_neurons)
the number of bytes used in bitfield recording for n_neurons
static void neuron_recording_record_bit(uint32_t var_index, uint32_t neuron_index)
stores a recording of a set bit; this is the only way to set a bit in a bitfield; neuron_recording_re...
static bool allocate_bitfield_dtcm(uint32_t n_neurons)
handles all the DTCM allocations for recording bitfields
static void neuron_recording_record_int32(uint32_t var_index, uint32_t neuron_index, int32_t value)
stores a recording of an int32_t variable only; this is faster than neuron_recording_record_value for...
A struct for information on a bitfield recording.
A struct for bitfield data.
The heading of the neuron recording region.
A struct for information for a non-bitfield recording.
A struct of the different types of recorded data.