////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2017 Homework 4
//
 /// SOLUTION

 /// Assignment  http://www.ece.lsu.edu/koppel/v/2017/hw04.pdf


 /// Additional Resources
  //
  // Verilog Documentation
  //    The Verilog Standard
  //      http://standards.ieee.org/getieee/1800/download/1800-2012.pdf
  //    Introductory Treatment (Warning: Does not include SystemVerilog)
  //      Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed.
  //
  // Account Setup and Emacs (Text Editor) Instructions
  //      http://www.ece.lsu.edu/koppel/v/proc.html
  //      To learn Emacs look for Emacs tutorial.


`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
 /// Modify maxrun so that it keeps track of the current and maximum runs.
//
//     [✔] Make sure that the testbench does not report errors.
//     [✔] Module must be synthesizable.
//     [✔] Code must be reasonably efficient.


 /// Solution 1: Written for maximum code clarity.
//
module maxrun
  #( int w = 2,
     int c = 4 )
   ( output uwire [w-1:0] len,
     output logic [c-1:0] mr_char,
     input uwire clk, reset, mr,
     input uwire [c-1:0] in_char );

   logic [w-1:0] cr_len, mr_len;
   logic [c-1:0] prev_char;

   assign len = mr ? mr_len : cr_len;

   always_ff @( posedge clk ) begin

      if ( reset ) mr_len = 0;

      if ( !reset && in_char == prev_char )
        cr_len++;
      else
        cr_len = 1;

      if ( cr_len > mr_len )
        begin
           mr_len = cr_len;
           mr_char = in_char;
        end

      prev_char = in_char;

   end

endmodule

 /// Solution 2: Written for high performance.
//
module maxrun_opt
  #( int w = 2,
     int c = 4 )
   ( output uwire [w-1:0] len,
     output logic [c-1:0] mr_char,
     input uwire clk, reset, mr,
     input uwire [c-1:0] in_char );

   logic [w-1:0] cr_len, mr_len;
   logic [c-1:0] prev_char;

   assign len = mr ? mr_len : cr_len;

   always_ff @( posedge clk ) begin

      logic match;
      match = in_char == prev_char;

      /// Approach to Reducing Critical Path
      //
      //   To keep addition off the critical path ..
      //   .. check cr_len >= mr_len && match ..
      //   .. rather than using incremented cr_len for: cr_len > mr_len.
      //
      //   Based on experimentation, use ..
      //   .. cr_len >= mr_len ..
      //   .. instead of ..
      //   .. cr_len == mr_len ..
      //   .. even though cr_len == mr_len is easier to compute.

      if ( reset ) begin
         mr_len = 1;
         mr_char = in_char;
      end else if ( cr_len >= mr_len && match ) begin
         mr_len = cr_len + 1;
         mr_char = in_char;
      end

      if ( !reset && match )
        cr_len = cr_len + 1;
      else
        cr_len = 1;

      prev_char = in_char;

   end

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

program reactivate
   (output uwire clk_reactive, output int cycle_reactive,
    input uwire clk, input var int cycle);
   assign clk_reactive = clk;
   assign cycle_reactive = cycle;
endprogram

module testbench;

   localparam int char_wid = 8;
   localparam int count_wid = 10;

   localparam int test_num_chars = 100;
   localparam int cycle_limit = test_num_chars + 20;

   localparam int nmuts = 1;

   localparam int char_mask = ( 1 << char_wid ) - 1;

   uwire [count_wid-1:0] len[nmuts];
   uwire [char_wid-1:0] mr_char[nmuts];
   logic [char_wid-1:0] char, shadow_last_char;
   logic mr;

   logic clock, reset;
   bit done;
   int cycle;

   logic clk_reactive;
   int cycle_reactive;
   reactivate ra(clk_reactive,cycle_reactive,clock,cycle);

   initial begin
      clock = 0;
      cycle = 0;

      fork
         forever #10 cycle += clock++;
         wait( done );
         wait( cycle >= cycle_limit )
           $write("*** Cycle limit exceeded, ending.\n");
      join_any;

      $finish();
   end

   maxrun_opt #(count_wid,char_wid) mr1 (len[0],mr_char[0],clock,reset,mr,char);

   initial begin

      automatic int n_err_cr_len = 0, n_err_mr_len = 0, n_err_mr_char = 0;
      int shadow_mr_len, shadow_mr_char, shadow_cr_len;
      bit is_err_cr_len, is_err_mr_len, is_err_mr_char;

      done = 0;
      reset = 0;
      char = 0;
      mr = 0;

      @( posedge clk_reactive );

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

         automatic bit do_reset = i == 0 || {$random} % 10 == 0;
         automatic bit do_new_char = {$random} % 3 == 0;
         logic [count_wid-1:0] mr_len, cr_len;

         @( negedge clock );

         shadow_last_char = char;

         if ( do_new_char ) char = {$random} & char_mask;
         reset = do_reset;

         if ( !do_reset && char === shadow_last_char )
           shadow_cr_len++;
         else
           shadow_cr_len = 1;

         if ( do_reset )
            shadow_mr_len = 0;

         if ( shadow_cr_len > shadow_mr_len ) begin
            shadow_mr_len = shadow_cr_len;
            shadow_mr_char = char;
         end

         @( posedge clk_reactive );

         repeat ( 2 ) begin
            if ( mr ) mr_len = len[0]; else cr_len = len[0];
            mr = !mr;
            #0; #0;
         end

         is_err_cr_len = shadow_cr_len !== cr_len;
         is_err_mr_len = shadow_mr_len !== mr_len;
         is_err_mr_char = shadow_mr_char !== mr_char[0];

         $write
           ("%5d %1s c=%2x    cr_len %3d %s    mr_len %3d %s    mr_c %2x %s\n",
            i, do_reset ? "r" : " ", char,
            cr_len,
            is_err_cr_len ? $sformatf("!= %3d", shadow_cr_len) : "ok    ",
            mr_len,
            is_err_mr_len ? $sformatf("!= %3d", shadow_mr_len) : "ok    ",
            mr_char[0],
            is_err_mr_char ? $sformatf("!= %2x", shadow_mr_char) : "ok   " );

         if ( shadow_cr_len !== cr_len ) n_err_cr_len++;
         if ( shadow_mr_len !== mr_len ) n_err_mr_len++;
         if ( shadow_mr_char !== mr_char[0] ) n_err_mr_char++;

      end

      $write("Done with %0d tests, %0d %0d %0d errors found.\n",
             test_num_chars,
             n_err_cr_len,
             n_err_mr_len,
             n_err_mr_char);

      done = 1;

   end

endmodule

// cadence translate_on