////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2020 Homework 2
//

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

 /// Instructions:
  //
  // (1) Find the undergraduate workstation laboratory, room 2241 Patrick
  //     F. Taylor Hall. Machines to use are in the back.
  //
  // (☣) Practice Social Distancing:
  //     - Use hand sanitizer before first touching a keyboard and
  //       after finishing.
  //     - Keep your mask on. 
  //     - If you must take off your mask for a short period of time
  //       do so at least 2 meters from the closest person.
  //     - Don't forget that it is often spread by those who don't
  //       know they are infectious.
  //
  // (2) Locate your account.  If you did not get an account please
  //     E-mail: koppel@ece.lsu.edu
  //
  // (3) Log in to a Linux workstation.
  //
  // (4) If you haven't already, follow the account setup instructions here:
  //     https://www.ece.lsu.edu/koppel/v/proc.html
  //
  // (5) Copy this assignment, local path name
  //     /home/faculty/koppel/pub/ee4755/hw/2020/hw02
  //     to a directory ~/hw02 in your class account. (~ is your home
  //     directory.) Use this file for your solution.
  ///      BE SURE THAT YOUR FILE IS CORRECTLY NAMED AND IN THE RIGHT PLACE.
  //
  // (6) Find the problems in this file and solve them.
  //
  //     Your entire solution should be in this file.
  //
  //     Do not change module names.
  //
  // (7) Your solution will automatically be copied from your account by
  //     the TA-bot.


 /// Additional Resources
  //
  // Verilog Documentation
  //    The Verilog Standard
  //      https://ieeexplore.ieee.org/document/8299595/
  //    Introductory Treatment (Warning: Does not include SystemVerilog)
  //      Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed.
  //
  // Account Setup and Emacs (Text Editor) Instructions
  //      https://www.ece.lsu.edu/koppel/v/proc.html
  //      To learn Emacs look for Emacs tutorial.
  //
  // Unix Help (Very outdated.  Alternatives welcome.)
  //      https://www.ece.lsu.edu/koppel/v/4ltrwrd/


`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
 /// Modify nn4x4b, nn1x4b, and nn1x2b to compute same output as nn4x4.
 ///
//
//     [ ] nn4x4b must instantiate exactly four nn1x4b modules.
//     [ ] nn1x4b must instantiate exactly two nn1x2b modules.
//
//     [ ] Make sure that the testbench does not report errors.
//     [ ] Module must be synthesizable. Use command: genus -files syn.tcl
//
//     [ ] Don't assume any particular parameter value.
//     [ ] Don't add more ports than are 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 MODULE

   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] );

   /// Instantiate Four nn1x4b Modules Here.
   //  The examples further below are for reference, they should not
   //  be part of the solution.





   // Example of how to extract a row of a 2-D array.
   //
   uwire [ww-1:0] wht_row_0[4] = wht[0];
   //
   // There is no need to use object wht_row_0 declared above, instead
   // wht[0] can be used in a port connection to one of the nn1x4b
   // instantiations.
   //
   // Example of how to use the row:
   //
   uwire [wa-1:0] ao_zero = ai[0] * wht_row_0[0] + ai[1] * wht_row_0[1]
                  + ai[2] * wht_row_0[2] + ai[3] * wht_row_0[3];
   //
   /// Do not use this ao_zero in your solution. Feel free to delete it.

endmodule

module nn1x4b;

   // [ ] Declare parameters and ports for one ao and a four-element ai.

   // Instantiate two nn1x2b modules here.
   // Other logic may be needed.

endmodule

module nn1x2b;

   // [ ] Declare parameters and ports for one ao and a two-element ai.

   // Write code to compute ao.

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

// cadence translate_off

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", "Hier" };

   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

// cadence translate_on