////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2023 Homework 1 -- SOLUTION
//

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

`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
  ///  Complete minmax2p1 using a compare_lt and mux2 instantiations.
 ///
//
//     [✔] Only modify minmax2p1. Use minmax2 for reference.
//
//     [✔] minmax2p1 must instantiate a compare_lt module and mux2 modules.
//     [✔] minmax2p1 must NOT use assign statements or procedural code.
//
//     [✔] 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 values.
//
//     [✔] Code must be written clearly.

module minmax2p1
  #( int w = 4 )
   ( output uwire [w-1:0] min, max,
     input uwire [w-1:0] a0, a1 );

   // Put solution here.

   /// SOLUTION
   uwire lt;
   compare_lt #(w) clt(lt, a0, a1);

   mux2 #(w) mn(max,lt,a0,a1);
   mux2 #(w) mx(min,lt,a1,a0);

endmodule

module compare_lt
  #( int w = 31 )
   ( output uwire lt,
     input uwire [w-1:0] a0, a1 );

   // DO NOT modify this module.

   // Set lt to 0 if a1 < a0, set lt to 1 otherwise.
   //
   assign lt = a0 <= a1;

endmodule

module mux2
  #( int w = 3 )
   ( output uwire [w-1:0] x,
     input uwire s,
     input uwire [w-1:0] a0, a1 );

   // DO NOT modify this module either.

   assign x = s ? a1 : a0;

endmodule

module minmax2
  #( int w = 10 )
   ( output uwire [w-1:0] min, max,
     input uwire [w-1:0] a0, a1 );

   // DO NOT modify this module either.

   // Assign min to the smaller of a0 and a1, and max to the larger.
   assign { min, max } = a0 <= a1 ? { a0, a1 } : { a1, a0 };

endmodule



//////////////////////////////////////////////////////////////////////////////
///  Problem 2
//
  ///  Complete minmax4 and minmax8.
//
//     [✔] In minmax4, instantiate minmax2.
//     [✔] In minmax8, instantiate minmax4.
//     [✔] In minmax4 and minmax8, instantiate min2 and max2, as necessary.
//
//     [✔] Do not use assign statements or procedural code.
//
//     [✔] 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 values.
//
//     [✔] Pay attention to cost.
//     [✔] Assume cost of min2 + max2 in more than minmax2.
//     [✔] Code must be written clearly.

module minmax4
  #( int w = 20 )
   ( output uwire [w-1:0] min, max,
     input uwire [w-1:0] a[4] );

   // Put solution here.

   /// SOLUTION

   uwire [w-1:0] lomin, lomax, himin, himax;

   minmax2 #(w) mlo( lomin, lomax, a[0], a[1] );
   minmax2 #(w) mhi( himin, himax, a[2], a[3] );

   min2 #(w) m1( min, lomin, himin );
   max2 #(w) m2( max, lomax, himax );

endmodule


module minmax8
  #( int w = 12 )
   ( output uwire [w-1:0] min, max,
     input uwire [w-1:0] a[8] );

   // Put solution here.

   /// SOLUTION

   uwire [w-1:0] lomin, lomax, himin, himax;

   minmax4 #(w) mlo( lomin, lomax, a[0:3] );
   minmax4 #(w) mhi( himin, himax, a[4:7] );

   min2 #(w) m1( min, lomin, himin );
   max2 #(w) m2( max, lomax, himax );

endmodule


module min2
  #( int w = 10 )
   ( output uwire [w-1:0] min,
     input uwire [w-1:0] a0, a1 );
   assign min = a0 < a1 ? a0 : a1;
endmodule

module max2
  #( int w = 10 )
   ( output uwire [w-1:0] max,
     input uwire [w-1:0] a0, a1 );
   assign max = a0 < a1 ? a1 : a0;
endmodule



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


// cadence translate_off

module testbench;

   localparam int npsets = 3; // Number of instantiations.
   localparam int pset[npsets] =
              '{ 2, 4, 8 };

   int t_errs;       // Total number of errors.
   initial begin t_errs = 0; end
   final $write("Total number of errors: %0d\n",t_errs);

   uwire d[npsets:-1];    // Start / Done signals.
   assign d[-1] = 1;  // Initialize first at true.

   // Instantiate a testbench at each size.
   //
   for ( genvar i=0; i<npsets; i++ )
     testbench_n #(pset[i]) t2( .done(d[i]), .tstart(d[i-1]) );

endmodule

module testbench_n
  #( int n = 5 )
   ( output logic done, input uwire tstart );

   localparam int w = 13;
   localparam int ntests = 100;

   logic [w-1:0] a[n], sa[n];
   uwire [w-1:0] min, max;

   if ( n == 2 )
     minmax2p1 #(w) mm( min, max, a[0], a[1] );
   else if ( n == 4 )
     minmax4 #(w) mm( min, max, a );
   else if ( n == 8 )
     minmax8 #(w) mm( min, max, a );

   int n_err_min, n_err_max;

   initial begin

      done = 0;
      wait( tstart );

      n_err_min = 0;
      n_err_max = 0;

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

         logic [w-1:0] shadow_min, shadow_max;

         for ( int i=0; i<n; i++ ) a[i] = {$random};
         sa = a; sa.sort();
         shadow_min = sa[0];
         shadow_max = sa[n-1];

         #1;
         if ( min !== shadow_min ) begin
            n_err_min++;
            if ( n_err_min < 5 )
              $write("Error n=%0d  min %d != %d (correct)\n",
                       n, min, shadow_min);
         end
         if ( max !== shadow_max ) begin
            n_err_max++;
            if ( n_err_max < 5 )
              $write("Error n=%0d  max %d != %d (correct)\n",
                       n, max, shadow_max);
         end

      end

      testbench.t_errs += n_err_min + n_err_max;

      done = 1;

      $write("Done with n=%0d, tests, %0d min %0d max errors found.\n",
               n, n_err_min, n_err_max );

   end
endmodule

// cadence translate_on