blob: 6fec60cd38c6a0ae1d6b557ad98b4f170824c7ad [file] [log] [blame]
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Tamas Hubai
`default_nettype none
// neural network composed of an input layer,
// two fully connected hidden layers with a ReLU activation function
// and a fully connected output layer with a softmax activation function
module neural_network (
input clk,
input fp,
output fp_out,
input [`INPUT_SIZE*`NUM_WIDTH-1:0] a0_pk,
output [`OUTPUT_SIZE*`NUM_WIDTH-1:0] a3_pk,
input bp,
output bp_out,
input [`OUTPUT_SIZE*`NUM_WIDTH-1:0] g3_pk, // ground truth
input wu,
input [1:0] w_layer,
input [`INDEX_WIDTH-1:0] w_i,
input [`INDEX_WIDTH-1:0] w_j,
input [`NUM_WIDTH-1:0] w_in,
output [`NUM_WIDTH-1:0] w_out
);
wire [`NUM_WIDTH-1:0] a0[`INPUT_SIZE-1:0];
`UNPACK_ARRAY(`NUM_WIDTH, `INPUT_SIZE, a0, a0_pk)
wire [`NUM_WIDTH-1:0] a3[`OUTPUT_SIZE-1:0];
`PACK_ARRAY(`NUM_WIDTH, `OUTPUT_SIZE, a3, a3_pk)
wire [`NUM_WIDTH-1:0] g3[`OUTPUT_SIZE-1:0];
`UNPACK_ARRAY(`NUM_WIDTH, `OUTPUT_SIZE, g3, g3_pk)
generate genvar g; genvar h;
// synapses between input layer & hidden layer 1
wire bp_s01;
wire [`NUM_WIDTH-1:0] e1[`HIDDEN1_SIZE-1:0];
for (g=0; g<`INPUT_SIZE; g=g+1) begin:g_syn01_o
for (h=0; h<`HIDDEN1_SIZE; h=h+1) begin:g_syn01_i
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] zc;
wire [`NUM_WIDTH-1:0] tc; // ignored for input layer
wire wu_sel = (w_layer == 0) && (w_i == g) && (w_j == h);
wire [`NUM_WIDTH-1:0] w_out_r;
synapse i_syn01 (
.clk,
.fp,
.fp_out,
.a(a0[g]),
.zc,
.bp(bp_s01),
.bp_out,
.e(e1[h]),
.tc,
.wu(wu && wu_sel),
.w_in(w_in),
.w_out(w_out_r)
);
wire [`NUM_WIDTH-1:0] w_out_s = wu_sel ? w_out_r : {(`NUM_WIDTH){1'b0}};
wire [`NUM_WIDTH-1:0] w_out_a;
if (h==0) begin
assign w_out_a = w_out_s;
end else begin
assign w_out_a = g_syn01_i[h-1].w_out_a | w_out_s;
end
end
wire [`NUM_WIDTH-1:0] w_out_b;
if (g==0) begin
assign w_out_b = g_syn01_i[`HIDDEN1_SIZE-1].w_out_a;
end else begin
assign w_out_b = g_syn01_o[g-1].w_out_b | g_syn01_i[`HIDDEN1_SIZE-1].w_out_a;
end
end
wire [`NUM_WIDTH-1:0] w_out_c01 = g_syn01_o[`INPUT_SIZE-1].w_out_b;
wire [`NUM_WIDTH-1:0] z1[`HIDDEN1_SIZE-1:0];
for (g=0; g<`HIDDEN1_SIZE; g=g+1) begin:g_z1_o
for (h=0; h<`INPUT_SIZE; h=h+1) begin:g_z1_i
wire [`NUM_WIDTH-1:0] zc = g_syn01_o[h].g_syn01_i[g].zc;
wire [`NUM_WIDTH-1:0] z1s;
if (h==0) begin
assign z1s = zc;
end else begin
add_sat_comb i_add_z1 (
.a(g_z1_i[h-1].z1s),
.b(zc),
.res(z1s)
);
end
end
assign z1[g] = g_z1_i[`INPUT_SIZE-1].z1s;
end
wire fp_h1 = g_syn01_o[0].g_syn01_i[0].fp_out;
assign bp_out = g_syn01_o[0].g_syn01_i[0].bp_out;
// hidden layer 1
wire bp_h1;
wire [`NUM_WIDTH-1:0] a1[`HIDDEN1_SIZE-1:0];
wire [`NUM_WIDTH-1:0] t1[`HIDDEN1_SIZE-1:0];
for (g=0; g<`HIDDEN1_SIZE; g=g+1) begin:g_layer1
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] to_act;
wire [`NUM_WIDTH-1:0] from_act;
wire [`NUM_WIDTH-1:0] from_act_diff;
neuron i_neu_1 (
.clk,
.fp(fp_h1),
.fp_out,
.z(z1[g]),
.a(a1[g]),
.bp(bp_h1),
.bp_out,
.t(t1[g]),
.e(e1[g]),
.to_act,
.from_act,
.from_act_diff
);
leaky_relu_comb i_act_1 (
.x(to_act),
.res(from_act)
);
leaky_relu_diff_comb i_act_diff_1 (
.x(to_act),
.res(from_act_diff)
);
end
wire fp_s12 = g_layer1[0].fp_out;
assign bp_s01 = g_layer1[0].bp_out;
// synapses between hidden layers 1 & 2
wire bp_s12;
wire [`NUM_WIDTH-1:0] e2[`HIDDEN2_SIZE-1:0];
for (g=0; g<`HIDDEN1_SIZE; g=g+1) begin:g_syn12_o
for (h=0; h<`HIDDEN2_SIZE; h=h+1) begin:g_syn12_i
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] zc;
wire [`NUM_WIDTH-1:0] tc;
wire wu_sel = (w_layer == 1) && (w_i == g) && (w_j == h);
wire [`NUM_WIDTH-1:0] w_out_r;
synapse i_syn12 (
.clk,
.fp(fp_s12),
.fp_out,
.a(a1[g]),
.zc,
.bp(bp_s12),
.bp_out,
.e(e2[h]),
.tc,
.wu(wu && wu_sel),
.w_in(w_in),
.w_out(w_out_r)
);
wire [`NUM_WIDTH-1:0] w_out_s = wu_sel ? w_out_r : {(`NUM_WIDTH){1'b0}};
wire [`NUM_WIDTH-1:0] w_out_a;
if (h==0) begin
assign w_out_a = w_out_s;
end else begin
assign w_out_a = g_syn12_i[h-1].w_out_a | w_out_s;
end
end
wire [`NUM_WIDTH-1:0] w_out_b;
if (g==0) begin
assign w_out_b = g_syn12_i[`HIDDEN2_SIZE-1].w_out_a;
end else begin
assign w_out_b = g_syn12_o[g-1].w_out_b | g_syn12_i[`HIDDEN2_SIZE-1].w_out_a;
end
end
wire [`NUM_WIDTH-1:0] w_out_c12 = g_syn12_o[`HIDDEN1_SIZE-1].w_out_b;
wire [`NUM_WIDTH-1:0] z2[`HIDDEN2_SIZE-1:0];
for (g=0; g<`HIDDEN2_SIZE; g=g+1) begin:g_z2_o
for (h=0; h<`HIDDEN1_SIZE; h=h+1) begin:g_z2_i
wire [`NUM_WIDTH-1:0] zc = g_syn12_o[h].g_syn12_i[g].zc;
wire [`NUM_WIDTH-1:0] z2s;
if (h==0) begin
assign z2s = zc;
end else begin
add_sat_comb i_add_z2 (
.a(g_z2_i[h-1].z2s),
.b(zc),
.res(z2s)
);
end
end
assign z2[g] = g_z2_i[`HIDDEN1_SIZE-1].z2s;
end
for (g=0; g<`HIDDEN1_SIZE; g=g+1) begin:g_t1_o
for(h=0; h<`HIDDEN2_SIZE; h=h+1) begin:g_t1_i
wire [`NUM_WIDTH-1:0] tc = g_syn12_o[g].g_syn12_i[h].tc;
wire [`NUM_WIDTH-1:0] t1s;
if (h==0) begin
assign t1s = tc;
end else begin
add_sat_comb i_add_t1 (
.a(g_t1_i[h-1].t1s),
.b(tc),
.res(t1s)
);
end
end
assign t1[g] = g_t1_i[`HIDDEN2_SIZE-1].t1s;
end
wire fp_h2 = g_syn12_o[0].g_syn12_i[0].fp_out;
assign bp_h1 = g_syn12_o[0].g_syn12_i[0].bp_out;
// hidden layer 2
wire bp_h2;
wire [`NUM_WIDTH-1:0] a2[`HIDDEN2_SIZE-1:0];
wire [`NUM_WIDTH-1:0] t2[`HIDDEN2_SIZE-1:0];
for (g=0; g<`HIDDEN2_SIZE; g=g+1) begin:g_layer2
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] to_act;
wire [`NUM_WIDTH-1:0] from_act;
wire [`NUM_WIDTH-1:0] from_act_diff;
neuron i_neu_2 (
.clk,
.fp(fp_h2),
.fp_out,
.z(z2[g]),
.a(a2[g]),
.bp(bp_h2),
.bp_out,
.t(t2[g]),
.e(e2[g]),
.to_act,
.from_act,
.from_act_diff
);
leaky_relu_comb i_act_2 (
.x(to_act),
.res(from_act)
);
leaky_relu_diff_comb i_act_diff_2 (
.x(to_act),
.res(from_act_diff)
);
end
wire fp_s23 = g_layer2[0].fp_out;
assign bp_s12 = g_layer2[0].bp_out;
// synapses between hidden layer 2 & output layer
wire bp_s23;
wire [`NUM_WIDTH-1:0] e3[`OUTPUT_SIZE-1:0];
for (g=0; g<`HIDDEN2_SIZE; g=g+1) begin:g_syn23_o
for (h=0; h<`OUTPUT_SIZE; h=h+1) begin:g_syn23_i
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] zc;
wire [`NUM_WIDTH-1:0] tc;
wire wu_sel = (w_layer == 2) && (w_i == g) && (w_j == h);
wire [`NUM_WIDTH-1:0] w_out_r;
synapse i_syn23 (
.clk,
.fp(fp_s23),
.fp_out,
.a(a2[g]),
.zc,
.bp(bp_s23),
.bp_out,
.e(e3[h]),
.tc,
.wu(wu && wu_sel),
.w_in(w_in),
.w_out(w_out_r)
);
wire [`NUM_WIDTH-1:0] w_out_s = wu_sel ? w_out_r : {(`NUM_WIDTH){1'b0}};
wire [`NUM_WIDTH-1:0] w_out_a;
if (h==0) begin
assign w_out_a = w_out_s;
end else begin
assign w_out_a = g_syn23_i[h-1].w_out_a | w_out_s;
end
end
wire [`NUM_WIDTH-1:0] w_out_b;
if (g==0) begin
assign w_out_b = g_syn23_i[`OUTPUT_SIZE-1].w_out_a;
end else begin
assign w_out_b = g_syn23_o[g-1].w_out_b | g_syn23_i[`OUTPUT_SIZE-1].w_out_a;
end
end
wire [`NUM_WIDTH-1:0] w_out_c23 = g_syn23_o[`HIDDEN2_SIZE-1].w_out_b;
wire [`NUM_WIDTH-1:0] z3[`OUTPUT_SIZE-1:0];
for (g=0; g<`OUTPUT_SIZE; g=g+1) begin:g_z3_o
for (h=0; h<`HIDDEN2_SIZE; h=h+1) begin:g_z3_i
wire [`NUM_WIDTH-1:0] zc = g_syn23_o[h].g_syn23_i[g].zc;
wire [`NUM_WIDTH-1:0] z3s;
if (h==0) begin
assign z3s = zc;
end else begin
add_sat_comb i_add_z3 (
.a(g_z3_i[h-1].z3s),
.b(zc),
.res(z3s)
);
end
end
assign z3[g] = g_z3_i[`HIDDEN2_SIZE-1].z3s;
end
for (g=0; g<`HIDDEN2_SIZE; g=g+1) begin:g_t2_o
for(h=0; h<`OUTPUT_SIZE; h=h+1) begin:g_t2_i
wire [`NUM_WIDTH-1:0] tc = g_syn23_o[g].g_syn23_i[h].tc;
wire [`NUM_WIDTH-1:0] t2s;
if (h==0) begin
assign t2s = tc;
end else begin
add_sat_comb i_add_t2 (
.a(g_t2_i[h-1].t2s),
.b(tc),
.res(t2s)
);
end
end
assign t2[g] = g_t2_i[`OUTPUT_SIZE-1].t2s;
end
wire fp_h3 = g_syn23_o[0].g_syn23_i[0].fp_out;
assign bp_h2 = g_syn23_o[0].g_syn23_i[0].bp_out;
// output layer
wire [`NUM_WIDTH-1:0] t3[`OUTPUT_SIZE-1:0];
wire [`NUM_WIDTH-1:0] to_softmax[`OUTPUT_SIZE-1:0];
wire [`NUM_WIDTH-1:0] from_softmax[`OUTPUT_SIZE-1:0];
wire [`NUM_WIDTH-1:0] from_softmax_diff[`OUTPUT_SIZE-1:0];
for (g=0; g<`OUTPUT_SIZE; g=g+1) begin:g_layer3
wire fp_out;
wire bp_out;
wire [`NUM_WIDTH-1:0] to_act;
wire [`NUM_WIDTH-1:0] from_act;
wire [`NUM_WIDTH-1:0] from_act_diff;
neuron i_neu_3 (
.clk,
.fp(fp_h3),
.fp_out,
.z(z3[g]),
.a(a3[g]),
.bp,
.bp_out,
.t(t3[g]),
.e(e3[g]),
.to_act(to_softmax[g]),
.from_act(from_softmax[g]),
.from_act_diff(from_softmax_diff[g])
);
// feedback using ground truth
sub_sat_comb i_sub_fb (
.a(a3[g]),
.b(g3[g]),
.res(t3[g])
);
end
wire [`OUTPUT_SIZE*`NUM_WIDTH-1:0] to_softmax_pk;
`PACK_ARRAY(`NUM_WIDTH, `OUTPUT_SIZE, to_softmax, to_softmax_pk)
wire [`OUTPUT_SIZE*`NUM_WIDTH-1:0] from_softmax_pk;
`UNPACK_ARRAY(`NUM_WIDTH, `OUTPUT_SIZE, from_softmax, from_softmax_pk)
wire [`OUTPUT_SIZE*`NUM_WIDTH-1:0] from_softmax_diff_pk;
`UNPACK_ARRAY(`NUM_WIDTH, `OUTPUT_SIZE, from_softmax_diff, from_softmax_diff_pk)
approx_softmax_comb i_act_3 (
.x_pk(to_softmax_pk),
.res_pk(from_softmax_pk)
);
approx_softmax_diff_comb i_act_diff_3 (
.x_pk(to_softmax_pk),
.res_pk(from_softmax_diff_pk)
);
assign fp_out = g_layer3[0].fp_out;
assign bp_s23 = g_layer3[0].bp_out;
assign w_out = w_out_c01 | w_out_c12 | w_out_c23;
endgenerate
endmodule