sPyNNaker neural_modelling  development
neuron_expander.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 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 
23 #include "param_generator.h"
24 #include "rng.h"
25 #include "type_writers.h"
26 
27 #include <spin1_api.h>
28 #include <data_specification.h>
29 #include <debug.h>
30 #include "common_mem.h"
31 
32 #define REPEAT_PER_NEURON 0xFFFFFFFF
33 
34 #define REPEAT_PER_NEURON_RECORDED 0x7FFFFFFF
35 
36 // Mask to work out MOD 4
37 #define MOD_4 0x3
38 
40 #define FLOOR_TO_2 0xFFFFFFFE
41 
43 #define CEIL_TO_2 1
44 
45 // An array of how much to add to align data to 4-bytes
46 // indexed by [current offset % 4][size to write % 4].
47 // Note sizes are expected to be 1, 2, 4 or 8 (indices 1, 2, 0, 0)
48 // so other values are 0 (but indexing an array is quicker).
49 static const uint32_t ADD[4][3] = {
50  {0, 0, 0}, // Offset 0 - anything goes
51  {3, 0, 1}, // Offset 1 - needs shift for 2, 4 and 8 (indices 2, 0, 0)
52  {2, 0, 0}, // Offset 2 - needs shift for 4 and 8 (indices 0, 0)
53  {1, 0, 1} // Offset 3 - needs shift for 2, 4 and 8 (indices 2, 0, 0)
54 };
55 
56 static inline uint32_t align_offset(uint32_t offset, uint32_t size) {
57  if (size == 0) {
58  log_error("Size of 0!");
59  rt_error(RTE_SWERR);
60  }
61  uint32_t size_mod = size & MOD_4;
62  if (size_mod == 3) {
63  log_error("Size %u unsupported!", size);
64  }
65  return ADD[offset & MOD_4][size & MOD_4];
66 }
67 
68 typedef struct neuron_param_item {
69  // The number of repeat calls to make of the generator
70  uint32_t n_repeats;
71  // The generator to use
72  uint32_t generator;
74 
75 typedef struct neuron_param {
76  // The type of the parameter
77  type param_type;
78  // The number of "items" which are groups of values to be repeated
79  uint32_t n_items;
80  // The "items" to generate
81  neuron_param_item_t item[];
83 
85 typedef struct neuron_params_struct {
86  // How many bytes are needed for an aligned copy of the struct
87  uint32_t bytes_per_repeat;
88  // How many repeats will be made in total
89  uint32_t n_repeats_total;
90  // How many words in this struct including variable size data
91  uint32_t struct_size_bytes;
92  // How many parameters in the struct
93  uint32_t n_params;
94  // Each of the groups of parameters to generate
95  neuron_param_t param[];
96  // Following on from this are the data of each param_generator in order
97  // of appearance in the above
99 
100 typedef struct sdram_variable_recording_data {
101  uint32_t rate;
102  uint32_t n_recording;
103  uint32_t element_size;
104  uint16_t indices[];
106 
107 typedef struct sdram_bitfield_recording_data {
108  uint32_t rate;
109  uint32_t n_recording;
110  uint16_t indices[];
112 
113 typedef struct recording_index {
114  uint32_t n_repeats:31;
115  uint32_t is_recording:1;
117 
118 typedef struct variable_recording {
119  uint32_t rate;
120  uint32_t element_size;
121  uint32_t n_recording;
122  uint32_t n_index_items;
123  recording_index_t index_items[];
125 
126 typedef struct bitfield_recording {
127  uint32_t rate;
128  uint32_t n_recording;
129  uint32_t n_index_items;
130  recording_index_t index_items[];
132 
133 typedef struct recording_params {
134  // How many variables can be recorded
135  uint32_t n_recordable_variables;
136  // How many bit fields can be recorded
137  uint32_t n_recordable_bit_fields;
139 
141 __attribute__((aligned(4)))
142 typedef struct expander_config {
143  uint32_t neuron_params_region;
144  uint32_t neuron_recording_region;
146  rng_t core_rng;
147  uint32_t n_structs;
148  uint32_t n_neurons;
149 } expander_config_t;
150 
153 
170  void **neuron_params_region, uint32_t n_neurons) {
171 
172  // Get the config for the repeated struct and move on to the data after
174  uint8_t *reg = *region;
175  *region = &(reg[config->struct_size_bytes]);
176 
177  // Read items from SDRAM into variables for use later
178  uint32_t n_params = config->n_params;
179  uint32_t bytes_per_repeat = config->bytes_per_repeat;
180  uint32_t n_repeats_total = config->n_repeats_total;
181  if (n_repeats_total == REPEAT_PER_NEURON) {
182  n_repeats_total = n_neurons;
183  }
184  log_debug("Reading %u params, %u bytes per neuron, %u neurons, "
185  "%u bytes to end of struct", n_params, bytes_per_repeat,
186  n_repeats_total, config->struct_size_bytes);
187 
188  // Get the current struct position
189  uint8_t* struct_ptr = *neuron_params_region;
190  *neuron_params_region = &(struct_ptr[bytes_per_repeat * n_repeats_total]);
191 
192  // Keep track of the offset of the param from the start of each struct
193  uint32_t param_offset = 0;
194 
195  // Go through the params in the struct to be expanded
196  neuron_param_t *param = &(config->param[0]);
197  for (uint32_t p = 0; p < n_params; p++) {
198  log_debug(" Param %u, type=%u, n_items=%u", p, param->param_type,
199  param->n_items);
200 
201  // Get the writer for the parameter type
202  type_info *writer = get_type_writer(param->param_type);
203 
204  // Align the offset for the size of parameter to be written
205  param_offset += align_offset(param_offset, writer->size);
206  log_debug(" Writing %u bytes each time to struct offset %u",
207  writer->size, param_offset);
208 
209  // Go through the items and generate
210  uint32_t n_items = param->n_items;
211  uint32_t offset = 0;
212  for (uint32_t i = 0; i < n_items; i++) {
213  neuron_param_item_t item = param->item[i];
214  log_debug(" Item %u, generator=%u, n_repeats=%u",
215  i, item.generator, item.n_repeats);
216 
217  param_generator_t gen = param_generator_init(item.generator, region);
218  if (gen == NULL) {
219  return false;
220  }
221 
222  uint32_t n_repeats = item.n_repeats;
223  if (n_repeats == REPEAT_PER_NEURON) {
224  n_repeats = n_neurons;
225  log_debug(" (Really only repeating %u times!)", n_repeats);
226  }
227 
228  // Generate the requested number of times
229  for (uint32_t r = 0; r < n_repeats; r++) {
230  accum value = param_generator_generate(gen);
231  uint32_t index = offset + param_offset;
232  log_debug(" Writing %k to offset %u", value, index);
233  writer->writer(&(struct_ptr[index]), value);
234  offset += bytes_per_repeat;
235  }
236 
237  // Finish with the generator
239  }
240 
241  // After writing, add to the offset for the next parameter
242  param_offset += writer->size;
243 
244  // Go to the next param
245  param = (neuron_param_t *) &(param->item[n_items]);
246  }
247 
248  // Return success!
249  return true;
250 }
251 
252 static inline void read_index(uint32_t n_items, recording_index_t *items,
253  uint32_t n_neurons, uint32_t n_neurons_max, uint32_t n_neurons_recording,
254  uint16_t *sdram_out) {
255  // Go through the data
256  uint16_t indices[n_neurons_max];
257  uint32_t neuron_id = 0;
258  uint16_t next_index = 0;
259  for (uint32_t i = 0; i < n_items; i++) {
260  if (neuron_id >= n_neurons) {
261  log_error("The next neuron %u >= the maximum %u", neuron_id, n_neurons);
262  rt_error(RTE_SWERR);
263  }
264  recording_index_t item = items[i];
265  uint32_t n_repeats = item.n_repeats;
266  if (n_repeats == REPEAT_PER_NEURON_RECORDED) {
267  n_repeats = n_neurons;
268  }
269  if (item.is_recording) {
270  for (uint32_t r = 0; r < n_repeats; r++) {
271  if (next_index >= n_neurons_recording) {
272  log_error("The next index %u >= the maximum %u", next_index,
273  n_neurons_recording);
274  rt_error(RTE_SWERR);
275  }
276  indices[neuron_id++] = next_index++;
277  }
278  } else {
279  for (uint32_t r = 0; r < n_repeats; r++) {
280  indices[neuron_id++] = (uint16_t) n_neurons_recording;
281  }
282  }
283  }
284 
285  // Copy to SDRAM
286  uint32_t *index_words = (uint32_t *) &(indices[0]);
287  uint32_t *sdram_out_words = (uint32_t *) sdram_out;
288  for (uint32_t i = 0; i < (n_neurons_max >> 1); i++) {
289  sdram_out_words[i] = index_words[i];
290  }
291 }
292 
293 static void write_zero_index(uint32_t n_neurons_max, uint16_t *sdram_out) {
294  uint32_t *sdram_out_words = (uint32_t *) sdram_out;
295  for (uint32_t i = 0; i < (n_neurons_max >> 1); i++) {
296  sdram_out_words[i] = 0;
297  }
298 }
299 
300 static inline uint32_t get_n_recording(uint32_t n_recording, uint32_t n_neurons) {
301  if (n_recording == REPEAT_PER_NEURON) {
302  return n_neurons;
303  }
304  return n_recording;
305 }
306 
307 static void read_recorded_variable(void **region, void **recording_region,
308  uint32_t n_neurons, uint32_t n_neurons_max) {
309  // Get the recording data
311  uint32_t n_items = rec->n_index_items;
312  *region = &(rec->index_items[n_items]);
313 
314  // Get the place to write data to, and move on to next
315  sdram_variable_recording_data_t *sdram_out = *recording_region;
316  *recording_region = &(sdram_out->indices[n_neurons_max]);
317 
318  // Do the simple things
319  uint32_t rate = rec->rate;
320  uint32_t n_recording = get_n_recording(rec->n_recording, n_neurons);
321  sdram_out->rate = rate;
322  sdram_out->element_size = rec->element_size;
323  sdram_out->n_recording = n_recording;
324 
325  if (rate == 0) {
326  write_zero_index(n_neurons_max, &sdram_out->indices[0]);
327  } else {
328  read_index(n_items, &rec->index_items[0], n_neurons, n_neurons_max,
329  n_recording, &sdram_out->indices[0]);
330  }
331 }
332 
333 static void read_recorded_bitfield(void **region, void **recording_region,
334  uint32_t n_neurons, uint32_t n_neurons_max) {
335  // Get the recording data
337  uint32_t n_items = rec->n_index_items;
338  *region = &(rec->index_items[n_items]);
339 
340  // Get the place to write data to, and move on to next
341  sdram_bitfield_recording_data_t *sdram_out = *recording_region;
342  *recording_region = &(sdram_out->indices[n_neurons_max]);
343 
344  // Do the simple things
345  uint32_t rate = rec->rate;
346  uint32_t n_recording = get_n_recording(rec->n_recording, n_neurons);
347  sdram_out->rate = rate;
348  sdram_out->n_recording = n_recording;
349 
350  if (rate == 0) {
351  write_zero_index(n_neurons_max, &sdram_out->indices[0]);
352  } else {
353  read_index(n_items, &rec->index_items[0], n_neurons, n_neurons_max,
354  n_recording, &sdram_out->indices[0]);
355  }
356 }
357 
365 static bool run_neuron_expander(data_specification_metadata_t *ds_regions,
366  void *params_address) {
367  // Read in the global parameters
368  expander_config_t *sdram_config = params_address;
369  expander_config_t *config = spin1_malloc(sizeof(expander_config_t));
370  fast_memcpy(config, sdram_config, sizeof(expander_config_t));
371  log_info("Generating %u structs", config->n_structs);
372 
373  // Get the synaptic matrix region
374  void *neuron_params_region = data_specification_get_region(
375  config->neuron_params_region, ds_regions);
376 
377  // Store the RNGs
378  population_rng = &(config->population_rng);
379  core_rng = &(config->core_rng);
380 
381  log_info("Population RNG: %u %u %u %u", population_rng->seed[0],
382  population_rng->seed[1], population_rng->seed[2],
383  population_rng->seed[3]);
384 
385  log_info("Core RNG: %u %u %u %u", core_rng->seed[0],
386  core_rng->seed[1], core_rng->seed[2], core_rng->seed[3]);
387 
388 
389  // Go through each struct and generate
390  void *address = &(sdram_config[1]);
391 
392  // Read the remaining structs
393  uint32_t n_neurons = config->n_neurons;
394  for (uint32_t s = 0; s < config->n_structs; s++) {
395  if (!read_struct_builder_region(&address, &neuron_params_region,
396  n_neurons)) {
397  return false;
398  }
399  }
400 
401  // Read recording data
402  recording_params_t *recording_params = address;
403  recording_params_t *sdram_recording_params = data_specification_get_region(
404  config->neuron_recording_region, ds_regions);
405 
406  // Copy header data
407  uint32_t n_variables = recording_params->n_recordable_variables;
408  uint32_t n_bitfields = recording_params->n_recordable_bit_fields;
409  sdram_recording_params->n_recordable_variables = n_variables;
410  sdram_recording_params->n_recordable_bit_fields = n_bitfields;
411 
412  // Move read and write pointers
413  address = &(recording_params[1]);
414  void *sdram_address = &(sdram_recording_params[1]);
415 
416  // Round up the number of neurons to the next multiple of 2
417  uint32_t n_neurons_max = (n_neurons + CEIL_TO_2) & FLOOR_TO_2;
418 
419  // Do variables
420  for (uint32_t i = 0; i < n_variables; i++) {
421  read_recorded_variable(&address, &sdram_address, n_neurons, n_neurons_max);
422  }
423  // Do bitfields
424  for (uint32_t i = 0; i < n_bitfields; i++) {
425  read_recorded_bitfield(&address, &sdram_address, n_neurons, n_neurons_max);
426  }
427 
428  // Clear checksums to avoid later issues
429  ds_regions->regions[config->neuron_params_region].checksum = 0;
430  ds_regions->regions[config->neuron_params_region].n_words = 0;
431  ds_regions->regions[config->neuron_recording_region].checksum = 0;
432  ds_regions->regions[config->neuron_recording_region].n_words = 0;
433 
434  return true;
435 }
436 
438 void c_main(void) {
439  sark_cpu_state(CPU_STATE_RUN);
440 
441  log_info("Starting To Build Connectors");
442 
443  // Get pointer to 1st virtual processor info struct in SRAM and get USER1;
444  // This is the ID of the connection builder region from which to read the
445  // rest of the data
446  vcpu_t *virtual_processor_table = (vcpu_t*) SV_VCPU;
447  uint user1 = virtual_processor_table[spin1_get_core_id()].user1;
448 
449  // Get the addresses of the regions
450  data_specification_metadata_t *ds_regions =
451  data_specification_get_data_address();
452  void *params_address = data_specification_get_region(user1, ds_regions);
453  log_info("\tReading SDRAM at 0x%08x", params_address);
454 
455  // Run the expander
456  if (!run_neuron_expander(ds_regions, params_address)) {
457  log_info("!!! Error reading SDRAM data !!!");
458  rt_error(RTE_ABORT);
459  }
460 
461  log_info("Finished On Machine Connectors!");
462 }
Utility functions for working with memory.
static void fast_memcpy(void *restrict to, const void *restrict from, size_t num_bytes)
A small and fast version of memcpy().
Definition: common_mem.h:33
static struct local_only_config config
A local copy of the configuration.
Definition: local_only.c:43
static uint32_t n_neurons
The number of neurons on the core.
Definition: neuron.c:45
#define FLOOR_TO_2
When bitwise anded with a number will floor to the nearest multiple of 2.
rng_t * core_rng
An RNG that is local to the current core.
static bool run_neuron_expander(data_specification_metadata_t *ds_regions, void *params_address)
Read the data for the expander.
void c_main(void)
Entry point.
#define CEIL_TO_2
Add to a number before applying floor to 2 to turn it into a ceil operation.
rng_t * population_rng
An RNG that starts in the same place on every core of the Population.
static bool read_struct_builder_region(void **region, void **neuron_params_region, uint32_t n_neurons)
Generate the synapses for a single connector.
The configuration of a struct.
void param_generator_free(param_generator_t generator)
Finish with a parameter generator.
param_generator_t param_generator_init(uint32_t hash, void **in_region)
Initialise a specific parameter generator.
accum param_generator_generate(param_generator_t generator)
Generate value with a parameter generator.
Interface for parameter generator.
Random number generator interface.
The Random number generator parameters.
Definition: rng.h:30
region
spike source array region IDs in human readable form
A region of SDRAM used to transfer synapses.
The type converters for parameter generation.