|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| `timescale 1ns / 1ps
|
|
|
| module tb_stdp;
|
|
|
| parameter NUM_NEURONS = 256;
|
| parameter NEURON_BITS = 8;
|
| parameter DATA_WIDTH = 16;
|
| parameter MAX_FANOUT = 32;
|
| parameter FANOUT_BITS = 5;
|
| parameter CONN_ADDR_BITS = 13;
|
| parameter CLK_PERIOD = 10;
|
|
|
| reg clk;
|
| reg rst_n;
|
| reg start;
|
| reg learn_enable;
|
| reg ext_valid;
|
| reg [NEURON_BITS-1:0] ext_neuron_id;
|
| reg signed [DATA_WIDTH-1:0] ext_current;
|
| reg conn_we;
|
| reg [NEURON_BITS-1:0] conn_src;
|
| reg [FANOUT_BITS-1:0] conn_slot;
|
| reg [NEURON_BITS-1:0] conn_target;
|
| reg signed [DATA_WIDTH-1:0] conn_weight;
|
|
|
| wire timestep_done;
|
| wire spike_out_valid;
|
| wire [NEURON_BITS-1:0] spike_out_id;
|
| wire [4:0] state_out;
|
| wire [31:0] total_spikes;
|
| wire [31:0] timestep_count;
|
|
|
| scalable_core_v2 #(
|
| .NUM_NEURONS (NUM_NEURONS),
|
| .NEURON_BITS (NEURON_BITS),
|
| .DATA_WIDTH (DATA_WIDTH),
|
| .MAX_FANOUT (MAX_FANOUT),
|
| .FANOUT_BITS (FANOUT_BITS),
|
| .CONN_ADDR_BITS(CONN_ADDR_BITS),
|
| .THRESHOLD (16'sd1000),
|
| .LEAK_RATE (16'sd3),
|
| .RESTING_POT (16'sd0),
|
| .REFRAC_CYCLES (2),
|
| .TRACE_MAX (8'd100),
|
| .TRACE_DECAY (8'd10),
|
| .LEARN_SHIFT (3),
|
| .WEIGHT_MAX (16'sd2000),
|
| .WEIGHT_MIN (16'sd0)
|
| ) dut (
|
| .clk (clk),
|
| .rst_n (rst_n),
|
| .start (start),
|
| .learn_enable (learn_enable),
|
| .graded_enable (1'b0),
|
| .dendritic_enable(1'b0),
|
| .ext_valid (ext_valid),
|
| .ext_neuron_id (ext_neuron_id),
|
| .ext_current (ext_current),
|
| .conn_we (conn_we),
|
| .conn_src (conn_src),
|
| .conn_slot (conn_slot),
|
| .conn_target (conn_target),
|
| .conn_weight (conn_weight),
|
| .conn_comp (2'd0),
|
| .prog_param_we (1'b0),
|
| .prog_param_neuron(8'd0),
|
| .prog_param_id (3'd0),
|
| .prog_param_value(16'sd0),
|
| .timestep_done (timestep_done),
|
| .spike_out_valid(spike_out_valid),
|
| .spike_out_id (spike_out_id),
|
| .spike_out_payload(),
|
| .state_out (state_out),
|
| .total_spikes (total_spikes),
|
| .timestep_count (timestep_count)
|
| );
|
|
|
| initial clk = 0;
|
| always #(CLK_PERIOD/2) clk = ~clk;
|
|
|
| task program_conn;
|
| input [NEURON_BITS-1:0] src;
|
| input [FANOUT_BITS-1:0] slot;
|
| input [NEURON_BITS-1:0] target;
|
| input signed [DATA_WIDTH-1:0] weight;
|
| begin
|
| @(posedge clk);
|
| conn_we <= 1;
|
| conn_src <= src;
|
| conn_slot <= slot;
|
| conn_target <= target;
|
| conn_weight <= weight;
|
| @(posedge clk);
|
| conn_we <= 0;
|
| @(posedge clk);
|
| end
|
| endtask
|
|
|
| task stimulate;
|
| input [NEURON_BITS-1:0] neuron;
|
| input signed [DATA_WIDTH-1:0] current;
|
| begin
|
| @(posedge clk);
|
| ext_valid <= 1;
|
| ext_neuron_id <= neuron;
|
| ext_current <= current;
|
| @(posedge clk);
|
| ext_valid <= 0;
|
| end
|
| endtask
|
|
|
| task run_timestep;
|
| begin
|
| @(posedge clk);
|
| start <= 1;
|
| @(posedge clk);
|
| start <= 0;
|
| wait(timestep_done);
|
| @(posedge clk);
|
| end
|
| endtask
|
|
|
|
|
| function signed [DATA_WIDTH-1:0] read_weight;
|
| input [NEURON_BITS-1:0] src;
|
| input [FANOUT_BITS-1:0] slot;
|
| reg [CONN_ADDR_BITS-1:0] addr;
|
| begin
|
| addr = {src, slot};
|
| read_weight = dut.weight_mem.mem[addr];
|
| end
|
| endfunction
|
|
|
| reg [7:0] spike_log [0:255];
|
| integer spike_count;
|
|
|
| always @(posedge clk) begin
|
| if (spike_out_valid && spike_count < 256) begin
|
| spike_log[spike_count] = spike_out_id;
|
| spike_count = spike_count + 1;
|
| end
|
| end
|
|
|
| reg signed [DATA_WIDTH-1:0] w_before, w_after;
|
| integer i;
|
| integer pass_count, fail_count;
|
|
|
| initial begin
|
| rst_n = 0;
|
| start = 0;
|
| learn_enable = 0;
|
| ext_valid = 0;
|
| conn_we = 0;
|
| conn_src = 0;
|
| conn_slot = 0;
|
| conn_target = 0;
|
| conn_weight = 0;
|
| ext_neuron_id = 0;
|
| ext_current = 0;
|
| spike_count = 0;
|
| pass_count = 0;
|
| fail_count = 0;
|
|
|
| #(CLK_PERIOD * 5);
|
| rst_n = 1;
|
| #(CLK_PERIOD * 3);
|
|
|
| $display("");
|
| $display("================================================================");
|
| $display(" STDP On-Chip Learning Test (Phase 7)");
|
| $display("================================================================");
|
|
|
|
|
|
|
|
|
| $display("");
|
| $display("--- TEST 1: Pre-before-Post → LTP ---");
|
|
|
|
|
| program_conn(8'd0, 5'd0, 8'd1, 16'sd500);
|
|
|
| program_conn(8'd1, 5'd0, 8'd2, 16'sd100);
|
|
|
| learn_enable = 1;
|
|
|
|
|
| stimulate(8'd0, 16'sd1200);
|
| spike_count = 0;
|
| run_timestep;
|
| $display(" TS1: N0 stimulated with 1200, spikes=%0d", spike_count);
|
|
|
| w_before = read_weight(8'd0, 5'd0);
|
| $display(" Weight N0→N1 before LTP: %0d", w_before);
|
|
|
|
|
| stimulate(8'd1, 16'sd1200);
|
| spike_count = 0;
|
| run_timestep;
|
| $display(" TS2: N1 stimulated with 1200, spikes=%0d", spike_count);
|
|
|
| w_after = read_weight(8'd0, 5'd0);
|
| $display(" Weight N0→N1 after LTP: %0d", w_after);
|
|
|
| if (w_after > w_before) begin
|
| $display(" PASS: Weight increased (%0d → %0d, +%0d)",
|
| w_before, w_after, w_after - w_before);
|
| pass_count = pass_count + 1;
|
| end else begin
|
| $display(" FAIL: Weight did not increase (%0d → %0d)",
|
| w_before, w_after);
|
| fail_count = fail_count + 1;
|
| end
|
|
|
|
|
|
|
| $display("");
|
| $display("--- TEST 2: Post-before-Pre → LTD ---");
|
|
|
| rst_n = 0;
|
| #(CLK_PERIOD * 3);
|
| rst_n = 1;
|
| #(CLK_PERIOD * 3);
|
| learn_enable = 1;
|
|
|
|
|
| program_conn(8'd10, 5'd0, 8'd11, 16'sd500);
|
|
|
|
|
| stimulate(8'd11, 16'sd1200);
|
| spike_count = 0;
|
| run_timestep;
|
| $display(" TS1: N11 (post) spiked first, spikes=%0d", spike_count);
|
|
|
| w_before = read_weight(8'd10, 5'd0);
|
| $display(" Weight N10→N11 before LTD: %0d", w_before);
|
|
|
|
|
| stimulate(8'd10, 16'sd1200);
|
| spike_count = 0;
|
| run_timestep;
|
| $display(" TS2: N10 (pre) spiked second, spikes=%0d", spike_count);
|
|
|
| w_after = read_weight(8'd10, 5'd0);
|
| $display(" Weight N10→N11 after LTD: %0d", w_after);
|
|
|
| if (w_after < w_before) begin
|
| $display(" PASS: Weight decreased (%0d → %0d, -%0d)",
|
| w_before, w_after, w_before - w_after);
|
| pass_count = pass_count + 1;
|
| end else begin
|
| $display(" FAIL: Weight did not decrease (%0d → %0d)",
|
| w_before, w_after);
|
| fail_count = fail_count + 1;
|
| end
|
|
|
|
|
|
|
| $display("");
|
| $display("--- TEST 3: Uncorrelated → No Change ---");
|
|
|
| rst_n = 0;
|
| #(CLK_PERIOD * 3);
|
| rst_n = 1;
|
| #(CLK_PERIOD * 3);
|
| learn_enable = 1;
|
|
|
| program_conn(8'd20, 5'd0, 8'd21, 16'sd500);
|
|
|
| w_before = read_weight(8'd20, 5'd0);
|
|
|
|
|
| for (i = 0; i < 5; i = i + 1) begin
|
| stimulate(8'd20, 16'sd1200);
|
| run_timestep;
|
| end
|
|
|
| w_after = read_weight(8'd20, 5'd0);
|
| $display(" Weight N20→N21: %0d → %0d", w_before, w_after);
|
|
|
| if (w_after == w_before) begin
|
| $display(" PASS: Weight unchanged (no correlated post activity)");
|
| pass_count = pass_count + 1;
|
| end else begin
|
| $display(" FAIL: Weight changed unexpectedly (%0d → %0d)",
|
| w_before, w_after);
|
| fail_count = fail_count + 1;
|
| end
|
|
|
|
|
|
|
| $display("");
|
| $display("--- TEST 4: Learning Disabled → No Change ---");
|
|
|
| rst_n = 0;
|
| #(CLK_PERIOD * 3);
|
| rst_n = 1;
|
| #(CLK_PERIOD * 3);
|
| learn_enable = 0;
|
|
|
| program_conn(8'd0, 5'd0, 8'd1, 16'sd500);
|
|
|
|
|
| stimulate(8'd0, 16'sd1200);
|
| run_timestep;
|
|
|
| w_before = read_weight(8'd0, 5'd0);
|
|
|
| stimulate(8'd1, 16'sd1200);
|
| run_timestep;
|
|
|
| w_after = read_weight(8'd0, 5'd0);
|
| $display(" Weight N0→N1: %0d → %0d (learn_enable=0)", w_before, w_after);
|
|
|
| if (w_after == w_before) begin
|
| $display(" PASS: Weight unchanged with learning disabled");
|
| pass_count = pass_count + 1;
|
| end else begin
|
| $display(" FAIL: Weight changed despite learning disabled");
|
| fail_count = fail_count + 1;
|
| end
|
|
|
| $display("");
|
| $display("--- TEST 5: Repeated Pre→Post Strengthens Over Time ---");
|
|
|
| rst_n = 0;
|
| #(CLK_PERIOD * 3);
|
| rst_n = 1;
|
| #(CLK_PERIOD * 3);
|
| learn_enable = 1;
|
|
|
| program_conn(8'd0, 5'd0, 8'd1, 16'sd200);
|
|
|
| w_before = read_weight(8'd0, 5'd0);
|
| $display(" Initial weight: %0d", w_before);
|
|
|
| for (i = 0; i < 10; i = i + 1) begin
|
| stimulate(8'd0, 16'sd1200);
|
| run_timestep;
|
|
|
| stimulate(8'd1, 16'sd1200);
|
| run_timestep;
|
|
|
| run_timestep;
|
| end
|
|
|
| w_after = read_weight(8'd0, 5'd0);
|
| $display(" After 10 pre→post cycles: %0d", w_after);
|
|
|
| if (w_after > w_before + 50) begin
|
| $display(" PASS: Significant strengthening (%0d → %0d, +%0d)",
|
| w_before, w_after, w_after - w_before);
|
| pass_count = pass_count + 1;
|
| end else begin
|
| $display(" FAIL: Insufficient strengthening (%0d → %0d)",
|
| w_before, w_after);
|
| fail_count = fail_count + 1;
|
| end
|
|
|
| $display("");
|
| $display("================================================================");
|
| $display(" STDP TEST RESULTS: %0d PASS, %0d FAIL", pass_count, fail_count);
|
| $display("================================================================");
|
| if (fail_count == 0)
|
| $display(" ALL TESTS PASSED");
|
| else
|
| $display(" SOME TESTS FAILED");
|
| $display("================================================================");
|
|
|
| #(CLK_PERIOD * 10);
|
| $finish;
|
| end
|
|
|
| initial begin
|
| #(CLK_PERIOD * 5_000_000);
|
| $display("TIMEOUT");
|
| $finish;
|
| end
|
|
|
| endmodule
|
|
|