////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2019 Homework 1
//

 /// Assignment  https://www.ece.lsu.edu/koppel/v/2019/hw01.pdf

 /// Instructions:
  //
  // (1) Find the undergraduate workstation laboratory, room 2241 Patrick
  //     F. Taylor Hall. Machines to use are in the back.
  //
  // (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/2019/hw01
  //     to a directory ~/hw01 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 mult16_tree, mult8_tree, and mult4_tree to implement multiplier.
 ///
//
//     [ ] Make sure that the testbench does not report errors.
//     [ ] mult16_tree must use exactly two mult8_tree modules, etc.
//     [ ] Pay attention to port widths. Do not make them larger than needed.
//     [ ] Module must be synthesizable. Use command: genus -files syn.tcl


module mult16_tree
  #( int wa = 16, int wb = 16, int wp = wa + wb )
   ( output uwire [31:0] prod,
     input uwire [15:0] a,
     input uwire [15:0] b );

   /// Problem 1 solution goes here, and in other modules.
   //  [ ] Instantiate two mult8_tree's.
   //  [ ] Use implicit structural or behavioral code to combine their outputs.


endmodule

module mult8_tree
  ( output uwire [0:0] prod,
    input uwire [0:0] a,
    input uwire [0:0] b );
   //  [ ] Pay attention to port widths. Do not make them larger than needed.

   /// Problem 1 solution goes here, and in other modules.
   //  [ ] Instantiate two mult4_tree's.
   //  [ ] Use implicit structural or behavioral code to combine their outputs.



endmodule

module mult4_tree
  ( output uwire [0:0] prod,
    input uwire [3:0] a,
    input uwire [15:0] b );
   //  [ ] Pay attention to port widths. Do not make them larger than needed.

   /// Problem 1 solution goes here, and in other modules.
   //  [ ] Use implicit structural or behavioral code to combine their outputs.

   mult2 mlo( /* Finish! */ );
   mult2 mhi( /* Finish! */ );

endmodule

 /// Do not modify the code below this point.

module mult2
  ( output uwire [17:0] prod, input uwire [1:0] a, input uwire [15:0] b );

   /// DO NOT MODIFY THIS ROUTINE.
   assign prod = a * b;

endmodule


module mult16_flat
  #( int wa = 16, int wb = 16, int wp = wa + wb )
   ( output uwire [31:0] prod, input uwire [15:0] a, b );

   /// DO NOT MODIFY THIS ROUTINE.

   uwire [17:0]  prod00, prod02, prod04, prod06, prod08, prod10, prod12, prod14;
   mult2 m0( prod00, a[1:0], b);
   mult2 m2( prod02, a[3:2], b);
   mult2 m4( prod04, a[5:4], b);
   mult2 m6( prod06, a[7:6], b);
   mult2 m8( prod08, a[9:8], b);
   mult2 m10( prod10, a[11:10], b);
   mult2 m12( prod12, a[13:12], b);
   mult2 m14( prod14, a[15:14], b);

   assign prod = prod00 + ( prod02 << 2 ) + ( prod04 << 4 ) + ( prod06 << 6 ) + ( prod08 << 8 ) + ( prod10 << 10 ) + ( prod12 << 12 ) + ( prod14 << 14 );

endmodule



module mult_operator
  #( int wa = 16, int wb = 16, int wp = wa + wb )
   ( output uwire [wp:1] prod, input uwire [wa:1] a, input uwire [wb:1] b );
   /// DO NOT MODIFY THIS ROUTINE.
   assign prod = a * b;
endmodule




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

// cadence translate_off

module testbench;

   localparam int wid = 16;
   localparam int num_tests = 1000;
   localparam int NUM_MULT = 3;
   localparam int err_limit = 7;

   logic [wid-1:0] plier, cand;
   logic [2*wid-1:0] prod[NUM_MULT], shadow_prod;

   mult_operator mb0(prod[0], plier, cand);
   mult16_flat mb1(prod[1], plier, cand);
   mult16_tree mb2(prod[2], plier, cand);

   string names[] = '{ "mult_operator", "mult16_flat", "mult16_tree" };

   int err_cnt[NUM_MULT];

   // Array of multiplier/multiplicand values to try out.
   // After these values are used a random number generator will be used.
   //
   int tests[$] = {1,1, 1,2,  1,32,  32, 1};

   initial begin

      $write("\nStarting testbench...\n");

      for ( int i=0; i<num_tests; i++ ) begin

         // Set multiplier and multiplicand values.
         //
         plier = tests.size() ? tests.pop_front() : $random();
         cand = tests.size() ? tests.pop_front() : $random();

         shadow_prod = plier * cand;

         #10;

         // Make sure each module's output is correct.
         //
         for ( int mut=0; mut<NUM_MULT; mut++ ) begin

            if ( shadow_prod !== prod[mut] ) begin

               err_cnt[mut]++;

               if ( err_cnt[mut] < err_limit )
                 $write("Error in %s test %4d:  %x != %x (correct)\n",
                          names[mut], i, prod[mut], shadow_prod);
            end

         end

      end

      // Tests completed, report error count for each device.
      //
      for ( int mut=0; mut<NUM_MULT; mut++ ) begin

         $write("Mut %-15s, %4d errors (%.1f%% of tests)\n",
                  names[mut], err_cnt[mut],
                  100.0 * err_cnt[mut]/real'(num_tests) );

      end

      $finish(2);

   end

endmodule

// cadence translate_on