/// LSU EE 4755 - Digital Design Using HDLs
//
 ///  Multiplexor Tree Implementations Classroom demo code.


// Ordinary Two-Input Multiplexor
//
module mux2
  #( int w = 5 )
   ( output uwire [w-1:0] x,
     input uwire s,
     input uwire [w-1:0] a[2] );
   assign x = a[s];
endmodule

// Recursively Described Tree Multiplexor -- Simpler
//
module mux
  #( int n = 4,
     int w = 5,
     int swid = $clog2(n) )
   ( output uwire [w-1:0] x,
     input uwire [swid-1:0] s,
     input uwire [w-1:0] a[n] );

   if ( n == 1 ) begin

      assign x = a[0];

   end else if ( n == 2 ) begin

      assign x = a[s];

   end else begin

      // Set the number of inputs to the lo mux to the largest
      // power of 2 less than n.
      //
      localparam int srec = $clog2(n) - 1;
      localparam int nlo = 1 << srec;

      // The remainder of the inputs go to the hi mux.
      //
      localparam int nhi = n - nlo;

      uwire [w-1:0] x2[2];

      mux #(nlo,w     ) muxlo( x2[0], s[srec-1:0], a[0:nlo-1] );
      mux #(nhi,w,srec) muxhi( x2[1], s[srec-1:0], a[nlo:n-1] );

      mux2 #(w)         mux21(x,     s[swid-1],   x2);

   end

endmodule


// Recursively Described Tree Multiplexor -- Shorter
//
module muxs
  #( int n = 4,
     int w = 5,
     int swid = $clog2(n) )
   ( output uwire [w-1:0] x,
     input uwire [swid-1:0] s,
     input uwire [w-1:0] a[n] );

   uwire [w-1:0] x2[2];
   localparam int slo = $clog2(n) - 1;
   localparam int nlo = 1 << slo;
   localparam int nhi = n - nlo;
   localparam int shi = $clog2(nhi);

   if ( nlo == 1 )
     assign x2[0] = a[0];
   else
     muxs #(nlo,w) muxlo( x2[0], s[slo-1:0], a[0:nlo-1] );

   if ( nhi == 1 )
     assign x2[1] = a[nlo];
   else
     muxs #(nhi,w) muxhi( x2[1], s[shi-1:0], a[nlo:n-1] );

   mux2 #(w) mux21 (x, s[swid-1], x2);

endmodule


//////////////////////////////////////////////////////////////////////////////
/// Testbench Code
//
//  The code below instantiates some of the modules above,
//  provides test inputs, and verifies the outputs.
//
//  The testbench may be modified to facilitate your solution. Of
//  course, the removal of tests which your module fails is not a
//  method of fixing a broken module. (One might modify the testbench
//  so that the first tests it performs are those which make it easier
//  to determine what the problem is, for example, test inputs that
//  are all 0's or all 1's.)



// cadence translate_off


module testbench();

   localparam int w = 10;
   localparam int n_in_max = 8;
   localparam int n_size = 5;
   localparam int n_mod = 2;
   localparam int n_mut = n_size * n_mod;

   uwire [w-1:0] x[n_mut];
   logic [2:0]  s;
   logic [w-1:0] a[n_in_max];

   localparam    int nin[n_size] = { 2,4,8,7,5};
   string descr[] = { "mux", "muxs" };

   for ( genvar i=0; i<n_size; i++ ) begin
      localparam int n = nin[i];
      localparam int swid = $clog2(n);
      mux  #(n,w) mi( x[i], s[swid-1:0], a[0:n-1] );
      muxs #(n,w) mni( x[n_size+i], s[swid-1:0], a[0:n-1] );
   end

   initial begin

      automatic int n_test = 0;
      automatic int n_err = 0;

      for ( int i=0; i < n_in_max; i++ ) begin
         n_test++;
         #1;
         s = i;
         for ( int j=0; j<n_in_max; j++ ) a[j] = {$random,j[3:0]};
         #1;
         for ( int m=0; m<n_mut; m++ ) begin
            automatic int ni = m % n_size;
            automatic int mi = m / n_size;
            automatic int n_in = nin[ni];
            automatic int sm = i;
            if ( i >= n_in ) continue;

            if ( x[m] !== a[sm] ) begin
               n_err++;
               $write
                 ("Error in %0d-input %s for s=%0d, 0x%0x != 0x%0x (correct)\n",
                  n_in, descr[mi], sm, x[m], a[sm]);
            end
         end
      end
      $write("Done with %0d tests, %0d errors found.\n",n_test,n_err);
   end

endmodule

// cadence translate_on