diff --git a/FIRMWARE/Makefile b/FIRMWARE/Makefile
index 259bb82..7c66466 100644
--- a/FIRMWARE/Makefile
+++ b/FIRMWARE/Makefile
@@ -17,9 +17,9 @@
## along with this library. If not, see .
##
-BINARY = main
+BINARY ?= main
-OBJS = HAL.o comm.o neuron.o
+OBJS = HAL.o comm.o neuron.o izhi.o
OPENCM3_DIR = ../libopencm3
diff --git a/FIRMWARE/debug/test/izhi_host.c b/FIRMWARE/debug/test/izhi_host.c
new file mode 100644
index 0000000..7ce3bb1
--- /dev/null
+++ b/FIRMWARE/debug/test/izhi_host.c
@@ -0,0 +1,50 @@
+#include
+
+#include "./izhi.h"
+
+// build with `gcc $SRCDIR/izhi.c izhi_host.c -I$SRCDIR -o izhi_host`
+// run something like `./izhi_host >neuron_out.dat`
+// and plot with gnuplot something like
+// `plot 'neuron_out.dat' using 1:2 title 'floating' with lines, \
+// 'neuron_out.dat' using 1:3 title 'fixed' with lines`
+
+int main(void) {
+ fneuron_t spiky_f[7];
+ ineuron_t spiky_i[7];
+
+ RS_f(&spiky_f[0]);
+ IB_f(&spiky_f[1]);
+ CH_f(&spiky_f[2]);
+ FS_f(&spiky_f[3]);
+ LTS_f(&spiky_f[4]);
+ RZ_f(&spiky_f[5]);
+ TC_f(&spiky_f[6]);
+
+ RS_i(&spiky_i[0]);
+ IB_i(&spiky_i[1]);
+ CH_i(&spiky_i[2]);
+ FS_i(&spiky_i[3]);
+ LTS_i(&spiky_i[4]);
+ RZ_i(&spiky_i[5]);
+ TC_i(&spiky_i[6]);
+
+ for (int i = 0; i < 5000; i++) {
+ if (i < 100) {
+ for (int j = 0; j < 7; j++) {
+ step_f(&spiky_f[j], 0, 0.125);
+ step_i(&spiky_i[j], 0, 3);
+ }
+ } else {
+ for (int j = 0; j < 7; j++) {
+ step_f(&spiky_f[j], 10, 0.125);
+ step_i(&spiky_i[j], 10 * spiky_i[j].scale, 3);
+ }
+ }
+ printf("%f ", i * 0.1);
+ for (int j = 0; j < 7; j++) {
+ printf("%f %f ", spiky_f[j].potential,
+ (float_t)(spiky_i[j].potential) / spiky_i[j].scale);
+ }
+ printf("\n");
+ }
+}
diff --git a/FIRMWARE/izhi.c b/FIRMWARE/izhi.c
new file mode 100644
index 0000000..79c8442
--- /dev/null
+++ b/FIRMWARE/izhi.c
@@ -0,0 +1,139 @@
+#include "./izhi.h"
+
+static void base_f(fneuron_t *neuron) {
+ // create a "regular spiking" floating point neuron
+ neuron->a = 0.02;
+ neuron->b = 0.2;
+ neuron->c = -65;
+ neuron->d = 2;
+ neuron->potential = neuron->recovery = 0;
+}
+
+void RS_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->d = 8;
+}
+
+void IB_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->c = -55;
+ neuron->d = 4;
+}
+
+void CH_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->c = -50;
+}
+
+void FS_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->a = 0.1;
+}
+
+void LTS_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->b = 0.25;
+}
+
+void RZ_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->a = 0.1;
+ neuron->b = 0.26;
+}
+
+void TC_f(fneuron_t *neuron) {
+ base_f(neuron);
+ neuron->b = 0.25;
+ neuron->d = 0.05;
+}
+
+void step_f(fneuron_t *neuron, float_t synapse, float_t ms) {
+ // step a neuron through ms milliseconds with synapse input
+ // if you don't have a good reason to do otherwise, keep ms between 0.1
+ // and 1.0
+ if (neuron->potential >= 30) {
+ neuron->potential = neuron->c;
+ neuron->recovery += neuron->d;
+ return;
+ }
+ float_t v = neuron->potential;
+ float_t u = neuron->recovery;
+ neuron->potential = v + ms * (0.04 * v * v + 5 * v + 140 - u + synapse);
+ neuron->recovery = u + ms * (neuron->a * (neuron->b * v - u));
+ return;
+}
+
+// XXX Note: if you change LOG_SQRT_FSCALE, you'll need to re-check the math
+// XXX to confirm that it won't overflow, as well as play with those
+// XXX constants when calculating `partial`.
+#define LOG_SQRT_FSCALE 10
+#define SQRT_FSCALE (1 << LOG_SQRT_FSCALE)
+#define FSCALE (SQRT_FSCALE * SQRT_FSCALE)
+
+static void base_i(ineuron_t *neuron) {
+ neuron->a_inv = 50;
+ neuron->b_inv = 5;
+ neuron->c = -65 * FSCALE;
+ neuron->d = 2 * FSCALE;
+ neuron->potential = neuron->recovery = 0;
+ neuron->scale = FSCALE;
+}
+
+void RS_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->d = 8 * FSCALE;
+}
+
+void IB_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->c = -55 * FSCALE;
+ neuron->d = 4;
+}
+
+void CH_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->c = -50 * FSCALE;
+}
+
+void FS_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->a_inv = 10;
+}
+
+void LTS_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->b_inv = 4;
+}
+
+void RZ_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->a_inv = 10;
+ neuron->b_inv = 4; // should be 1/0.26, we'll see if this is close enough
+}
+
+void TC_i(ineuron_t *neuron) {
+ base_i(neuron);
+ neuron->d = FSCALE / 20;
+ neuron->b_inv = 4;
+}
+
+void step_i(ineuron_t *neuron, fixed_t synapse, uint8_t fracms) {
+ // step a neuron by 2**(-fracms) milliseconds. synapse input must be scaled
+ // before being passed to this function.
+ if (neuron->potential >= 30 * FSCALE) {
+ neuron->potential = neuron->c;
+ neuron->recovery += neuron->d;
+ return;
+ }
+ fixed_t v = neuron->potential;
+ fixed_t u = neuron->recovery;
+ //fixed_t partial = (v / SQRT_FSCALE) / 5;
+ fixed_t partial = ((v >> LOG_SQRT_FSCALE) * 819) >> 12;
+ neuron->potential = v + ((partial * partial + 5 * v + 140 * FSCALE
+ - u + synapse) >> fracms);
+ //neuron->recovery = u + (((v / neuron->b_inv - u) / neuron->a_inv)
+ // >> fracms);
+ neuron->recovery = u + (((v - u * neuron->b_inv)
+ / (neuron->b_inv * neuron->a_inv)) >> fracms);
+ return;
+}
diff --git a/FIRMWARE/izhi.h b/FIRMWARE/izhi.h
new file mode 100644
index 0000000..5b2d117
--- /dev/null
+++ b/FIRMWARE/izhi.h
@@ -0,0 +1,39 @@
+#ifndef IZHI_H
+#define IZHI_H
+
+#include
+
+typedef float float_t;
+typedef struct {
+ float_t a, b, c, d;
+ float_t potential, recovery;
+} fneuron_t;
+
+void RS_f(fneuron_t *neuron);
+void IB_f(fneuron_t *neuron);
+void CH_f(fneuron_t *neuron);
+void FS_f(fneuron_t *neuron);
+void LTS_f(fneuron_t *neuron);
+void RZ_f(fneuron_t *neuron);
+void TC_f(fneuron_t *neuron);
+void step_f(fneuron_t *neuron, float_t synapse, float_t ms);
+
+
+typedef int32_t fixed_t;
+typedef struct {
+ // using 1/a, 1/b because a and b are small fractions
+ fixed_t a_inv, b_inv, c, d;
+ fixed_t potential, recovery;
+ fixed_t scale;
+} ineuron_t;
+
+void RS_i(ineuron_t *neuron);
+void IB_i(ineuron_t *neuron);
+void CH_i(ineuron_t *neuron);
+void FS_i(ineuron_t *neuron);
+void LTS_i(ineuron_t *neuron);
+void RZ_i(ineuron_t *neuron);
+void TC_i(ineuron_t *neuron);
+void step_i(ineuron_t *neuron, fixed_t synapse, uint8_t fracms);
+
+#endif // IZHI_H
diff --git a/FIRMWARE/izhi_demo.c b/FIRMWARE/izhi_demo.c
new file mode 100644
index 0000000..15a297d
--- /dev/null
+++ b/FIRMWARE/izhi_demo.c
@@ -0,0 +1,35 @@
+#include "HAL.h"
+#include "neuron.h"
+#include "izhi.h"
+
+int main(void)
+{
+ clock_setup();
+ gpio_setup();
+ tim_setup();
+ // this gives a main_tick roughly every 5.1ms, which makes the neuron
+ // trigger at a sensible rate on my setup
+ systick_setup(10);
+
+ // initialize neuron
+ ineuron_t neuron;
+ CH_i(&neuron);
+
+ for(;;)
+ {
+ if (main_tick)
+ {
+ main_tick = 0;
+ step_i(&neuron, 10 * neuron.scale, 3);
+ // have the LED in R, G, B for the rest, transition, spike regions
+ // respectively
+ if (neuron.potential < -60 * neuron.scale) {
+ setLED(1000, 0, 0);
+ } else if (neuron.potential < -40 * neuron.scale) {
+ setLED(0, 1000, 0);
+ } else {
+ setLED(0, 0, 1000);
+ }
+ }
+ }
+}