Synapse_core.
v
// Copyright (C) 2016-2019 Université catholique de Louvain (UCLouvain), Belgium.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0/. The software, hardware and materials
// distributed under this License are provided in the hope that it will be useful
// on an as is basis, without warranties or conditions of any kind, either
// expressed or implied; without even the implied warranty of merchantability or
// fitness for a particular purpose. See the Solderpad Hardware License for more
// detailed permissions and limitations.
//------------------------------------------------------------------------------
//
// "synaptic_core.v" - ODIN synaptic core module
//
// Project: ODIN - An online-learning digital spiking neuromorphic processor
//
// Author: C. Frenkel, Université catholique de Louvain (UCLouvain), 04/2017
//
// Cite/paper: C. Frenkel, M. Lefebvre, J.-D. Legat and D. Bol, "A 0.086-mm² 12.7-pJ/SOP 64k-
Synapse 256-Neuron Online-Learning
// Digital Spiking Neuromorphic Processor in 28-nm CMOS," IEEE Transactions on
Biomedical Circuits and Systems,
// vol. 13, no. 1, pp. 145-158, 2019.
//
//------------------------------------------------------------------------------
module synaptic_core #(
parameter N = 256,
parameter M = 8
)(
// Global inputs ------------------------------------------
input wire RSTN_syncn,
input wire CLK,
// Inputs from SPI configuration registers ----------------
input wire SPI_GATE_ACTIVITY_sync,
input wire [ N-1:0] SPI_SYN_SIGN,
input wire SPI_UPDATE_UNMAPPED_SYN,
// Inputs from controller ---------------------------------
input wire [ 7:0] CTRL_PRE_EN,
input wire CTRL_BIST_REF,
input wire CTRL_SYNARRAY_WE,
input wire [ 12:0] CTRL_SYNARRAY_ADDR,
input wire CTRL_SYNARRAY_CS,
input wire [2*M-1:0] CTRL_PROG_DATA,
input wire [2*M-1:0] CTRL_SPI_ADDR,
// Inputs from neurons ------------------------------------
input wire [ N-1:0] NEUR_V_UP,
input wire [ N-1:0] NEUR_V_DOWN,
// Outputs ------------------------------------------------
output wire [ 31:0] SYNARRAY_RDATA,
output wire [ 31:0] SYNARRAY_WDATA,
output wire SYN_SIGN
);
// Internal regs and wires definitions
wire [ 31:0] SYNARRAY_WDATA_int;
wire [ N-1:0] NEUR_V_UP_int, NEUR_V_DOWN_int;
wire [ N-2:0] syn_sign_dummy;
genvar i;
// SDSP update logic
generate
for (i=0; i<8; i=i+1) begin
sdsp_update #(
.WIDTH(3)
) sdsp_update_gen (
// Inputs
// General
.SYN_PRE(CTRL_PRE_EN[i] & (SPI_UPDATE_UNMAPPED_SYN |
SYNARRAY_RDATA[(i<<2)+3])),
.SYN_BIST_REF(CTRL_BIST_REF),
// From neuron
.V_UP(NEUR_V_UP_int[i]),
.V_DOWN(NEUR_V_DOWN_int[i]),
// From SRAM
.WSYN_CURR(SYNARRAY_RDATA[(i<<2)+3:(i<<2)]),
// Output
.WSYN_NEW(SYNARRAY_WDATA_int[(i<<2)+3:(i<<2)])
);
end
endgenerate
assign NEUR_V_UP_int = NEUR_V_UP >> ({3'b0,CTRL_SYNARRAY_ADDR[4:0]} << 3);
assign NEUR_V_DOWN_int = NEUR_V_DOWN >> ({3'b0,CTRL_SYNARRAY_ADDR[4:0]} <<
3);
// Updated or configured weights to be written to the synaptic memory
generate
for (i=0; i<4; i=i+1) begin
assign SYNARRAY_WDATA[(i<<3)+7:(i<<3)] = SPI_GATE_ACTIVITY_sync
((i == CTRL_SPI_ADDR[14:13])
? ((CTRL_PROG_DATA[M-1:0] & ~CTRL_PROG_DATA[2*M-
1:M]) | (SYNARRAY_RDATA[(i<<3)+7:(i<<3)] & CTRL_PROG_DATA[2*M-1:M]))
: SYNARRAY_RDATA[(i<<3)+7:(i<<3)])
: SYNARRAY_WDATA_int[(i<<3)+7:(i<<3)];
end
endgenerate
// Synaptic memory wrapper
SRAM_8192x32_wrapper synarray_0 (
// Global inputs
.RSTN (RSTN_syncn),
.CK (CLK),
// Control and data inputs
.CS (CTRL_SYNARRAY_CS),
.WE (CTRL_SYNARRAY_WE),
.A (CTRL_SYNARRAY_ADDR),
.D (SYNARRAY_WDATA),
// Data output
.Q (SYNARRAY_RDATA)
);
assign {syn_sign_dummy,SYN_SIGN} = SPI_SYN_SIGN >> CTRL_SYNARRAY_ADDR[12:5];
endmodule
module SRAM_8192x32_wrapper (
input wire CK, // Clock
input wire RSTN, // Reset (active low)
input wire CS, // Chip Select
input wire WE, // Write Enable
input wire [12:0] A, // Address
input wire [31:0] D, // Data input
output reg [31:0] Q // Data output
);
reg [31:0] mem [0:8191];
// Load weights from memory file
initial begin
$readmemh("weights.mem", mem);
end
always @(posedge CK) begin
if (!RSTN)
Q <= 0;
else if (CS) begin
if (WE)
mem[A] <= D;
else
Q <= mem[A];
end
end
endmodule
aer_output.v
// Copyright (C) 2016-2019 Université catholique de Louvain (UCLouvain), Belgium.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0/. The software, hardware and materials
// distributed under this License are provided in the hope that it will be useful
// on an as is basis, without warranties or conditions of any kind, either
// expressed or implied; without even the implied warranty of merchantability or
// fitness for a particular purpose. See the Solderpad Hardware License for more
// detailed permissions and limitations.
//------------------------------------------------------------------------------
//
// "aer_out.v" - ODIN AER output link module
//
// Project: ODIN - An online-learning digital spiking neuromorphic processor
//
// Author: C. Frenkel, Université catholique de Louvain (UCLouvain), 04/2017
//
// Cite/paper: C. Frenkel, M. Lefebvre, J.-D. Legat and D. Bol, "A 0.086-mm² 12.7-pJ/SOP
64k-Synapse 256-Neuron Online-Learning
// Digital Spiking Neuromorphic Processor in 28-nm CMOS," IEEE Transactions on
Biomedical Circuits and Systems,
// vol. 13, no. 1, pp. 145-158, 2019.
//
//------------------------------------------------------------------------------
module aer_out #(
parameter N = 256,
parameter M = 8
)(
// Global input -----------------------------------
input wire CLK,
input wire RST,
// Inputs from SPI configuration latches ----------
input wire SPI_GATE_ACTIVITY_sync,
input wire SPI_OUT_AER_MONITOR_EN,
input wire [ M-1:0] SPI_MONITOR_NEUR_ADDR,
input wire [ M-1:0] SPI_MONITOR_SYN_ADDR,
input wire SPI_AER_SRC_CTRL_nNEUR,
// Neuron data inputs -----------------------------
input wire [ 14:0] NEUR_STATE_MONITOR,
input wire [ 6:0] NEUR_EVENT_OUT,
input wire CTRL_NEURMEM_WE,
input wire [ M-1:0] CTRL_NEURMEM_ADDR,
input wire CTRL_NEURMEM_CS,
// Synapse data inputs ----------------------------
input wire [ 31:0] SYNARRAY_WDATA,
input wire CTRL_SYNARRAY_WE,
input wire [ 12:0] CTRL_SYNARRAY_ADDR,
input wire CTRL_SYNARRAY_CS,
// Input from scheduler ---------------------------
input wire [ 12:0] SCHED_DATA_OUT,
// Input from controller --------------------------
input wire CTRL_AEROUT_POP_NEUR,
// Output to controller ---------------------------
output reg AEROUT_CTRL_BUSY,
// Output 8-bit AER link --------------------------
output reg [ M-1:0] AEROUT_ADDR,
output reg AEROUT_REQ,
input wire AEROUT_ACK
);
reg AEROUT_ACK_sync_int, AEROUT_ACK_sync, AEROUT_ACK_sync_del;
wire AEROUT_ACK_sync_negedge;
reg [ 7:0] neuron_state_monitor_samp;
reg [ 3:0] synapse_state_samp;
wire [ 31:0] synapse_state_int;
wire neuron_state_event, synapse_state_event, synapse_state_event_cond;
reg synapse_state_event_del;
wire monitored_neuron_popped;
reg do_neuron0_transfer, do_neuron1_transfer, do_synapse_transfer;
wire rst_activity;
assign rst_activity = RST || SPI_GATE_ACTIVITY_sync;
assign monitored_neuron_popped = CTRL_AEROUT_POP_NEUR &&
(SCHED_DATA_OUT[M-1:0] == SPI_MONITOR_NEUR_ADDR);
assign neuron_state_event = SPI_OUT_AER_MONITOR_EN && ((CTRL_NEURMEM_CS
&& CTRL_NEURMEM_WE && (CTRL_NEURMEM_ADDR == SPI_MONITOR_NEUR_ADDR)) ||
(monitored_neuron_popped && SPI_AER_SRC_CTRL_nNEUR));
assign synapse_state_event_cond = SPI_OUT_AER_MONITOR_EN &&
CTRL_SYNARRAY_CS && CTRL_SYNARRAY_WE && (CTRL_SYNARRAY_ADDR ==
{SPI_MONITOR_SYN_ADDR, SPI_MONITOR_NEUR_ADDR[7:3]});
assign synapse_state_event = synapse_state_event_cond && !neuron_state_event;
// Sync barrier
always @(posedge CLK, posedge rst_activity) begin
if (rst_activity) begin
AEROUT_ACK_sync_int <= 1'b0;
AEROUT_ACK_sync <= 1'b0;
AEROUT_ACK_sync_del <= 1'b0;
end
else begin
AEROUT_ACK_sync_int <= AEROUT_ACK;
AEROUT_ACK_sync <= AEROUT_ACK_sync_int;
AEROUT_ACK_sync_del <= AEROUT_ACK_sync;
end
end
assign AEROUT_ACK_sync_negedge = ~AEROUT_ACK_sync && AEROUT_ACK_sync_del;
// Register state bank
always @(posedge CLK) begin
if (neuron_state_event)
neuron_state_monitor_samp <= NEUR_STATE_MONITOR[7:0];
else
neuron_state_monitor_samp <= neuron_state_monitor_samp;
end
always @(posedge CLK) begin
if (synapse_state_event_cond)
synapse_state_samp <= synapse_state_int[3:0];
else
synapse_state_samp <= synapse_state_samp;
end
assign synapse_state_int = SYNARRAY_WDATA >> ({2'b0,SPI_MONITOR_NEUR_ADDR[2:0]}
<< 2);
// Output AER interface
always @(posedge CLK, posedge rst_activity) begin
if (rst_activity) begin
AEROUT_ADDR <= 8'b0;
AEROUT_REQ <= 1'b0;
AEROUT_CTRL_BUSY <= 1'b0;
do_neuron0_transfer <= 1'b0;
do_neuron1_transfer <= 1'b0;
do_synapse_transfer <= 1'b0;
synapse_state_event_del <= 1'b0;
end else if (~SPI_OUT_AER_MONITOR_EN) begin
do_neuron0_transfer <= 1'b0;
do_neuron1_transfer <= 1'b0;
do_synapse_transfer <= 1'b0;
synapse_state_event_del <= 1'b0;
if ((SPI_AER_SRC_CTRL_nNEUR ? CTRL_AEROUT_POP_NEUR : NEUR_EVENT_OUT[6])
&& ~AEROUT_ACK_sync) begin
AEROUT_ADDR <= SPI_AER_SRC_CTRL_nNEUR ? SCHED_DATA_OUT[M-1:0] :
CTRL_NEURMEM_ADDR;
AEROUT_REQ <= 1'b1;
AEROUT_CTRL_BUSY <= 1'b1;
end else if (AEROUT_ACK_sync) begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= 1'b0;
AEROUT_CTRL_BUSY <= 1'b1;
end else if (AEROUT_ACK_sync_negedge) begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= 1'b0;
AEROUT_CTRL_BUSY <= 1'b0;
end else begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= AEROUT_REQ;
AEROUT_CTRL_BUSY <= AEROUT_CTRL_BUSY;
end
end else begin
if (AEROUT_ACK_sync_negedge) begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= 1'b0;
AEROUT_CTRL_BUSY <= do_neuron0_transfer || synapse_state_event_del;
do_neuron0_transfer <= 1'b0;
do_neuron1_transfer <= do_neuron0_transfer;
do_synapse_transfer <= 1'b0;
synapse_state_event_del <= synapse_state_event_del;
end else if (AEROUT_ACK_sync) begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= 1'b0;
AEROUT_CTRL_BUSY <= 1'b1;
do_neuron0_transfer <= do_neuron0_transfer;
do_neuron1_transfer <= do_neuron1_transfer;
do_synapse_transfer <= do_synapse_transfer;
synapse_state_event_del <= synapse_state_event_del;
end else if ((neuron_state_event || synapse_state_event) && !AEROUT_REQ) begin
AEROUT_ADDR <= synapse_state_event ? {4'b1111,synapse_state_int[3:0]}
: {(SPI_AER_SRC_CTRL_nNEUR ?
monitored_neuron_popped : NEUR_EVENT_OUT[6]),NEUR_STATE_MONITOR[14:8]};
AEROUT_REQ <= 1'b1;
AEROUT_CTRL_BUSY <= 1'b1;
do_neuron0_transfer <= neuron_state_event;
do_neuron1_transfer <= 1'b0;
do_synapse_transfer <= synapse_state_event;
synapse_state_event_del <= synapse_state_event_cond && neuron_state_event;
end else if (do_neuron1_transfer && !AEROUT_REQ) begin
AEROUT_ADDR <= neuron_state_monitor_samp;
AEROUT_REQ <= 1'b1;
AEROUT_CTRL_BUSY <= 1'b1;
do_neuron0_transfer <= 1'b0;
do_neuron1_transfer <= 1'b1;
do_synapse_transfer <= 1'b0;
synapse_state_event_del <= synapse_state_event_del;
end else if (synapse_state_event_del && !AEROUT_REQ) begin
AEROUT_ADDR <= {4'b1111,synapse_state_samp};
AEROUT_REQ <= 1'b1;
AEROUT_CTRL_BUSY <= 1'b1;
do_neuron0_transfer <= 1'b0;
do_neuron1_transfer <= 1'b0;
do_synapse_transfer <= 1'b0;
synapse_state_event_del <= 1'b0;
end else begin
AEROUT_ADDR <= AEROUT_ADDR;
AEROUT_REQ <= AEROUT_REQ;
AEROUT_CTRL_BUSY <= AEROUT_CTRL_BUSY;
do_neuron0_transfer <= do_neuron0_transfer;
do_neuron1_transfer <= do_neuron1_transfer;
do_synapse_transfer <= do_synapse_transfer;
synapse_state_event_del <= synapse_state_event_del;
end
end
end
endmodule
sdsp_update.v
// Copyright (C) 2016-2019 Université catholique de Louvain (UCLouvain), Belgium.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0/. The software, hardware and materials
// distributed under this License are provided in the hope that it will be useful
// on an as is basis, without warranties or conditions of any kind, either
// expressed or implied; without even the implied warranty of merchantability or
// fitness for a particular purpose. See the Solderpad Hardware License for more
// detailed permissions and limitations.
//------------------------------------------------------------------------------
//
// "sdsp_update.v" - ODIN SDSP update logic module
//
// Project: ODIN - An online-learning digital spiking neuromorphic processor
//
// Author: C. Frenkel, Université catholique de Louvain (UCLouvain), 04/2017
//
// Cite/paper: C. Frenkel, M. Lefebvre, J.-D. Legat and D. Bol, "A 0.086-mm² 12.7-pJ/SOP 64k-
Synapse 256-Neuron Online-Learning
// Digital Spiking Neuromorphic Processor in 28-nm CMOS," IEEE Transactions on
Biomedical Circuits and Systems,
// vol. 13, no. 1, pp. 145-158, 2019.
//
//------------------------------------------------------------------------------
module sdsp_update #(
parameter WIDTH = 3
)(
// Inputs
// General
input wire SYN_PRE,
input wire SYN_BIST_REF,
// From neuron
input wire V_UP,
input wire V_DOWN,
// From SRAM
input wire [WIDTH:0] WSYN_CURR,
// Output
output reg [WIDTH:0] WSYN_NEW
);
wire w_lt_half;
wire do_up, do_down;
wire overflow;
assign w_lt_half = SYN_PRE & ~WSYN_CURR[WIDTH-1];
assign do_up = SYN_PRE & (SYN_BIST_REF ? ~w_lt_half : V_UP);
assign do_down = SYN_PRE & (SYN_BIST_REF ? w_lt_half : V_DOWN);
assign overflow = SYN_PRE & ((do_up && (&WSYN_CURR[WIDTH-1:0])) || (do_down &&
(~|WSYN_CURR[WIDTH-1:0])));
always @(*) begin
if (overflow) WSYN_NEW = WSYN_CURR;
else if (do_up) WSYN_NEW = WSYN_CURR + {{(WIDTH){1'b0}},1'b1};
else if (do_down) WSYN_NEW = WSYN_CURR - {{(WIDTH){1'b0}},1'b1};
else WSYN_NEW = WSYN_CURR;
end
endmodule
tb_synaptic_core.v
`timescale 1ns / 1ps
module tb_synaptic_core;
// Parameters
localparam N = 256;
localparam M = 8;
// Clock and Reset
reg CLK;
reg RSTN_syncn;
// SPI Configuration Inputs
reg SPI_GATE_ACTIVITY_sync;
reg [N-1:0] SPI_SYN_SIGN;
reg SPI_UPDATE_UNMAPPED_SYN;
// Controller Inputs
reg [7:0] CTRL_PRE_EN;
reg CTRL_BIST_REF;
reg CTRL_SYNARRAY_WE;
reg [12:0] CTRL_SYNARRAY_ADDR;
reg CTRL_SYNARRAY_CS;
reg [2*M-1:0] CTRL_PROG_DATA;
reg [2*M-1:0] CTRL_SPI_ADDR;
// Neuron Inputs
reg [N-1:0] NEUR_V_UP;
reg [N-1:0] NEUR_V_DOWN;
// Outputs
wire [31:0] SYNARRAY_RDATA;
wire [31:0] SYNARRAY_WDATA;
wire SYN_SIGN;
// Instantiate DUT
synaptic_core #(
.N(N),
.M(M)
) dut (
.RSTN_syncn(RSTN_syncn),
.CLK(CLK),
.SPI_GATE_ACTIVITY_sync(SPI_GATE_ACTIVITY_sync),
.SPI_SYN_SIGN(SPI_SYN_SIGN),
.SPI_UPDATE_UNMAPPED_SYN(SPI_UPDATE_UNMAPPED_SYN),
.CTRL_PRE_EN(CTRL_PRE_EN),
.CTRL_BIST_REF(CTRL_BIST_REF),
.CTRL_SYNARRAY_WE(CTRL_SYNARRAY_WE),
.CTRL_SYNARRAY_ADDR(CTRL_SYNARRAY_ADDR),
.CTRL_SYNARRAY_CS(CTRL_SYNARRAY_CS),
.CTRL_PROG_DATA(CTRL_PROG_DATA),
.CTRL_SPI_ADDR(CTRL_SPI_ADDR),
.NEUR_V_UP(NEUR_V_UP),
.NEUR_V_DOWN(NEUR_V_DOWN),
.SYNARRAY_RDATA(SYNARRAY_RDATA),
.SYNARRAY_WDATA(SYNARRAY_WDATA),
.SYN_SIGN(SYN_SIGN)
);
// Clock generation
always #5 CLK = ~CLK; // 100MHz
// Stimulus
initial begin
$dumpfile("tb_synaptic_core.vcd");
$dumpvars(0, tb_synaptic_core);
CLK = 0;
RSTN_syncn = 0;
SPI_GATE_ACTIVITY_sync = 0;
SPI_SYN_SIGN = 0;
SPI_UPDATE_UNMAPPED_SYN = 0;
CTRL_PRE_EN = 8'h00;
CTRL_BIST_REF = 0;
CTRL_SYNARRAY_WE = 0;
CTRL_SYNARRAY_ADDR = 13'd0;
CTRL_SYNARRAY_CS = 0;
CTRL_PROG_DATA = 0;
CTRL_SPI_ADDR = 0;
NEUR_V_UP = 0;
NEUR_V_DOWN = 0;
// Reset pulse
#20;
RSTN_syncn = 1;
// Preload configuration
#10;
CTRL_SYNARRAY_ADDR = 13'd1;
CTRL_SYNARRAY_CS = 1;
CTRL_SYNARRAY_WE = 0;
#10;
SPI_UPDATE_UNMAPPED_SYN = 1;
CTRL_PRE_EN = 8'hFF;
NEUR_V_UP = {256{1'b1}};
NEUR_V_DOWN = {256{1'b0}};
// Simulate SPI programming
#20;
SPI_GATE_ACTIVITY_sync = 1;
CTRL_PROG_DATA = 16'h00FF;
CTRL_SPI_ADDR = 16'h0000;
#20;
SPI_GATE_ACTIVITY_sync = 0;
CTRL_SYNARRAY_WE = 1;
#10;
CTRL_SYNARRAY_WE = 0;
#50;
$finish;
end
endmodule