summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/README.md66
-rw-r--r--src/application.c82
-rw-r--r--src/benchmark.c67
-rw-r--r--src/benchmark.h6
-rw-r--r--src/crossbar_generator.c50
-rw-r--r--src/crossbar_generator.h4
-rw-r--r--src/online_crossbar.c413
-rw-r--r--src/online_crossbar.h40
-rw-r--r--src/read_crossbar.c43
-rw-r--r--src/read_crossbar.h6
10 files changed, 715 insertions, 62 deletions
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 <spires.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
// 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 <math.h>
#include <plplot/plplot.h>
@@ -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 <ctype.h>
+#include <math.h>
+#include <ngspice/sharedspice.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 <stddef.h>
+
+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 <ctype.h>
-#include <errno.h> //maybe I could use this
-#include <fcntl.h>
-#include <math.h>
+#include <stdbool.h>
+#include <ngspice/sharedspice.h>
+
+#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
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 <stddef.h>
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);