From 8671410a0f9bdd4b405762ad4790c15d601b0fba Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 28 Aug 2026 13:42:10 -0700 Subject: online crossbar inputs --- src/README.md | 66 +++++++- src/application.c | 82 +++++++--- src/benchmark.c | 67 +++++++- src/benchmark.h | 6 + src/crossbar_generator.c | 50 ++++++ src/crossbar_generator.h | 4 + src/online_crossbar.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++ src/online_crossbar.h | 40 +++++ src/read_crossbar.c | 43 ++--- src/read_crossbar.h | 6 +- 10 files changed, 715 insertions(+), 62 deletions(-) create mode 100644 src/online_crossbar.c create mode 100644 src/online_crossbar.h (limited to 'src') diff --git a/src/README.md b/src/README.md index 48a0c5b..b973527 100644 --- a/src/README.md +++ b/src/README.md @@ -11,8 +11,9 @@ and generating plots. * const char *subcircuit_name * **run_benchmark() (int)** - * Executes the full crossbar simulation pipeline and stores the decoded - crossbar predictions in the passed output array. + * Executes the offline crossbar pipeline. It generates a batch netlist from + a previously collected state matrix, launches the ngspice executable, reads + the output file, and stores the decoded predictions in the output array. * parameters * const spires_reservoir_config *config * spires_reservoir *reservoir @@ -21,6 +22,19 @@ and generating plots. * const char *subcircuit_name * double *predictions_out +* **run_online_benchmark() (int)** + * Steps SPIRES once per input, streams each live reservoir state to a + persistent shared-ngspice crossbar, and stores the one-step-delayed decoded + predictions in the output array. + * parameters + * const spires_reservoir_config *config + * spires_reservoir *reservoir + * const double *input_series + * size_t num_timesteps + * const char *model_path + * const char *subcircuit_name + * double *predictions_out + * **calculate_MSE() (double)** * calculates the mean squared error of the predictions against the expected values @@ -86,6 +100,54 @@ Handles generation of the SPICE netlist used to simulate the memristive crossbar * const char *output_filename * const Crossbar_Config *config +* **generate_online_crossbar() (int)** + * Generates a persistent transient netlist whose row voltages are supplied + through shared-ngspice external voltage-source callbacks. + * parameters + * const char *output_filename + * const Crossbar_Config *config + + +## online_crossbar.h + +Provides the stateful online readout engine used by `run_online_benchmark()`. +The engine owns the shared-ngspice lifecycle, synchronization state, trained +weight-to-resistance mapping, bounded state buffer, and decoded result storage. + +* **Online_Crossbar_Config struct** + * Configures the crossbar dimensions, known run length, SPICE timestep, + voltage scale, load and device resistance limits, device model, subcircuit, + and generated netlist path. + +* **online_crossbar_init() (int)** + * Copies the trained SPIRES readout weights, maps them into differential + resistances, allocates the bounded pipeline, and generates the external-source + netlist. + +* **online_crossbar_start() (int)** + * Initializes shared-ngspice, loads the netlist, installs the first timestep + breakpoint, and starts the transient simulation on the ngspice worker thread. + +* **online_crossbar_submit() (int)** + * Submits `state[t]` with bounded backpressure. Submission `t = 0` reports + that no output is ready. Each later submission returns decoded output + `t - 1`. + +* **online_crossbar_finish() (int)** + * Waits for and returns the decoded output associated with the final + submitted state. + +* **online_crossbar_destroy() (void)** + * Stops a running simulation if necessary, waits for its callbacks to exit, + resets shared-ngspice, and releases all synchronization and data storage. + +The online simulation remains continuous for the full known run length, so +stateful memristor models retain their internal state between reservoir +timesteps. Explicit SPICE breakpoints guarantee that a result is emitted before +the pipeline requests a state that has not yet been published. At most one +future state is buffered; slow crossbar evaluation therefore blocks the +producer instead of dropping or reordering data. + ## spires_interface.h diff --git a/src/application.c b/src/application.c index b2105ce..6a868b3 100644 --- a/src/application.c +++ b/src/application.c @@ -5,6 +5,7 @@ #include #include #include +#include // spires reservoir parameters #define NUM_NEURONS 400 @@ -20,12 +21,37 @@ #define PI 3.14159265358979323846 #define LAMBDA 1.0e-4 -#define NUM_TRAINING_STEPS 50 +#define NUM_TRAINING_STEPS 500 #define NUM_CROSSBAR_COLUMNS (NUM_OUTPUTS * 2) // #define NUM_STEPS 2000 -int main(void) +typedef enum { + BENCHMARK_ONLINE, + BENCHMARK_OFFLINE, +} Benchmark_Mode; + +static int parse_mode(int argc, char **argv, Benchmark_Mode *mode) +{ + if (argc == 1 || (argc == 2 && strcmp(argv[1], "--online") == 0)) { + *mode = BENCHMARK_ONLINE; + return 0; + } + if (argc == 2 && strcmp(argv[1], "--offline") == 0) { + *mode = BENCHMARK_OFFLINE; + return 0; + } + fprintf(stderr, "Usage: %s [--online|--offline]\n", argv[0]); + return -1; +} + +int main(int argc, char **argv) { + Benchmark_Mode mode; + if (parse_mode(argc, argv, &mode) != 0) + return -1; + printf("Benchmark mode: %s\n", + mode == BENCHMARK_ONLINE ? "online" : "offline"); + /* ---------- LIST ALL MODELS HERE ----------*/ MemModel models[] = { { @@ -110,25 +136,23 @@ int main(void) return -1; } - /* ---------- Collect reservoir states ----------*/ Reservoir_State_Matrix state_matrix = {0}; - if (collect_reservoir_states(reservoir, training_inputs, - NUM_TRAINING_STEPS, &state_matrix) != 0) { - fprintf(stderr, "Failed to collect reservoir states"); - spires_reservoir_destroy(reservoir); - return -1; - } - printf("collected state matrix size: %zu x %zu\n", - state_matrix.num_samples, state_matrix.num_features); - - /* ---------- Generate raster plot ----------*/ - if (plot_raster(&state_matrix, NUM_NEURONS, 0.5) != 0) { - fprintf(stderr, "Failed to plot raster"); + if (mode == BENCHMARK_OFFLINE) { + if (collect_reservoir_states(reservoir, training_inputs, + NUM_TRAINING_STEPS, + &state_matrix) != 0) { + fprintf(stderr, "Failed to collect reservoir states"); + spires_reservoir_destroy(reservoir); + return -1; + } + printf("Collected offline state matrix: %zu x %zu\n", + state_matrix.num_samples, state_matrix.num_features); + if (plot_raster(&state_matrix, NUM_NEURONS, 0.5) != 0) + fprintf(stderr, "Failed to plot raster\n"); } /* ---------- Run Benchmark on each model ----------*/ - size_t predictions_per_model = - state_matrix.num_samples * config.num_outputs; + size_t predictions_per_model = NUM_TRAINING_STEPS * config.num_outputs; double *predictions = malloc(model_count * NUM_OUTPUTS * NUM_TRAINING_STEPS * sizeof(double)); @@ -147,10 +171,19 @@ int main(void) printf("\n\nRunning benchmark on %s\n", models[model].model_path); - if (run_benchmark(&config, reservoir, &state_matrix, - models[model].model_path, - models[model].subcircuit_name, - model_predictions) < 0) { + int benchmark_status; + if (mode == BENCHMARK_ONLINE) { + benchmark_status = run_online_benchmark( + &config, reservoir, training_inputs, + NUM_TRAINING_STEPS, models[model].model_path, + models[model].subcircuit_name, model_predictions); + } else { + benchmark_status = run_benchmark( + &config, reservoir, &state_matrix, + models[model].model_path, + models[model].subcircuit_name, model_predictions); + } + if (benchmark_status < 0) { fprintf(stderr, "Failed to run benchmark"); free(predictions); free_reservoir_state_matrix(&state_matrix); @@ -159,12 +192,12 @@ int main(void) } plot_reservoir_predictions( - target_outputs, model_predictions, state_matrix.num_samples, + target_outputs, model_predictions, NUM_TRAINING_STEPS, config.num_outputs, 0, models[model].model_path); mean_squared_error[model] = calculate_MSE(target_outputs, model_predictions, - state_matrix.num_samples, config.num_outputs); + NUM_TRAINING_STEPS, config.num_outputs); } double *fixed_predictions = predictions; @@ -174,8 +207,7 @@ int main(void) model_predictions = predictions + model * predictions_per_model; if (plot_model_delta(fixed_predictions, model_predictions, - state_matrix.num_samples, - config.num_outputs, 0, + NUM_TRAINING_STEPS, config.num_outputs, 0, models[model].model_path) < 0) { fprintf(stderr, "Failed to plot model delta\n"); } diff --git a/src/benchmark.c b/src/benchmark.c index 0e667f2..d3427e4 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -2,6 +2,7 @@ #include "crossbar_generator.h" #include "read_crossbar.h" #include "spires_interface.h" +#include "online_crossbar.h" #include #include @@ -13,6 +14,68 @@ #define SPIKE_THRESHOLD 0.1 #define SPIKE_AMPLITUDE 0.1 +int run_online_benchmark(const spires_reservoir_config *config, + spires_reservoir *reservoir, + const double *input_series, size_t num_timesteps, + const char *model_path, const char *subcircuit_name, + double *predictions_out) +{ + if (!config || !reservoir || !input_series || num_timesteps == 0 || + !model_path || !subcircuit_name || !predictions_out) + return -1; + + Online_Crossbar_Config online_config = { + .num_neurons = config->num_neurons, + .num_outputs = config->num_outputs, + .num_timesteps = num_timesteps, + .time_step = 1.0e-6, + .spike_amplitude = SPIKE_AMPLITUDE, + .load_resistance = 50.0, + .r_on = 1000.0, + .r_off = 100000.0, + .model_path = model_path, + .subcircuit_name = subcircuit_name, + .netlist_path = "output/online_crossbar.cir"}; + + Online_Crossbar *crossbar = NULL; + double *state = malloc(config->num_neurons * sizeof(*state)); + if (!state) + return -1; + if (spires_reservoir_reset(reservoir) != SPIRES_OK || + online_crossbar_init(&online_config, reservoir, &crossbar) != 0 || + online_crossbar_start(crossbar) != 0) { + free(state); + online_crossbar_destroy(crossbar); + return -1; + } + + for (size_t timestep = 0; timestep < num_timesteps; timestep++) { + const double *input = + input_series + timestep * config->num_inputs; + int output_ready = 0; + double *previous = timestep == 0 + ? NULL + : predictions_out + + (timestep - 1) * config->num_outputs; + if (spires_step(reservoir, input) != SPIRES_OK || + spires_read_reservoir_state(reservoir, state) != SPIRES_OK || + online_crossbar_submit(crossbar, timestep, state, previous, + &output_ready) != 0 || + output_ready != (timestep != 0)) { + free(state); + online_crossbar_destroy(crossbar); + return -1; + } + } + + int status = online_crossbar_finish( + crossbar, predictions_out + + (num_timesteps - 1) * config->num_outputs); + free(state); + online_crossbar_destroy(crossbar); + return status; +} + int run_benchmark(const spires_reservoir_config *config, spires_reservoir *reservoir, Reservoir_State_Matrix *state_matrix, const char *model_path, @@ -81,8 +144,8 @@ int run_benchmark(const spires_reservoir_config *config, // crossbar parameters needed for reading Crossbar_Output_Matrix crossbar_output = { - .num_samples = state_matrix->num_samples, - .num_outputs = config->num_outputs * 2, + // .num_samples = state_matrix->num_samples, + // .num_outputs = config->num_outputs * 2, .time = NULL, .voltages = NULL}; diff --git a/src/benchmark.h b/src/benchmark.h index 57156cc..c7ffab6 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -16,6 +16,12 @@ int run_benchmark(const spires_reservoir_config *config, Reservoir_State_Matrix *state_matrix, const char *model_path, const char *subcircuit_name, double *predictions_out); +int run_online_benchmark(const spires_reservoir_config *config, + spires_reservoir *reservoir, + const double *input_series, size_t num_timesteps, + const char *model_path, const char *subcircuit_name, + double *predictions_out); + double calculate_MSE(const double *expected, const double *predicted, const size_t num_steps, const size_t num_outputs); diff --git a/src/crossbar_generator.c b/src/crossbar_generator.c index 4e0b935..4bf6746 100644 --- a/src/crossbar_generator.c +++ b/src/crossbar_generator.c @@ -86,6 +86,19 @@ static int write_input_series(FILE *file, const Crossbar_Config *config) return ferror(file) ? -1 : 0; } +static int write_external_inputs(FILE *file, const Crossbar_Config *config) +{ + if (fprintf(file, "\n* Online SPIRES reservoir states\n") < 0) + return -1; + + for (size_t row = 0; row < config->rows; row++) { + if (fprintf(file, "VROW%zu row%zu 0 dc 0 external\n", row, + row) < 0) + return -1; + } + return 0; +} + static int write_memristor_array(FILE *file, const Crossbar_Config *config) { if (fprintf(file, "\n* Memristor Array\n") < 0) { @@ -201,3 +214,40 @@ int generate_crossbar(const char *output_filename, } return 0; } + +int generate_online_crossbar(const char *output_filename, + const Crossbar_Config *config) +{ + if (validate_config(config) < 0) { + fprintf(stderr, "invalid config"); + return -1; + } + + FILE *file = fopen(output_filename, "w"); + if (file == NULL) { + fprintf(stderr, "could not create online crossbar file"); + return -1; + } + + if (write_header(file, config) != 0 || + write_external_inputs(file, config) != 0 || + write_memristor_array(file, config) != 0 || + write_column_loads(file, config) != 0 || + fprintf(file, "\n* Online simulation\n.tran %.17g %.17g uic\n", + config->time_step, config->stop_time) < 0 || + fprintf(file, ".save time") < 0) { + fclose(file); + return -1; + } + + for (size_t column = 0; column < config->columns; column++) { + if (fprintf(file, " v(col%zu)", column) < 0) { + fclose(file); + return -1; + } + } + + if (fprintf(file, "\n.end\n") < 0 || fclose(file) != 0) + return -1; + return 0; +} diff --git a/src/crossbar_generator.h b/src/crossbar_generator.h index d773fad..8fc754b 100644 --- a/src/crossbar_generator.h +++ b/src/crossbar_generator.h @@ -37,4 +37,8 @@ typedef struct { int generate_crossbar(const char *output_filename, const Crossbar_Config *config); +/* Generate a netlist whose row voltages are supplied by libngspice callbacks. */ +int generate_online_crossbar(const char *output_filename, + const Crossbar_Config *config); + #endif diff --git a/src/online_crossbar.c b/src/online_crossbar.c new file mode 100644 index 0000000..b01ea19 --- /dev/null +++ b/src/online_crossbar.c @@ -0,0 +1,413 @@ +#include "online_crossbar.h" + +#include "crossbar_generator.h" +#include "read_crossbar.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct Online_Crossbar { + Online_Crossbar_Config config; + double *resistances; + conductance_mapping mapping; + double *state_ring; + double *results; + size_t published_count; + size_t consumed_count; + size_t result_count; + int started; + int simulation_done; + int failed; + int mutex_initialized; + int state_condition_initialized; + int progress_condition_initialized; + pthread_mutex_t mutex; + pthread_cond_t state_available; + pthread_cond_t progress; +}; + +static int callback_text(char *text, int ident, void *user_data) +{ + (void)ident; + (void)user_data; + if (text && + (strncmp(text, "stderr", 6) == 0 || strstr(text, "Error") != NULL || + strstr(text, "error") != NULL)) + fprintf(stderr, "ngspice: %s\n", text); + return 0; +} + +static int callback_init_data(pvecinfoall values, int ident, void *user_data) +{ + (void)values; + (void)ident; + (void)user_data; + return 0; +} + +static void fail_locked(Online_Crossbar *crossbar) +{ + crossbar->failed = 1; + pthread_cond_broadcast(&crossbar->state_available); + pthread_cond_broadcast(&crossbar->progress); +} + +static int callback_exit(int status, NG_BOOL immediate, NG_BOOL quit_exit, + int ident, void *user_data) +{ + (void)status; + (void)immediate; + (void)quit_exit; + (void)ident; + Online_Crossbar *crossbar = user_data; + pthread_mutex_lock(&crossbar->mutex); + fail_locked(crossbar); + pthread_mutex_unlock(&crossbar->mutex); + return 0; +} + +static int callback_background(NG_BOOL running, int ident, void *user_data) +{ + (void)ident; + Online_Crossbar *crossbar = user_data; + /* libngspice passes false at worker start and true at worker exit. */ + if (!running) + return 0; + pthread_mutex_lock(&crossbar->mutex); + crossbar->simulation_done = 1; + if (crossbar->result_count < crossbar->config.num_timesteps) + fail_locked(crossbar); + pthread_cond_broadcast(&crossbar->progress); + pthread_cond_broadcast(&crossbar->state_available); + pthread_mutex_unlock(&crossbar->mutex); + return 0; +} + +static int row_from_name(const char *name, size_t *row_out) +{ + const char *digits = name + strlen(name); + while (digits > name && isdigit((unsigned char)digits[-1])) + digits--; + if (*digits == '\0') + return -1; + char *end = NULL; + unsigned long value = strtoul(digits, &end, 10); + if (end == digits || *end != '\0') + return -1; + *row_out = (size_t)value; + return 0; +} + +static size_t timestep_for_time(const Online_Crossbar *crossbar, double time) +{ + if (time <= 0.0) + return 0; + double scaled = time / crossbar->config.time_step; + /* Hold state[t] over the full interval (t*dt, (t+1)*dt]. */ + double interval = ceil(scaled - 1.0e-9); + size_t timestep = interval <= 1.0 ? 0 : (size_t)interval - 1; + if (timestep >= crossbar->config.num_timesteps) + timestep = crossbar->config.num_timesteps - 1; + return timestep; +} + +static int callback_voltage(double *voltage, double time, char *name, int ident, + void *user_data) +{ + (void)ident; + Online_Crossbar *crossbar = user_data; + size_t row; + if (!name || row_from_name(name, &row) != 0 || + row >= crossbar->config.num_neurons) { + fprintf(stderr, "ngspice: unrecognized external source %s\n", + name ? name : "(null)"); + return 1; + } + + size_t timestep = timestep_for_time(crossbar, time); + pthread_mutex_lock(&crossbar->mutex); + while (!crossbar->failed && crossbar->published_count <= timestep) + pthread_cond_wait(&crossbar->state_available, &crossbar->mutex); + if (crossbar->failed) { + pthread_mutex_unlock(&crossbar->mutex); + return 1; + } + + *voltage = + crossbar->config.spike_amplitude * + crossbar->state_ring[(timestep % 2) * crossbar->config.num_neurons + + row]; + if (crossbar->consumed_count < timestep + 1) { + crossbar->consumed_count = timestep + 1; + pthread_cond_broadcast(&crossbar->progress); + } + pthread_mutex_unlock(&crossbar->mutex); + return 0; +} + +static int callback_data(pvecvaluesall values, int count, int ident, + void *user_data) +{ + (void)count; + (void)ident; + Online_Crossbar *crossbar = user_data; + double time = -1.0; + double *columns = + calloc(crossbar->config.num_outputs * 2, sizeof(*columns)); + unsigned char *found = + calloc(crossbar->config.num_outputs * 2, sizeof(*found)); + if (!columns || !found) { + free(columns); + free(found); + pthread_mutex_lock(&crossbar->mutex); + fail_locked(crossbar); + pthread_mutex_unlock(&crossbar->mutex); + return 1; + } + + for (int i = 0; i < values->veccount; i++) { + pvecvalues value = values->vecsa[i]; + if (value->is_scale || strcmp(value->name, "time") == 0) { + time = value->creal; + continue; + } + const char *col = strstr(value->name, "col"); + if (!col) + continue; + char *end = NULL; + unsigned long index = strtoul(col + 3, &end, 10); + if (end != col + 3 && + index < crossbar->config.num_outputs * 2) { + columns[index] = value->creal; + found[index] = 1; + } + } + + if (time >= 0.0) { + double scaled = time / crossbar->config.time_step; + double rounded = nearbyint(scaled); + if (fabs(scaled - rounded) <= 1.0e-7 && rounded >= 1.0 && + (size_t)rounded <= crossbar->config.num_timesteps) { + size_t timestep = (size_t)rounded - 1; + int complete = 1; + for (size_t i = 0; i < crossbar->config.num_outputs * 2; + i++) + complete = complete && found[i]; + if (complete) { + double *decoded = + crossbar->results + + timestep * crossbar->config.num_outputs; + if (convert_output_to_software( + crossbar->config.num_neurons, + crossbar->config.num_outputs, 1, + columns, crossbar->resistances, + crossbar->config.load_resistance, + &crossbar->mapping, NULL, + crossbar->config.spike_amplitude, + decoded) == 0) { + pthread_mutex_lock(&crossbar->mutex); + if (crossbar->result_count < + timestep + 1) + crossbar->result_count = + timestep + 1; + if (timestep + 1 < + crossbar->config.num_timesteps) + ngSpice_SetBkpt( + (timestep + 2) * + crossbar->config.time_step); + pthread_cond_broadcast( + &crossbar->progress); + pthread_mutex_unlock(&crossbar->mutex); + } + } + } + } + + free(columns); + free(found); + return 0; +} + +int online_crossbar_init(const Online_Crossbar_Config *config, + const spires_reservoir *reservoir, + Online_Crossbar **crossbar_out) +{ + if (!config || !reservoir || !crossbar_out || + config->num_neurons == 0 || config->num_outputs == 0 || + config->num_timesteps == 0 || config->time_step <= 0.0 || + config->spike_amplitude == 0.0 || config->load_resistance <= 0.0 || + !config->model_path || !config->subcircuit_name || + !config->netlist_path) + return -1; + + Online_Crossbar *crossbar = calloc(1, sizeof(*crossbar)); + if (!crossbar) + return -1; + crossbar->config = *config; + if (pthread_mutex_init(&crossbar->mutex, NULL) != 0) + goto fail; + crossbar->mutex_initialized = 1; + if (pthread_cond_init(&crossbar->state_available, NULL) != 0) + goto fail; + crossbar->state_condition_initialized = 1; + if (pthread_cond_init(&crossbar->progress, NULL) != 0) + goto fail; + crossbar->progress_condition_initialized = 1; + + crossbar->state_ring = calloc(2 * config->num_neurons, sizeof(double)); + crossbar->results = + calloc(config->num_timesteps * config->num_outputs, sizeof(double)); + if (!crossbar->state_ring || !crossbar->results) + goto fail; + if (convert_weights_to_resistances( + reservoir, config->num_neurons, config->num_outputs, + config->r_on, config->r_off, &crossbar->resistances, + &crossbar->mapping) != 0) + goto fail; + + Crossbar_Config netlist = {.rows = config->num_neurons, + .columns = config->num_outputs * 2, + .input_series = crossbar->state_ring, + .num_samples = config->num_timesteps, + .initial_resistance = crossbar->resistances, + .model_path = config->model_path, + .subcircuit_name = config->subcircuit_name, + .load_resistance = config->load_resistance, + .time_step = config->time_step, + .stop_time = config->num_timesteps * + config->time_step, + .print_state_nodes = 0}; + if (generate_online_crossbar(config->netlist_path, &netlist) != 0) + goto fail; + + *crossbar_out = crossbar; + return 0; + +fail: + online_crossbar_destroy(crossbar); + return -1; +} + +int online_crossbar_start(Online_Crossbar *crossbar) +{ + if (!crossbar || crossbar->started) + return -1; + if (ngSpice_Init(callback_text, callback_text, callback_exit, + callback_data, callback_init_data, callback_background, + crossbar) != 0 || + ngSpice_Init_Sync(callback_voltage, NULL, NULL, NULL, crossbar) != + 0) + return -1; + + char command[4096]; + if (snprintf(command, sizeof(command), "source %s", + crossbar->config.netlist_path) >= (int)sizeof(command) || + ngSpice_Command(command) != 0) + return -1; + crossbar->started = 1; + ngSpice_SetBkpt(crossbar->config.time_step); + if (ngSpice_Command("bg_run") != 0) { + crossbar->started = 0; + return -1; + } + return 0; +} + +int online_crossbar_submit(Online_Crossbar *crossbar, size_t timestep, + const double *state, double *previous_output, + int *output_ready) +{ + if (!crossbar || !state || !output_ready || !crossbar->started || + timestep >= crossbar->config.num_timesteps || + timestep != crossbar->published_count) + return -1; + + pthread_mutex_lock(&crossbar->mutex); + while (!crossbar->failed && timestep >= crossbar->consumed_count + 2) + pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + if (crossbar->failed) { + pthread_mutex_unlock(&crossbar->mutex); + return -1; + } + memcpy(crossbar->state_ring + + (timestep % 2) * crossbar->config.num_neurons, + state, crossbar->config.num_neurons * sizeof(double)); + crossbar->published_count++; + pthread_cond_broadcast(&crossbar->state_available); + + *output_ready = timestep != 0; + if (timestep != 0) { + if (!previous_output) { + pthread_mutex_unlock(&crossbar->mutex); + return -1; + } + while (!crossbar->failed && crossbar->result_count < timestep) + pthread_cond_wait(&crossbar->progress, + &crossbar->mutex); + if (crossbar->failed) { + pthread_mutex_unlock(&crossbar->mutex); + return -1; + } + memcpy(previous_output, + crossbar->results + + (timestep - 1) * crossbar->config.num_outputs, + crossbar->config.num_outputs * sizeof(double)); + } + pthread_mutex_unlock(&crossbar->mutex); + return 0; +} + +int online_crossbar_finish(Online_Crossbar *crossbar, double *final_output) +{ + if (!crossbar || !final_output || + crossbar->published_count != crossbar->config.num_timesteps) + return -1; + pthread_mutex_lock(&crossbar->mutex); + while (!crossbar->failed && + crossbar->result_count < crossbar->config.num_timesteps) + pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + if (crossbar->failed) { + pthread_mutex_unlock(&crossbar->mutex); + return -1; + } + memcpy(final_output, + crossbar->results + (crossbar->config.num_timesteps - 1) * + crossbar->config.num_outputs, + crossbar->config.num_outputs * sizeof(double)); + pthread_mutex_unlock(&crossbar->mutex); + return 0; +} + +void online_crossbar_destroy(Online_Crossbar *crossbar) +{ + if (!crossbar) + return; + if (crossbar->started && ngSpice_running()) { + ngSpice_Command("bg_halt"); + if (crossbar->mutex_initialized) { + pthread_mutex_lock(&crossbar->mutex); + while (!crossbar->simulation_done && ngSpice_running()) + pthread_cond_wait(&crossbar->progress, + &crossbar->mutex); + pthread_mutex_unlock(&crossbar->mutex); + } + } + if (crossbar->started) + ngSpice_Reset(); + free(crossbar->resistances); + free(crossbar->state_ring); + free(crossbar->results); + if (crossbar->state_condition_initialized) + pthread_cond_destroy(&crossbar->state_available); + if (crossbar->progress_condition_initialized) + pthread_cond_destroy(&crossbar->progress); + if (crossbar->mutex_initialized) + pthread_mutex_destroy(&crossbar->mutex); + free(crossbar); +} diff --git a/src/online_crossbar.h b/src/online_crossbar.h new file mode 100644 index 0000000..f89a306 --- /dev/null +++ b/src/online_crossbar.h @@ -0,0 +1,40 @@ +#ifndef ONLINE_CROSSBAR_H +#define ONLINE_CROSSBAR_H + +#include "spires_interface.h" +#include + +typedef struct Online_Crossbar Online_Crossbar; + +typedef struct { + size_t num_neurons; + size_t num_outputs; + size_t num_timesteps; + double time_step; + double spike_amplitude; + double load_resistance; + double r_on; + double r_off; + const char *model_path; + const char *subcircuit_name; + const char *netlist_path; +} Online_Crossbar_Config; + +int online_crossbar_init(const Online_Crossbar_Config *config, + const spires_reservoir *reservoir, + Online_Crossbar **crossbar_out); +int online_crossbar_start(Online_Crossbar *crossbar); + +/* + * Submit state[t]. On return, output_ready is zero for t=0 and otherwise + * previous_output contains output[t-1]. Calls apply bounded backpressure. + */ +int online_crossbar_submit(Online_Crossbar *crossbar, size_t timestep, + const double *state, double *previous_output, + int *output_ready); + +/* Wait for and copy the output belonging to the final submitted state. */ +int online_crossbar_finish(Online_Crossbar *crossbar, double *final_output); +void online_crossbar_destroy(Online_Crossbar *crossbar); + +#endif diff --git a/src/read_crossbar.c b/src/read_crossbar.c index b075c69..5b79233 100644 --- a/src/read_crossbar.c +++ b/src/read_crossbar.c @@ -1,15 +1,12 @@ #include "read_crossbar.h" -#include -#include //maybe I could use this -#include -#include +#include +#include + +#include #include #include #include -#include -#include -#include int run_ngspice(const char *crossbar_path) { @@ -48,8 +45,8 @@ int read_crossbar(const char *data_path, size_t num_outputs, return -1; } - result->num_samples = 0; - result->num_outputs = num_outputs; + // result->num_samples = 0; + // result->num_outputs = num_outputs; result->time = NULL; result->voltages = NULL; @@ -78,22 +75,8 @@ int read_crossbar(const char *data_path, size_t num_outputs, char line[16384]; // size_t line_number = 0; + size_t sample = 0; while (fgets(line, sizeof(line), file) != NULL) { - /* Raises capacity if needed, not working rn tho */ - // if (result->num_samples == capacity) { - // capacity *= 2; - // - // result->time = realloc(result->time, capacity * - // sizeof(double)); result->voltages = - // realloc(result->voltages, capacity * sizeof(double)); - // - // if (result->time == NULL || result->voltages == NULL) { - // fprintf(stderr, "Failed to reallocate memory"); - // fclose(file); - // return -1; - // } - // } - char *position = line; double sample_time = 0.0; @@ -115,12 +98,12 @@ int read_crossbar(const char *data_path, size_t num_outputs, sample_time = time; } - result->voltages[result->num_samples * num_outputs + - output] = voltage; + result->voltages[sample * num_outputs + output] = + voltage; position += char_count; } - result->time[result->num_samples] = sample_time; - result->num_samples++; + result->time[sample] = sample_time; + sample++; } fclose(file); return 0; @@ -232,8 +215,8 @@ void free_crossbar_output_matrix(Crossbar_Output_Matrix *result) free(result->time); free(result->voltages); - result->num_samples = 0; - result->num_outputs = 0; + // result->num_samples = 0; + // result->num_outputs = 0; result->time = NULL; result->voltages = NULL; } diff --git a/src/read_crossbar.h b/src/read_crossbar.h index 74828d2..968bc94 100644 --- a/src/read_crossbar.h +++ b/src/read_crossbar.h @@ -5,14 +5,14 @@ #include typedef struct { - size_t num_samples; // number of time-steps - size_t num_outputs; // number of columns - + // size_t num_samples; + // size_t num_outputs; double *time; // stored in row major order // voltages[sameple * num_outputs + output] double *voltages; + } Crossbar_Output_Matrix; int run_ngspice(const char *crossbar_path); -- cgit v1.2.3