////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2025 Homework 4
//

 /// Assignment  https://www.ece.lsu.edu/koppel/v/2025/hw04.pdf

 /// Instructions:
  //
  // (1) Find the undergraduate workstation laboratory, room 2241 Patrick
  //     F. Taylor Hall.
  //
  // (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/2025/hw04
  //     to a directory ~/hw04 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/10458102
  //    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.
  //



`default_nettype none

typedef enum logic [1:0]
  { tr_both = 3, tr_incr = 2, tr_decr = 1, tr_equal = 0 } Trend;

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
  /// Complete trend2
  //  
//
//     [ ] Complete trend so that it computes the correct outputs.
//     [ ] It CAN NOT use expressions such as samp < samp_prev,
//     [ ] .. instead it must use the outputs of ist_compare instantiations.
//     [ ] Instantiate as many ist_compare modules as needed.
//     [ ] The module can use procedural code, but not for comparisons.
//
//     [ ] Make sure that the testbench does not report errors.
//     [ ] Do not assume particular parameter values.
//     [ ] The module must be synthesizable.
//
//     [ ] Code must be written clearly.
//     [ ] Pay attention to cost and performance.




module ist_compare
  #( int w = 15 )
   ( output uwire [1:0] gl, input uwire [w-1:0] a, b );
   assign gl = { a < b, a > b };
   // Do not modify this module.
endmodule


module trend2
  #( int wd = 20, wi = 10  )
   ( output logic [1:0] prev_trend,
     output logic [wi-1:0] prev_length,
     output logic [1:0] curr_trend, last_2_trend,
     output logic [wi-1:0] curr_idx_start,
     input uwire [wd-1:0] samp,
     input uwire reset, clk );

   uwire [wi-1:0] debug_val;


endmodule



//////////////////////////////////////////////////////////////////////////////
/// Testbench Code
//
// It is okay to modify the testbench code to facilitate the coding
// and debugging of your modules. Keep in mind that your submission
// will be tested using a different testbench, so no one will be
// accused of dishonesty for modifying the testbench below. If you do
// modify the testbench be sure to make sure all of the original tests
// are performed to make sure that your code passes the original
// testbench.


// 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

string trend_str[int] =
       '{ tr_both: "Bth", tr_incr: "Inc", tr_decr: "Dec", tr_equal: "Eql" };

function string trend_str_get( logic [1:0] trend );
      trend_str_get = $isunknown(trend) ? "Unk" : trend_str[trend];
endfunction

function logic[1:0] func_compare( int unsigned a, b );
   func_compare = { a < b, a > b };
endfunction

function string eob( bit is_error, string err_msg, string debug_msg );
      localparam bit debug = 0;
      eob = is_error ? err_msg : ( debug ? debug_msg : " " );
endfunction

function int min( int a, int b );
      min = a <= b ? a : b;
endfunction

module testbench;

   // Minimum number of trace lines to show.
   //
   localparam int trace_lines_n_wanted = 10;
   //
   // Additional lines may be shown if there are errors.

   localparam int n_errors_show = 20;


   logic done;

   localparam int wd = 10;
   localparam int wi = 12;
   localparam int max_idx = ( 1 << wi ) - 1;
   localparam int limit_val = ( 1 << wd ) - 1;

   localparam int n_tests = 20000;

   localparam int cyc_max = n_tests + 100;

   int seed;
   initial seed = 475501;

   function automatic bit rand_bern( int period );
      rand_bern = $dist_uniform(seed,1,period) == 1;
   endfunction

   function automatic int rand_n( int n );
      rand_n = $dist_uniform(seed,0,n-1);
   endfunction

   bit clk;
   int cycle, cycle_limit;
   logic clk_reactive;
   int cycle_reactive;
   reactivate ra(clk_reactive,cycle_reactive,clk,cycle);
   string event_trace;
   string ev_trace[$];

   initial begin
      clk = 0;
      cycle = 0;
      event_trace = "";

      done = 0;
      cycle_limit = cyc_max;
      //  wait( tstart );

      fork
         while ( !done ) #1 cycle += clk++;
         wait( cycle >= cycle_limit )
           $write("Exit from clock loop at cycle %0d, limit %0d.  %s\n %s\n",
                  cycle, cycle_limit, "** CYCLE LIMIT EXCEEDED **",
                  event_trace);
      join_any;

      done = 1;
   end

   logic [wd-1:0] din;
   logic reset;
   uwire [wi-1:0] curr_idx_start, prev_length;
   uwire [1:0] curr_trend, prev_trend, last_2_trend;

   trend2 #(wd,wi) mut_trend2
     ( prev_trend, prev_length, curr_trend, last_2_trend,
       curr_idx_start, din, reset, clk );

   initial begin

      automatic int n_err_trend = 0, n_err_last_2_trend = 0;
      automatic int n_err_idx_start = 0, n_err_prev_length = 0;
      automatic int n_err_prev_trend = 0;
      automatic int n_resets = 0, n_changes = 0;
      automatic int heading_shown_cyc = -1000;
      automatic Trend shadow_curr_trend = tr_equal;
      string tr_entry, err_text, heading;
      int shadow_idx, idx_last_change;
      int shadow_prev_length, shadow_idx_start;
      Trend shadow_prev_trend;

      reset = 1;
      din = 0;
      @( negedge clk );
      @( negedge clk );


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

         automatic logic [wd-1:0] din_prev = din;
         automatic Trend shadow_last_2_trend;
         bit err_idx_start, err_prev_length, err_prev_trend;
         bit err_trend, err_last_2_trend, show_errs, have_errs;

         if ( i == 0 || shadow_idx >= max_idx || rand_bern( n_tests/5 ) ) begin

            // Reset
            reset = 1;
            n_resets++;
            din = rand_n( 1 << wd );
            din_prev = din;
            shadow_prev_trend = tr_equal;
            shadow_prev_length = 0;
            shadow_curr_trend = tr_equal;
            shadow_idx = 0;
            shadow_idx_start = shadow_idx;
            idx_last_change = 0;

         end else begin
            shadow_idx++;
         end

         if ( reset ) begin ; end else if ( !shadow_curr_trend ) begin

            // Pick a direction.
            automatic int dir = rand_bern( 2 );
            automatic int abs_delta = 1 + $abs($dist_normal(seed,4,10));
            automatic int delta = dir ? abs_delta : -abs_delta;
            din += delta;
            if ( din > din_prev ) shadow_curr_trend = tr_incr;
            if ( din < din_prev ) shadow_curr_trend = tr_decr;

            shadow_idx_start = idx_last_change;

         end else if ( rand_bern( 3 ) ) begin
            // Switch trend.
            automatic int abs_delta = 1 + $abs($dist_normal(seed,4,10));
            n_changes++;
            if ( shadow_prev_trend === shadow_curr_trend )
              $fatal(1, "Benchmark Error: should have switched trends %h %h.",
                     shadow_prev_trend, shadow_curr_trend);
            shadow_prev_trend = shadow_curr_trend;
            shadow_prev_length = idx_last_change - shadow_idx_start;
            shadow_idx_start = idx_last_change;
            if ( shadow_curr_trend == tr_incr ) begin
               din = din - min(din, abs_delta);
               shadow_curr_trend = tr_decr;
            end else begin
               din = min(din + abs_delta,(1<<wd)-1);
               shadow_curr_trend = tr_incr;
            end
         end else if ( rand_bern( 3 ) == 0 ) begin
            //   Maintain trend.
            automatic int delta = 1 + $abs($dist_normal(seed,4,10));
            automatic int d_try =
              shadow_curr_trend == tr_incr ? din + delta : din - delta;
            din = d_try < 0 ? 0 : d_try > limit_val ? limit_val : d_try;
         end else begin
            //   Equal.
         end

         if ( din != din_prev ) idx_last_change = shadow_idx;

         shadow_last_2_trend =
           din > din_prev ? tr_incr : din < din_prev ? tr_decr : tr_equal;

         @( negedge clk );

         err_prev_trend = shadow_prev_trend !== prev_trend;
         err_prev_length = shadow_prev_length !== prev_length;

         err_trend = shadow_curr_trend !== curr_trend;
         err_last_2_trend = shadow_last_2_trend !== last_2_trend;
         err_idx_start = shadow_idx_start !== curr_idx_start;

         heading = $sformatf
           ("   %5s c %4s  %4s  TrLst2  TrCurr %5s   TrPrev %4s %12s %5s",
               "Cyc", "Idx","Samp",              "IdxSt", "LenP", " ", "Debug");

         tr_entry =
           $sformatf
             ({"Tr %5d %s %4d  %4d  %2b-%3s  ",
               "%2b-%3s %5d   %2b-%3s %4d <- Correct  %5d "},
              cycle, reset ? "R" : " ",
              shadow_idx,

              din,

              shadow_last_2_trend,
              trend_str[shadow_last_2_trend],

              shadow_curr_trend,
              trend_str[shadow_curr_trend],
              shadow_idx_start,

              shadow_prev_trend,
              trend_str[shadow_prev_trend],
              shadow_prev_length,

              mut_trend2.debug_val
              );

         err_text =
           $sformatf("Er %5s %13s %6s  %6s %5s   %6s %4s <- Mod Errors",
                     " ", " ",

                     eob( err_last_2_trend,
                          $sformatf("%2b-%s",last_2_trend,
                                    trend_str_get(last_2_trend)), "2t2t2t" ),

                     eob( err_trend,
                          $sformatf("%2b-%s",curr_trend,
                                    trend_str_get(curr_trend)), "ctctct"),

                     eob( err_idx_start,
                          $sformatf("%0d",curr_idx_start), "ist" ),

                     eob( err_prev_trend,
                          $sformatf("%2b-%s",prev_trend,
                                    trend_str_get(prev_trend)), "------"),
                     eob( err_prev_length,
                          $sformatf("%0d",prev_length), "len" )
                     );

         ev_trace.push_back(tr_entry);
         while ( ev_trace.size() > 10 ) tr_entry = ev_trace.pop_front();

         show_errs = 0;

         if ( err_prev_trend && n_err_prev_trend++ < n_errors_show )
           show_errs = 1;
         if ( err_prev_length && n_err_prev_length++ < n_errors_show )
           show_errs = 1;
         if ( err_idx_start && n_err_idx_start++ < n_errors_show )
           show_errs = 1;

         if ( err_trend && n_err_trend++ < n_errors_show ) show_errs = 1;
         if ( err_last_2_trend && n_err_last_2_trend++ < n_errors_show )
           show_errs = 1;

         have_errs = err_prev_trend || err_prev_length ||
           err_idx_start || err_trend || err_last_2_trend;

         if ( show_errs || cycle < trace_lines_n_wanted ) begin
            automatic bit show_heading = cycle - heading_shown_cyc > 12;
            if ( show_heading ) begin
               $write("\n%s\n",heading);
               heading_shown_cyc = cycle;
            end
            while ( ev_trace.size() ) $write("%s\n",ev_trace.pop_front());
            if ( have_errs )
              $write("%s\n",err_text);
         end

         reset = 0;

      end

      $write("\nErrors last-2-trend: %0d\n", n_err_last_2_trend);
      $write("Errors current trend, idx start: %0d, %0d\n",
             n_err_trend, n_err_idx_start);
      $write("Errors previ trend, length: %0d, %0d\n",
             n_err_prev_trend, n_err_prev_length);
      $write("%s",
             $sformatf({ "Done. Summary: %0d resets, %0d changes, errs: ",
               "(pt,pl,ct,is,lt) %0d,%0d,%0d,%0d,%0d\n"},
             n_resets, n_changes,
             n_err_prev_trend, n_err_prev_length,
             n_err_trend, n_err_idx_start,
             n_err_last_2_trend));

      done = 1;

   end

endmodule

// cadence translate_on