Week 6 Challenges - zanzibarcircuit/ECE510 GitHub Wiki

Challenge 19 Binary LIF Neuron

This week I chose Challenge 19, which was to implement the LIF in SystemVerilog. Because this was relatively easy, I chose this opportunity to try and do less vibe coding and learn how to do the SystemVerilog a little more on my own.

Code

The algorithm works as follows. If a spike is received, it updates the current potential p to p(t) = lambdap(t-1) + 1; else p(t) = lambdap(t-1). In other words, the potential is incremented by 1 if a spike is received; otherwise it continues to reduce the potential by the leakage factor lambda. If the threshold exceeds some value, then it sends an output spike and updates the current potential back to its reset value. The following is my code:

`timescale 1ns/1ps

module lif_neuron #(
  parameter signed [7:0] LAMBDA      = 8'sb0001_0100,  // 1.25 in Q4.4
  parameter signed [7:0] THRESHOLD   = 8'sb0100_0000,  // 4.0 in Q4.4
  parameter signed [7:0] RESET_LEVEL = 8'sb0000_0000   // 0.0
) (
  input  logic clk,
  input  logic rst_n,     // active-low reset
  input  logic spike_in,
  output logic spike_out
);

  // State registers
  logic signed [7:0] P_reg, P_next;
  logic signed [7:0] integrated;

  // Fixed-point multiply: 8Γ—8 β†’ 16 bits (Q8.8)
  wire signed [15:0] mul_full  = P_reg * LAMBDA;
  // Downshift to Q4.4 by taking bits [11:4]
  wire signed [7:0]  leak_mult = mul_full[11:4];

  // Combinational next-state and integration
  always_comb begin
    integrated = leak_mult + spike_in;  // Q4.4 + integer β†’ Q4.4
    if (integrated >= THRESHOLD)
      P_next = RESET_LEVEL;
    else
      P_next = integrated;
  end

  // Sequential update and spike generation
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      P_reg    <= RESET_LEVEL;
      spike_out<= 1'b0;
    end else begin
      P_reg    <= P_next;
      // Fire when threshold crossed
      spike_out<= (P_next == RESET_LEVEL && integrated >= THRESHOLD);
    end
  end

endmodule

The only particularly interesting thing about this is the multiplication. We do a normal multiplication to create a Q8.8 number and then just take the middle 8 bits to make it Q4.4.

Test Bench

I parameterized a task for my test bench that allowed me to inject a spike every n cycles over a total of m cycles. My code for that is below:

  // send a 1 every `period` cycles, for `n_cycles` total
  task automatic periodic_spikes(input int period, input int n_cycles);
    bit saw_spike;

    int i;
    begin
      saw_spike = 0;

      // assume DUT's been reset already
      for (i = 1; i <= n_cycles; i++) begin
        // drive spike_in = 1 on exactly the 10th, 20th, 30th… cycle
        spike_in = (i % period == 0) ? 1'b1 : 1'b0;
        @(posedge clk);
        // optional: print what you saw
        if (spike_out) begin
          $display("[INFO] Periodic_spikes every %0d cycles saw spike at cycle %0d", period, i+1);
          saw_spike = 1;
        end
        
      end
      if (!saw_spike) begin
        $display("[INFO] Didn't see spike for spike at every %0d cycles after %0d total cycles", period, n_cycles);
      end
      
    end
  endtask

For my first case, I sent in a spike every cycle and saw spiking every 16 cycles. For the second test case, I sent in no spikes and saw no spikes on the output, as expected. The third test case I sent in a spike every 10 cycles and saw the first spike at my 55th cycle. For the fourth test case, I sent in a spike at just the 500th cycle over 1000 cycles and never saw an output spike, also expected. My simulation output is below:

image

I could change the frequency if I were to play with the reset level, threshold, and lambda values, and I played with that a little bit, but my baseline parameters were as follows:

  parameter signed [7:0] LAMBDA      = 8'sb0001_0100,  // 1.25 in Q4.4
  parameter signed [7:0] THRESHOLD   = 8'sb0100_0000,  // 4.0 in Q4.4
  parameter signed [7:0] RESET_LEVEL = 8'sb0000_0000   // 0.0

I'd be more interested to learn how you can implement typical neural networks in this environment. I figure you'd have to encode your spatial domain stuff into the time domain. Like for pixel intensity, maybe you do fewer spikes over a time period for low intensity and dense spikes for a high intensity value.