sPyNNaker neural_modelling 7.1.1
Loading...
Searching...
No Matches
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).
49static 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
56static 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
68typedef 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
75typedef 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
83
85typedef 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
100typedef struct sdram_variable_recording_data {
101 uint32_t rate;
102 uint32_t n_recording;
103 uint32_t element_size;
104 uint16_t indices[];
106
107typedef struct sdram_bitfield_recording_data {
108 uint32_t rate;
109 uint32_t n_recording;
110 uint16_t indices[];
112
113typedef struct recording_index {
114 uint32_t n_repeats:31;
115 uint32_t is_recording:1;
117
118typedef 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
126typedef 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
133typedef 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)))
142typedef struct expander_config {
143 uint32_t neuron_params_region;
144 uint32_t neuron_recording_region;
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
252static 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
293static 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
300static 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
307static 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
333static 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
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
438void c_main(void) {
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
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 !!!");
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
data_specification_metadata_t * data_specification_get_data_address(void)
uint32_t n_words
uint32_t checksum
void log_error(const char *message,...)
void log_debug(const char *message,...)
void log_info(const char *message,...)
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
#define SV_VCPU
CPU_STATE_RUN
RTE_ABORT
uint user1
void sark_cpu_state(cpu_state state)
void rt_error(uint code,...)
region
spike source array region IDs in human readable form
A region of SDRAM used to transfer synapses.
#define NULL
uint spin1_get_core_id(void)
unsigned int uint
The type converters for parameter generation.