/// EE 4755 - Digital Design Using HDLs
//

 /// Classroom Code Examples
 //
 //  Covered 25, .. September 2015

 //  Sequential Shifter
 //  Order-d Sequential Shifter
 //
 //  For lecture slides, including diagrams:
 //        http://www.ece.lsu.edu/v/2015/lsli-syn-seq.pdf


module shift_fixed
  #( int wid_lg = 4,
     int amt = 1,
     int wid = 1 << wid_lg )
   ( output [wid-1:0] shifted,
     input [wid-1:0] unshifted,
     input shift );

   assign  shifted = shift ? unshifted << amt : unshifted;

endmodule


module shift_lt_behav
  #( int wid_lg = 4,
     int wid = 1 << wid_lg )
   ( output [wid-1:0] shifted,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt );

   assign               shifted = unshifted << amt;

endmodule

module shift_lt_comb
  #( int wid_lg = 4,
     int wid = 1 << wid_lg )
   ( output [wid-1:0] shifted,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt );

   wire [wid-1:0]       step[wid_lg-1:-1];

   assign step[-1] = unshifted;
   assign shifted = step[wid_lg-1];

   for ( genvar i=0; i<wid_lg; i++ )
     shift_fixed #(wid_lg,1<<i) sf( step[i], step[i-1], amt[i] );

endmodule


module shift_lt_seq_live
  #( int wid_lg = 4,
     int wid = 1 << wid_lg )
   ( output logic [wid-1:0] shifted,
     output logic ready,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt,
     input start,
     input clk );

   logic [wid_lg-1:0]   cnt;

   wire [wid-1:0]       sf_out;

   shift_fixed #(wid_lg,1) sf( sf_out, shifted, 1'b1 );

   always_ff @( posedge clk ) begin

      if ( start ) begin

         cnt = amt;
         shifted = unshifted;
         ready = 0;
         
      end else if (cnt != 0 ) begin

         cnt--;
         shifted = sf_out;

      end else begin

         ready = 1;

      end

   end

endmodule









module shift_lt_seq
  #( int wid_lg = 4,
     int wid = 1 << wid_lg )
   ( output logic [wid-1:0] shifted,
     output wire ready,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt,
     input start,
     input clk );

   logic [wid_lg-1:0]   cnt;

   wire [wid-1:0]       sf_out;

   shift_fixed #(wid_lg,1) sf( sf_out, shifted, 1'b1 );

   always_ff @( posedge clk ) begin

      if ( start == 1 ) begin

         shifted = unshifted;
         cnt = amt;

      end else if ( cnt > 0 ) begin

         shifted = sf_out;
         cnt--;

      end 

//         shifted_reg[14]/CLK   setup                     0  +277    1236 R 


   end

   assign ready = cnt == 0;
   //  shifted_reg[12]/CLK   setup                    0  +266    1233 R 


   // Original
   //  shifted_reg[14]/CLK   setup                     0  +277    1200 R

   //       ready = !start && cnt == 0;
   // shifted_reg[14]/CLK   setup                     0  +277    1301 R


   // ready = !start && cnt < 2;   
   // shifted_reg[10]/CLK   setup                     0  +277    1224 R 

   // ready = ~|cnt[wid_lg-1:1];
   // cnt_reg[3]/CLK   setup                     0  +276    1248 R

   // ready = 0;
   // shifted_reg[5]/CLK   setup                     0  +277    1302 R

      // ready = 0; cnt > 0
   // shifted_reg[10]/CLK   setup                     0  +277    1248 R 

endmodule


module shift_lt_seq_d_live
  #( int wid_lg = 4,
     int num_shifters = 2,
     int wid = 1 << wid_lg )
   ( output logic [wid-1:0] shifted,
     output wire ready,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt,
     input start,
     input clk );


   logic [num_shifters-1:0] shift;

   wire [wid-1:0]           shin[num_shifters-1:-1];

   for ( genvar i=0; i<num_shifters; i++ )
     shift_fixed sf(shin[i],shift[i],shin[i-1]);



   
endmodule


















module shift_lt_seq_d
  #( int wid_lg = 4,
     int num_shifters = 2,
     int wid = 1 << wid_lg )
   ( output logic [wid-1:0] shifted,
     output wire ready,
     input [wid-1:0] unshifted,
     input [wid_lg-1:0] amt,
     input start,
     input clk );

   localparam int cnt_bits = ( wid_lg + num_shifters - 1 ) / num_shifters;
   logic [num_shifters-1:0][cnt_bits-1:0] cnt;
   wire [wid-1:0]      inter_sh[num_shifters-1:-1];
   assign              inter_sh[-1] = shifted;
   for ( genvar i = 0; i < num_shifters; i++ ) begin
      localparam int shift_amt = 1 << i * cnt_bits;
      wire       shift = cnt[i] != 0;
      shift_fixed #(wid_lg,shift_amt) sf( inter_sh[i], inter_sh[i-1], shift );
   end

   always_ff @( posedge clk )
     
      if ( start == 1 ) begin

         shifted = unshifted;
         cnt = amt;

      end else if ( cnt > 0 ) begin

         shifted = inter_sh[num_shifters-1];
         for ( int i=0; i<num_shifters; i++ ) if ( cnt[i] ) cnt[i]--;

      end

   assign ready = cnt == 0;

   // num_shifters = 1
   // shifted_reg[11]/CLK   setup                     0  +277    1200 R 

   // num_shifters = 2
   //  shifted_reg[13]/CLK   setup                     0  +277    1336 R

   // assign ready = cnt == 0;
   //  shifted_reg[11]/CLK   setup                    0  +276    1287 R 


   // num_shifters = 4
   // shifted_reg[14]/CLK   setup                     0  +260    1487 R 

