Week‐1 Challenge‐4 - AbhishekMusku/hw4aiml-am GitHub Wiki
Challenge #4 — LLM-Guided Design of a Spiking Neuron in SystemVerilog
Overview
Inspired by the paper “Designing Silicon Brains Using LLMs”, this challenge explored the design of a synthesizable Leaky Integrate-and-Fire (LIF) neuron using only natural language interactions with an LLM (ChatGPT). The objective was to co-design a neuron module through a sequence of prompts and response-refinement loops, while learning how much the LLM can contribute to RTL development.
LIF Neuron Design via LLM Prompts
The following section documents the design and verification of a Leaky Integrate-and-Fire (LIF) neuron using LLM-assisted prompts. Additional components such as ReLU and synapse modules will be added in subsequent sections of this page.
Prompt 1: Kickstarting the Design
Prompt:
“Can you write a Verilog module for a spiking leaky integrate and fire neuron? Please include a multibit current input and a refractory period.”
LLM Response:
// Initial version with basic LIF neuron behavior
// (multi-bit input, leak, refractory, threshold)
...
Analysis:
The LLM created a complete, synthesizable neuron with parameterization and correct behavior:
- Integration and leakage
- Refractory period with a countdown counter
- Spike generation at threshold
At this stage, neither overflow nor underflow were explicitly handled.
Prompt 2: Verilog Compatibility
Prompt:
“Does Verilog support logic type?”
Response Summary:
logic
is SystemVerilog, not Verilog-2001.- Use
reg
/wire
for older tools. - Fine to use
logic
in SystemVerilog-based flow.
Analysis:
Since our flow supports SystemVerilog, the original usage is acceptable.
Prompt 3: Overflow Handling
Prompt:
“Does the code take care of overflow of membrane_potential?”
Response Summary:
- Overflow could occur with values > 255 (8-bit).
- Suggested clamping or widening intermediates.
- Offered a clamped update logic to prevent wraparound.
Analysis:
Although not strictly necessary under current configuration (THRESHOLD = 100
, WIDTH = 8
), it was good insight for future-proofing.
Prompt 4: Underflow Check
Prompt:
“Can you just check membrane potential + current - leak before updating, and clamp it to threshold if needed?”
Observation:
This led to a conversation about both overflow and underflow. The LLM pointed out that subtractive leakage could cause underflow when membrane_potential < LEAK
, which results in unexpected values due to unsigned wraparound.
To fix this, the LLM updated the logic to:
if (membrane_potential > LEAK)
membrane_potential <= membrane_potential - LEAK + input_current;
else
membrane_potential <= input_current;
Analysis:
This was not included in the original response — only after prompting. It ensures that when the leak would exceed the current membrane value, we avoid a negative wraparound.
While not essential with THRESHOLD = 100
and small leak values, it reflects better defensive design.
Prompt 5: Spike Duration
Prompt:
“Does the code make sure spike is always 0 except when membrane_potential >= THRESHOLD?”
Response Summary:
- Spike is reset at the start of every clock cycle.
- Only raised to
1
for the spike event. - Guaranteed to be 1-cycle wide.
Analysis:
This avoids stale values on spike
, which could otherwise persist and confuse downstream logic.
Prompt 6: Final Clean Version (No Clamping)
Prompt:
“Give me the final code without clamping logic.”
Final Response:
module lif_neuron #(
parameter int WIDTH = 8,
parameter int THRESHOLD = 100,
parameter int LEAK = 2,
parameter int REF_PERIOD = 10
)(
input logic clk,
input logic rst_n,
input logic [WIDTH-1:0] input_current,
output logic spike
);
logic [WIDTH-1:0] membrane_potential;
logic [$clog2(REF_PERIOD+1)-1:0] ref_counter;
logic in_refractory;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
membrane_potential <= 0;
ref_counter <= 0;
in_refractory <= 0;
spike <= 0;
end else begin
spike <= 0; // Default to no spike
if (in_refractory) begin
if (ref_counter == REF_PERIOD - 1) begin
in_refractory <= 0;
ref_counter <= 0;
membrane_potential <= 0;
end else begin
ref_counter <= ref_counter + 1;
end
end else begin
if (membrane_potential > LEAK)
membrane_potential <= membrane_potential - LEAK + input_current;
else
membrane_potential <= input_current;
if (membrane_potential >= THRESHOLD) begin
spike <= 1;
in_refractory <= 1;
ref_counter <= 0;
membrane_potential <= 0;
end
end
end
end
endmodule
Analysis:
This is the final version used — it integrates the underflow fix, drops optional overflow clamp, and has lean logic suitable for synthesis and extension.
Visualization: LIF Neuron Behavior Over Time
The diagram below illustrates how the membrane potential of a Leaky Integrate-and-Fire (LIF) neuron evolves with time. It highlights:
- Integration of input current
- Spiking event when the threshold is crossed
- Reset and entry into the refractory period
Simulation and Testing
To verify the functionality of the LIF neuron module, I prompted Claude to generate a simple testbench. The testbench fed the neuron a repeated current pulse of magnitude 25 and monitored the spike
output across clock cycles. The clock and reset behavior were modeled using standard always blocks, and the simulation was run long enough to observe spiking behavior and refractory enforcement.
And a few relevant lines from the simulation output (Transcript_LIF_Neuron
):
# Test Case 2: Threshold crossing - Testing spike generation
# Time= 185000, Current= 30, Membrane= 0, Spike=0, Refractory=1, RefCounter= 8
# Time= 195000, Current= 30, Membrane= 0, Spike=0, Refractory=1, RefCounter= 9
# Time= 205000, Current= 30, Membrane= 0, Spike=0, Refractory=0, RefCounter= 0
# Time= 215000, Current= 30, Membrane= 30, Spike=0, Refractory=0, RefCounter= 0
# Time= 225000, Current= 30, Membrane= 58, Spike=0, Refractory=0, RefCounter= 0
# Time= 235000, Current= 30, Membrane= 86, Spike=0, Refractory=0, RefCounter= 0
# Time= 245000, Current= 30, Membrane=114, Spike=0, Refractory=0, RefCounter= 0
# Time= 255000, Current= 30, Membrane= 0, Spike=1, Refractory=1, RefCounter= 0
...
This confirms the following:
- The neuron correctly accumulates membrane potential over time due to the continuous
input_current
. - A spike is triggered once the threshold is reached (
THRESHOLD = 100
), visible at Time = 255000. - The
spike
returns to 0 immediately after firing and remains suppressed during the refractory period, verifying correct timing logic.
While the testbench confirmed the core behavior (integration, spiking, refractory), it didn’t dump internal signals like membrane_potential
, and it focused only on one type of stimulus. This was sufficient for basic validation but could be extended in future tests to cover edge cases and visualize membrane dynamics in more detail.
Simple 2-Layer Network
To extend the spiking neuron model into a small network, we used ChatGPT iteratively to build a simple 2-layer SNN architecture where input spikes are weighted and routed into an output layer of LIF neurons. Below, we document the key prompts, the generated responses, and our analysis.
Prompt 1
"Can you create a 2-layer spiking neural network in SystemVerilog using the LIF neuron we built earlier?"
Response Summary
The model generated a spiking_nn_2layer
module with:
- Parameterizable
N_INPUT
,N_OUTPUT
, andWIDTH
- A 2D array of
synapse_weights
- Accumulator
output_currents
for the second layer - Output layer composed of LIF neurons
Analysis
- Correctly captured the architecture as fully connected
- Reused the
lif_neuron
module from earlier - Treated input layer as external spike sources, not instantiated neurons
Prompt 2
"Explain how this spiking_nn_2layer module works."
Response Summary
The LLM broke it down step-by-step:
- Input spikes are received as a
logic [N_INPUT-1:0]
vector - For each spiking input neuron, its weight to each output is added to
output_currents
- These currents are fed to instantiated
lif_neuron
modules - Each neuron spikes based on LIF rules
Analysis
- Excellent explanation of conditional addition
- Clarified that this implements weighted spike routing
- Interpreted spike-based transmission as a binary-gated weight application
Prompt 3
"Is this really a 2-layer network if there are no neurons in the input layer?"
Response Summary
The LLM clarified:
- Only the output layer contains instantiated neurons
- The input layer is abstract — just a set of spike sources (e.g., from a testbench or external sensors)
- For a full 2-layer network, we’d need to instantiate
lif_neuron
modules in the input layer too
Analysis
- Provided important architectural clarification
- Distinguished between abstract spike sources vs real neurons
- Prompt helped correct a possible misunderstanding in design hierarchy
Prompt 4
"Why use temporary variables like output_currents before feeding them to the neurons?"
Response Summary
The LLM listed several reasons:
- Allows accumulation from multiple inputs per output
- Cleanly separates spike routing logic from neuron dynamics
- Avoids overwriting issues or logic entanglement
Example Snippet
if (input_spikes[i]) begin
output_currents[j] += synapse_weights[i][j];
end
Analysis
- Justified a good design decision
- Encouraged modular thinking
- Helped highlight hardware timing concerns in SNNs
Prompt 5
"Can you modify the design so that I can write to the synapse weights using an external interface? I want to be able to specify the input-output index and the value, kind of like memory-mapped SPI."
Response Summary
The LLM extended the design by:
- Adding control inputs:
write_en
,write_data
,write_row
,write_col
- Writing the given value to the indexed location in the
synapse_weights
array - Preserving all previous SNN behavior (spike routing, LIF dynamics)
Example Snippet
if (write_en) begin
synapse_weights[write_row][write_col] <= write_data;
end
Analysis
- Enabled real-time reconfiguration of weights
- Mimics a memory-mapped peripheral interface
- Makes the design more hardware-realistic (usable for training, learning, or dynamic modulation)
- Major usability boost — this version can now be integrated with a microcontroller or learning algorithm
spiking_nn_2layer
Final Design: The final SystemVerilog module included:
- Fully connected weighted spike routing
- Output layer of LIF neurons
- Runtime-writeable synapse weights via external control interface
- Combinational spike accumulation + clean modular separation
Reflections
The LLM was consistently helpful in converting architectural intent into HDL. Prompt chaining was crucial — each stage unlocked new capabilities:
- From fixed connectivity to parameterized
- From hardcoded weights to externally writable ones
- From abstract inputs to neuron-layered discussions However, while the LLM produced clean and modular code, it occasionally:
- It tends to oversimplify terms like “2-layer” unless specified
- It can produce syntactically correct code that needs validation (e.g., timing, synthesis constraints)
Careful analysis and follow-ups were essential to avoid misinterpretation, especially around hardware structure and biological naming conventions.
Simulation Analysis
To validate the spiking_nn_2layer
module, a testbench was used that performs the following:
- Programs synapse weights using the external interface.
- Applies controlled spike inputs on different input channels.
- Monitors output spike behavior from the LIF neurons.
The simulation demonstrates proper interaction across all major components:
- Weight Programming via Interface
The testbench initializes synaptic weights dynamically:
W[0][0] = 50
W[1][0] = 60
W[2][0] = 70
These weights are written using write_row
, write_col
, write_data
, and write_en
.
-
Input Spike Propagation
Inputs are applied at various times (input_spikes = 3'b001
, etc.), and their effects are accurately integrated by the output neuron. -
Output Spiking Behavior Matches LIF Model
The neuron does not spike immediately — it requires multiple weighted spikes to accumulate current. The firing happens after expected delays, with the output resetting due to the refractory period.
Representative Transcript Snippet:
# Time: 200
input_spikes = 3'b001
output_spikes = 0
output_currents[0] = 50
# Time: 220
input_spikes = 3'b010
output_spikes = 0
output_currents[0] = 110
# Time: 240
input_spikes = 3'b000
output_spikes = 1
membrane_potential = 110 -> 0 (fired and reset)
This shows that:
- Two inputs fire sequentially: one at time 200 and one at 220.
- Their respective weights (50 and 60) accumulate in
output_currents[0]
. - At time 240, the output neuron spikes — evidence of correct thresholding, membrane integration, and reset logic.
Summary
The simulation confirms:
- Correct configuration of synapse weights
- Proper summation of weighted inputs into LIF dynamics
- Expected neuron firing behavior once the threshold is met
The evidence confirms that the interface, spike propagation, and neuron logic all function coherently. The design is reliable for simple 2-layer SNN modeling and ready for scale-up to more complex topologies or learning rules.
SPI & TOP Module (LLM-Aided Design)
This section documents the iterative design of the SPI interface and TOP Module, based entirely on actual prompts and responses exchanged with the LLM. Unlike the flawed version in the reference paper, this design emphasizes synthesizability, correct protocol behavior (SPI Mode 0), and proper integration with a 2-layer spiking neuron array.
Prompt 1
User:
Now I need you to design an SPI interface to communicate with the network module above. Please don’t copy the SPI module from paper directly; it has both design, logical and syntax errors. Use it only as a reference.
LLM Response (Summary):
The assistant generated a spi_interface
module with:
- Parameterizable
ADDR_WIDTH
andDATA_WIDTH
- Clean 3-state FSM:
IDLE
,RECEIVE
,PROCESS
- Shift register for capturing 16-bit command frames (8-bit address + 8-bit data)
- Read-back support via
data_in → spi_miso
- Edge detection for
spi_clk
andspi_cs_n
- Single-cycle
write_en
pulse
Analysis:
- Major improvement over the paper: edge detection implemented using
spi_clk_prev
andspi_cs_n_prev
- Correct FSM separation (sequential + combinational)
- Proper SPI timing for Mode 0
- Domain synchronization to
clk
not formally addressed
Prompt 2
User:
Okay now explain how does your SPI work, and on what edge are you doing what. You can use a walkthrough example. Also include concepts of SPI as I forgot them long ago. Then the state machine.
LLM Response (Summary):
Detailed breakdown of SPI concepts and protocol:
- SPI Mode 0 explanation
- Sample
MOSI
on rising edge, driveMISO
on falling edge - Example of writing 0x42 to address 0x10
- FSM transition diagram and ASCII waveform
Analysis:
- Accurate Mode 0 timing
- Explained “sampling vs shifting” clearly
- Great documentation aid
- Minor clarification needed on
write_en
cycle
Prompt 3
User:
I understood the SPI module. Now explain how the top module works — how are neuron, 2-layer network, and everything connected. Where are values generated, how are they passed and why. Explain everything.
LLM Response (Summary):
Described integration in spiking_nn_top
:
spi_interface
connected to register file- Parameters and weights mapped via address decoding
- Weights reshaped from flat to 2D and passed to
spiking_nn_2layer
Analysis:
- Modular register map
- Flat → 2D weight conversion clean and scalable
- Full SPI-driven configurability
- No register address range checking
Technical Summary of the Final Design
spi_interface.sv
- FSM:
IDLE → RECEIVE → PROCESS
- Shift register captures [ADDR, DATA]
write_en
one-cycle pulse in PROCESSdata_in
drivesspi_miso
during readback
spiking_nn_top.sv
- Maps:
- 0x00 → THRESHOLD
- 0x01 → LEAK_RATE
- 0x02 → REF_PERIOD
- 0x10+ → WEIGHTS
- Reshapes weights from 1D to 2D
- Parameters passed to neuron network
SPI Communication Diagram
┌──────────────────────┐
│ SPI Master │
│ (e.g., MCU / Host) │
└─────────┬────────────┘
│
┌───────────────▼────────────────┐
│ spiking_nn_top │
│ ┌────────────────────────────┐ │
│ │ spi_interface │ │
│ │ │ │
│ │ SCLK ◄────────────────┐ │ │
│ │ MOSI ◄────────────────┘ │ │
│ │ MISO ─────────────────► │ │
│ │ CS_N ◄────────────────┐ │ │
│ └───────┬──────────────────┘ │
│ ▼ ▼
│ ┌──────────────────────────────┐
│ │ Register File + MUX │◄────── Internal bus
│ │ - THRESHOLD │
│ │ - LEAK_RATE │
│ │ - REF_PERIOD │
│ │ - WEIGHTS (Flat Array) │
│ └────────────┬─────────────────┘
│ ▼
│ ┌──────────────────────────────┐
│ │ 2-Layer Spiking NN Core │
│ └──────────────────────────────┘
└────────────────────────────────┘
📎 Transcript Evidence:
# Test Case 3: Single input spike
# Time=7375000: Output spikes=001
# Time=7385000: Output spikes=010
...
# Time=7445000: Output spikes=100
# Test Case 4: Multiple input spikes
# Time=8075000: Output spikes=111
# Time=8205000: Output spikes=111
Analysis
- The SPI interface correctly programmed all neuron parameters and weights before stimulus application.
- Neuron dynamics are clearly visible:
- In Test Case 3, a single input spike causes one neuron to spike at a time.
- In Test Case 4, multiple simultaneous inputs trigger all output neurons.
- Staggered spikes in the logs suggest proper threshold accumulation and refractory behavior.
- Consistent multi-cycle output spikes (like
111
persisting across time steps) confirm sustained input effect.
Conclusion
This simulation demonstrates that the full path — from SPI-based configuration to spiking output — was functionally correct. The network responds dynamically to varied input, validating the effectiveness of LLM-assisted design for complex HDL systems.
Reflections
What Went Well
- SPI FSM and shift logic were cleanly scaffolded by LLM
- Mode 0 behavior was correctly implemented after prompting
- Flat-to-2D weights allowed scalable design
Challenges
- Needed clarification on domain synchronization and edge timing
- Had to manually ensure
write_en
was pulsed - Read path needed proper indexing for
bit_counter
Takeaway
The LLM worked great as a co-designer when given precise, protocol-level prompts. While not always perfect, it significantly accelerated the design process and offered better structure than the original flawed SPI in the reference paper.
ReLU Neuron Hardware Design (vs. LIF)
This secton explores replacing a Leaky Integrate-and-Fire (LIF) neuron with a Rectified Linear Unit (ReLU) neuron in a synthesizable hardware implementation, along with a comparison of their properties, use cases, and trade-offs.
ReLU Neuron: Hardware Implementation
We designed a simple synthesizable ReLU neuron module in SystemVerilog. The ReLU neuron outputs the input value if it is positive; otherwise, it outputs zero. This is commonly expressed as:
output = max(0, input)
relu_neuron.sv
SystemVerilog Module: module relu_neuron #(
parameter DATA_WIDTH = 16
)(
input logic clk,
input logic rst_n,
input logic signed [DATA_WIDTH-1:0] input_val,
input logic input_valid,
output logic signed [DATA_WIDTH-1:0] output_val,
output logic output_valid
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
output_val <= '0;
output_valid <= 1'b0;
end else if (input_valid) begin
output_val <= (input_val > 0) ? input_val : '0;
output_valid <= 1'b1;
end else begin
output_valid <= 1'b0;
end
end
endmodule
ReLU as a replacement to LIF in the design
Replacing LIF with ReLU would:
- Turn the system into a non-spiking feedforward neural network.
- Require removing/replacing all time-domain logic (accumulators, refractory filters).
- Eliminate event-driven sparsity, increasing power and data bandwidth.
- Be easier to implement and highly parallelizable (ideal for FPGAs). So the idea is viable if the goal shifts from neuromorphic inference to a more conventional neural net accelerator.
LIF vs. ReLU: Summary Comparison
This table summarizes the key differences between Leaky Integrate-and-Fire (LIF) and Rectified Linear Unit (ReLU) neurons from both algorithmic and hardware design perspectives.
Aspect | LIF Neuron | ReLU Neuron |
---|---|---|
Domain | Spiking Neural Networks (SNNs) | Artificial Neural Networks (ANNs) |
Output | Binary spike (0 or 1) | Real-valued: max(0, input) |
Stateful? | Yes — membrane potential with decay | No — stateless |
Time-dependent? | Yes — integrates over time | No — instantaneous |
Power Efficiency | High — sparse/event-driven | Moderate — dense compute |
Hardware Cost | Higher — accumulators, decay logic | Lower — single comparator/MUX |
Training | Unsupervised or surrogate gradients | Supervised (backpropagation) |
Biological Realism | High | Low |
Synthesis Friendly | Moderate | Very high |
Use Cases | Low-power edge AI, neuromorphic research | CNNs, DNNs, hardware inference accelerators |
Comparison of Spiking Neuron Array Components
Between ChatGPT (Paper) Versions and User Versions
This section provides a detailed comparison of four major modules from the "Designing Silicon Brains using LLM" paper with revised versions implemented in this challenge. The analysis evaluates functionality, synthesis-friendliness, correctness, and improvements.
1. LIF Neuron
Functionality
- ChatGPT: Basic LIF neuron with programmable threshold, leak, and refractory period. Resets spike within a single clocked block.
- User: Uses FSM-like design, clean state separation, more accurate modeling of refractory behavior.
Synthesis-Friendliness
- ChatGPT: Syntactically correct but lacks clarity in handling unsigned math.
- User: Clear
logic
typing and modular design suited for synthesis.
Correctness
- ChatGPT: Logic errors around pulse resetting and wraparound risks.
- User: Correct and verifiable neuron behavior.
2. Neuron Network
Functionality
- ChatGPT: Fully connected 2-layer network using flattened arrays.
- User: Parametric, modular SystemVerilog implementation with scoped generates.
Synthesis-Friendliness
- ChatGPT: Misuses 2D ports, uses
initial
blocks. - User: Clean and reusable code, no
initial
blocks, synthesizable logic.
Correctness
- ChatGPT: Error-prone indexing and bad 2D-to-1D mapping.
- User: Clear spike-weight accumulation and correct modular behavior.
Improvements to ChatGPT
- Flatten 2D arrays properly
- Use generate blocks
- Avoid
initial
blocks - Refactor for clarity
3. SPI Interface
Functionality
- ChatGPT: SPI slave with FSM and shift register, but ignores
sclk
. - User: Fully SPI-compliant slave module using
sclk
,cs
, and correct bit sampling.
Synthesis-Friendliness
- ChatGPT: Uses
typedef enum
in.v
file, multiply driven nets. - User: Uses clean state management, edge detection, and
valid/rw
control.
Correctness
- ChatGPT: Timing issues and protocol violations.
- User: Meets timing and protocol needs.
Improvements to ChatGPT
- Use
sclk
for SPI FSM - Remove SystemVerilog constructs from
.v
- Fix write_enable driving
- Add handshake control
4. Top Module
Functionality
- ChatGPT: Instantiates modules but lacks decoding and state control.
- User: Adds address decoding, configuration gating, and FSM for inference.
Synthesis-Friendliness
- ChatGPT: Flat structure, global signal issues.
- User: Modular, FSM-driven, and memory-mapped.
Correctness
- ChatGPT: No isolation between config and inference.
- User: Safe config loading, scalable design.
Improvements to ChatGPT
- Add address decoding
- Gate config with FSM
- Separate config/infer phases
Summary Table
Component | User Version Strengths | ChatGPT Fix Suggestions |
---|---|---|
LIF Neuron | Structured FSM, proper refractory logic | Fix unsigned math, logic typing |
Network | Parametric and modular | Flatten ports, avoid initial |
SPI | True SPI, correct FSM and bit timing | Use sclk , fix FSM style |
Top | Address decoding, gated config logic | Add FSM, phase separation |
LLM Acknowledgment and Code Access
This work was developed with the assistance of large language models (LLMs), specifically ChatGPT and Claude, which were used for iterative prompt-based hardware design. These models helped generate baseline code, guided debugging, and enabled improvements through conversational refinement.
All code related to the Spiking Neural Network, including:
- SystemVerilog modules
- ChatGPT and Claude prompt transcripts
- Simulation outputs and testbenches
…can be found on the GitHub repository under the Week 1 directory.