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

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


`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 0
//
 /// Look over the modules and enum below.
//
//

 /// Declare Useful Enumeration Constants.
//
typedef enum
  { Char_space = 32, Char_0 = 48, Char_9 = 57,
    Char_A = 65, Char_Z = 90, Char_a = 97, Char_z = 122 }
  Chars_Special;
//
// See digit_valid_09 to see how these constants can be used.


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

   assign x = s ? a1 : a0;

endmodule



//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
 ///   Replace procedural code in atoi1 with modules as described in handout.
 ///
//
//     [✔] atoi1 must instantiate and use a digit_valid_az module.
//     [✔] atoi1 must instantiate and use a char_to_uc module.
//     [✔] atoi1 must instantiate and use mux2 modules.
//     [✔] The completed atoi1 must not have procedural code (always_comb, etc.)
//
//     [✔] Complete module digit_valid_az.
//     [✔] Complete module char_to_uc.
//
//     [✔] 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 value.
//
//     [✔] Code must be written clearly.
//     [✔] Pay attention to cost and performance.


module char_to_uc( output uwire [7:0] uc, input uwire [7:0] c );
   uwire is_lc = c >= Char_a && c <= Char_z;
   uwire [7:0] uc_if_lc = c - Char_a + Char_A;
   /// SOLUTION
   mux2 #(8) m( uc, is_lc, c, uc_if_lc );
endmodule

module digit_valid_az
  #( int r = 11, vw = $clog2(r) )
   ( output uwire valid, output uwire [vw-1:0] val, input uwire [7:0] char );
   /// SOLUTION
   assign val = 10 + char - Char_A;
   assign valid = char >= Char_A && char < Char_A + r - 10;
endmodule

module atoi1
  #( int r = 32, w = $clog2(r) )
   ( output logic [w-1:0] val,
     output logic is_digit,
     input uwire [7:0] char );

   /// SOLUTION
   logic [w-1:0] val_09, val_az, val_n;
   logic is_09, is_az;

   digit_valid_09 #(r,w) v09( is_09, val_09, char );
   uwire [7:0] char_uc;
   char_to_uc tuc(char_uc,char);
   digit_valid_az #(r,w) vaz( is_az, val_az, char_uc );

   uwire [w-1:0] z = 0;
   mux2 #(w) mval(val_n,is_09,val_az,val_09);
   mux2 #(w) mval0(val,is_digit,z,val_n);

   assign is_digit = is_09 || is_az;

endmodule


module digit_valid_09
  #( int r = 9, vw = $clog2(r) )
   ( output uwire valid, output uwire [vw-1:0] val, input uwire [7:0] char );
   assign val = char - Char_0;
   assign valid = char >= Char_0 && char <= Char_9 && char < Char_0 + r;
endmodule


 /// Reference Module -- Do Not Modify
//
module atoi1_behavioral
  #( int r = 32, w = $clog2(r) )
   ( output logic [w-1:0] val,
     output logic is_digit,
     input uwire [7:0] char );

   logic [7:0] char_uc;
   logic [w-1:0] val_09, val_az;
   logic is_09, is_az;

   always_comb begin

      char_uc = char >= Char_a && char <= Char_z
        ? char - Char_a + Char_A : char;

      val_09 = char - Char_0;
      val_az = 10 + char_uc - Char_A;

      is_09 = char >= Char_0 && char <= Char_9 && char < Char_0 + r;
      is_az = char_uc >= Char_A && char_uc < Char_A + r - 10;
      is_digit = is_09 || is_az;

      if ( is_09 )       val = val_09;
      else if ( is_az )  val = val_az;
      else               val = 0;

   end

endmodule


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

// cadence translate_off

module testbench;

   localparam int nradices = 6;
   localparam int radices[nradices] =
              '{ 4, 8, 10, 14, 16, 19 };

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

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

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

endmodule


module testbench_r
  #( int r = 16 )
   ( output logic done, input uwire tstart );

   localparam int wd = 8;
   localparam int w = $clog2(r**wd);
   localparam int w1 = 2 * $clog2(r);
   localparam int ntests = 500;

   logic [1:0][7:0] str;

   uwire [w1-1:0] val1;
   uwire is_digit1;

   atoi1 #(r,w1) a1( val1, is_digit1, str[0] );

   logic [7:0] non_digit[256];

   function string to_string(input logic [w1-1:0] val);

      automatic string result = "";
      if ( val == 0 ) result = "0";
      while ( val ) begin

         automatic int d = val % r;
         automatic int v = d < 10 ? d + Char_0 : d - 10 + Char_A;
         val = val / r;
         result = { string'(v), result };
      end
      to_string = result;
   endfunction

   initial begin
      automatic int nd_size = 0;
      automatic int rm10 = r > 10 ? 10 : r;
      automatic bit err_silent = 0;
      for ( int i=32; i<128; i++ ) begin
         if ( i >= Char_0 && i < Char_0 + rm10 ) continue;
         if ( i >= Char_A && i < Char_A + r - 10 ) continue;
         if ( i >= Char_a && i < Char_a + r - 10 ) continue;
         non_digit[nd_size++] = i;
      end
      str[1] = Char_space;

      wait( tstart );

      for ( int tt=0; tt<1; tt++ ) begin

         automatic bit single_char = tt == 0;
         automatic bit space_pad = single_char || tt == 1;
         automatic int nttests = single_char ? 256 : ntests;
         automatic int n_err = 0, n_lerr = 0;
         automatic string ttype =
           single_char ? "Single_Char (SC)"
             : space_pad ? "Space_Pad (SP)" : "General (GE)";
         automatic string abbrev =
           single_char ? "SC" : space_pad ? "SP" : "GE";

      for ( int i=0; i<nttests; i++ ) begin
         automatic int len = single_char ? 1 : 1 + {$random} % wd;
         automatic logic [w1-1:0] sval = 0;
         automatic bit is_09 = i >= Char_0 && i <= Char_9 && i < Char_0 + r;
         automatic int iuc =
           i >= Char_a && i <= Char_z ? i - Char_a + Char_A : i;
         automatic bit is_az = iuc >= Char_A && iuc < Char_A + r - 10;
         automatic bit sis_digit = is_09 || is_az;

         str[0] = i;
         sval = is_09 ? i - Char_0 : is_az ? 10 + iuc - Char_A : 0;

         #1;

         if ( sval !== val1 ) begin
            n_err++;
            if ( !err_silent && n_err < 5 )
              $write("R %2d  Error val 'h%h or %s != %s (correct) for string \"%s\"\n",
                     r, val1, to_string(val1), to_string(sval),
                     string'(str));
         end
         if ( sis_digit !== is_digit1 ) begin
            n_lerr++;
            if ( !err_silent && n_lerr < 5 )
              $write("R %2d  Error is_digit %h != %0d (correct) for string \"%s\"\n",
                     r, is_digit1, sis_digit, string'(str) );
         end

         #1;

      end

         $write("Radix %2d, done with %0d tests, %0d val errors, %0d is_digit errors.\n",
             r, nttests, n_err, n_lerr);

            testbench.t_errs += n_err + n_lerr;
         if ( n_err + n_lerr ) err_silent = 1;
      end
      done = 1;
   end


endmodule