```////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2020 Homework 2 -- SOLUTION
//

/// Assignment  https://www.ece.lsu.edu/koppel/v/2020/hw02.pdf

`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
/// Modify
///
//
//     [✔] nn4x4b must instantiate exactly four nn1x4b modules.
//     [✔] nn1x4b must instantiate exactly two nn1x2 modules.
//
//     [✔] Module must be synthesizable. Use command: genus -files syn.tcl
//
//     [✔] Don't assume any particular parameter value.
//     [✔] Pay attention to port widths. Do not make them larger than needed.
//
//     [✔] Code must be written clearly.

module nn4x4
#( int wa = 10, ww = 5 )
( output uwire [wa-1:0] ao[4],
input uwire [wa-1:0] ai[4],
input uwire [ww-1:0] wht[4][4] );

/// DO NOT MODIFY THIS ROUTINE.

assign ao[0] = ai[0] * wht[0][0] + ai[1] * wht[0][1]
+ ai[2] * wht[0][2] + ai[3] * wht[0][3];

assign ao[1] = ai[0] * wht[1][0] + ai[1] * wht[1][1]
+ ai[2] * wht[1][2] + ai[3] * wht[1][3];

assign ao[2] = ai[0] * wht[2][0] + ai[1] * wht[2][1]
+ ai[2] * wht[2][2] + ai[3] * wht[2][3];

assign ao[3] = ai[0] * wht[3][0] + ai[1] * wht[3][1]
+ ai[2] * wht[3][2] + ai[3] * wht[3][3];

endmodule

module nn4x4b
#( int wa = 10, ww = 5 )
( output uwire [wa-1:0] ao[4],
input uwire [wa-1:0] ai[4],
input uwire [ww-1:0] wht[4][4] );

/// SOLUTION

nn1x4b #(wa,ww) n0( ao[0], ai, wht[0] );
nn1x4b #(wa,ww) n1( ao[1], ai, wht[1] );
nn1x4b #(wa,ww) n2( ao[2], ai, wht[2] );
nn1x4b #(wa,ww) n3( ao[3], ai, wht[3] );

endmodule

module nn1x4b
#( int wa = 10, ww = 5 )
( output uwire [wa-1:0] ao,
input uwire [wa-1:0] ai[4],
input uwire [ww-1:0] wht[4] );

/// SOLUTION

uwire [wa-1:0] aoa, aob;
nn1x2b #(wa,ww) n0(aoa, ai[0:1], wht[0:1] );
nn1x2b #(wa,ww) n1(aob, ai[2:3], wht[2:3] );
assign ao = aoa + aob;

endmodule

module nn1x2b
#( int wa = 10, ww = 5 )
( output uwire [wa-1:0] ao,
input uwire [wa-1:0] ai[2],
input uwire [ww-1:0] wht[2] );

/// SOLUTION

assign ao = ai[0] * wht[0] + ai[1] * wht[1];

endmodule

//////////////////////////////////////////////////////////////////////////////
/// Testbench Code

module nnOxI
#( int no = 4, ni = 4, wa = 10, ww = 5 )
( output logic [wa-1:0] ao[no],
input uwire [wa-1:0] ai[ni],
input uwire [ww-1:0] wht[no][ni] );

/// DO NOT MODIFY THIS ROUTINE.

always_comb
for ( int o = 0;  o < no;  o++ ) begin
ao[o] = 0;
for ( int i=0; i<ni; i++ ) ao[o] += ai[i] * wht[o][i];
end

endmodule

module testbench;

localparam int wa = 16;
localparam int ww = 8;
localparam int ni = 4;  // Number of input neurons.
localparam int no = 4;  // Number of output neurons.
localparam int nmut = 3;

localparam int ntests = 10;

logic [wa-1:0] ai[ni];
uwire [wa-1:0] ao[nmut][no];
logic [ww-1:0] wht[no][ni];

string mname[] = { "Behav", "Flat", "Sol" };

typedef struct { string name; int no, ni; } Test_Set;
Test_Set ts[] = '{ '{ "n12", 1, 2 }, '{ "n14", 1, 4 }, '{ "n44", 4, 4 } };

nnOxI #(no,ni,wa,ww) nn1i(ao[0],ai,wht);
nn4x4 #(wa,ww) nn2i(ao[1],ai,wht);
nn4x4b #(wa,ww) nn3i(ao[2],ai,wht);

initial begin

automatic int mut = 2;
automatic string test_summary = "";

\$write("Testing module %s\n", mname[mut]);

foreach ( ts[ti] ) begin

automatic Test_Set tinfo = ts[ti];
automatic int n_err = 0;

\$write("\n** Starting test set %s  (%0d outputs, %0d inputs) **\n",
tinfo.name, tinfo.no, tinfo.ni );

for ( int tnum=0; tnum < ntests;  tnum++ ) begin

for ( int io=0; io<no; io++ )
for ( int ii=0; ii<ni; ii++ )
wht[io][ii] = io < tinfo.no && ii < tinfo.ni ? {\$random} : 0;

for ( int ii=0; ii<ni; ii++ )
ai[ii] = ii < tinfo.ni ? {\$random} : 0;

#1;

for ( int io=0; io<tinfo.no; io++ ) begin

if ( ao[0][io] !== ao[mut][io] ) begin
n_err++;
if ( n_err < 4 )
\$write
("Error test # %0d, output %0d: %0d != %0d (correct)\n",
tnum, io, ao[mut][io], ao[0][io] );
end
end
end

begin
automatic string msg =
\$sformatf("%0d %s tests on %s: %0d errors found.",
ntests, tinfo.name, mname[mut], n_err);
\$write("Done with %s\n",msg);
test_summary = { test_summary, \$sformatf("Results of %s\n", msg) };
end

end

\$write("\n** Summary of Results **\n%s", test_summary);

end

endmodule