endmodule


// cadence translate_off

module testbench;

   localparam int wid_lg = 6;
   localparam int wid = 1 << wid_lg;

   localparam int max_units = 10;

   logic      clk;
   bit        done;
   int cycle;

   uwire [wid-1:0] sout[max_units];
   uwire ready[max_units];
   logic [wid-1:0] sin;
   logic [wid_lg-1:0] amt;
   logic              start;

   typedef struct { int idx; int err_count = 0; bit seq = 0;
                    int cyc_tot = 0; } Info;
   Info pi[string];

   shift_lt_behav #(wid_lg) my_sr1(sout[0], sin, amt);
   initial pi["Behavioral"].idx = 0;

   shift_lt_comb #(wid_lg) my_sr2(sout[1], sin, amt);
   initial pi["Combinational"].idx = 1;

   shift_lt_seq_live #(wid_lg) my_sll(sout[7], ready[7], sin, amt, start, clk);
   initial begin
      automatic string m = "Sequential Lv";
      pi[m].idx = 7; pi[m].seq = 1;
   end

   shift_lt_seq #(wid_lg) my_sl3(sout[2], ready[2], sin, amt, start, clk);
   initial begin
      automatic string m = "Sequential";
      pi[m].idx = 2; pi[m].seq = 1;
   end

   shift_lt_seq_d #(wid_lg,1) my_sld1(sout[6], ready[6], sin, amt, start, clk);
   initial begin
      automatic string m = "Degree 1";
      pi[m].idx = 6; pi[m].seq = 1;
   end

   shift_lt_seq_d #(wid_lg,2) my_sld(sout[3], ready[3], sin, amt, start, clk);
   initial begin
      automatic string m = "Degree 2";
      pi[m].idx = 3; pi[m].seq = 1;
   end

   shift_lt_seq_d #(wid_lg,3) my_sld3(sout[4], ready[4], sin, amt, start, clk);
   initial begin
      automatic string m = "Degree 3";
      pi[m].idx = 4; pi[m].seq = 1;
   end

   shift_lt_seq_d #(wid_lg,4) my_sld4(sout[5], ready[5], sin, amt, start, clk);
   initial begin
      automatic string m = "Degree 4";
      pi[m].idx = 5; pi[m].seq = 1;
   end

   localparam int tests_per_sa = 10;
   localparam int num_tests = wid * tests_per_sa;
   localparam int cycle_limit = num_tests * wid * 2;

   initial begin
      clk = 0;
      cycle = 0;
      while ( !done && cycle < cycle_limit ) #10 cycle += clk++;

      if ( cycle == cycle_limit )
        $write("*** Cycle limit exceeded, ending.\n");
      $finish();
   end

   initial begin

      // Number of test inputs (stimuli).
      //
      automatic int test_count = 0;

      done = 0;

      @( negedge clk ); @( negedge clk );


      // Provide one test pattern per shift amount.
      //
      for ( int i=0; i<num_tests; i++ ) begin

         logic [wid-1:0] shadow_sout;
         int awaiting;
         test_count++;

         sin = $random;
         amt = i / tests_per_sa;

         shadow_sout = sin << amt;

         start = 1;

         @( negedge clk );

         start = 0;

         do begin

            awaiting = 0;

            @( negedge clk );

            foreach ( pi[mut] )
              if ( pi[mut].seq && ready[pi[mut].idx] !== 1 )
                begin  pi[mut].cyc_tot++;  awaiting++;  end

         end while ( awaiting );

         // Check the output of each Module Under Test.
         //
         foreach ( pi[ mut ] ) begin

            automatic logic [wid-1:0] sout = sout[pi[mut].idx];

            if ( shadow_sout !== sout ) begin
               pi[mut].err_count++;
               if ( pi[mut].err_count < 5 )
                 $write
                   ("%-20s wrong result for 0x%0h << %0d:  0x%0h != 0x%0h (correct)\n",
                    mut, sin, amt, sout, shadow_sout);
            end

         end

      end

      done = 1;

      foreach ( pi[ mut ] )
         $write("Ran %4d tests for %-15s, %4d errors found. Avg cyc %.1f\n",
                  test_count, mut, pi[mut].err_count,
                pi[mut].seq ? real'(pi[mut].cyc_tot) / test_count : 1
                );
   end


endmodule

// cadence translate_on