////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2019 Homework 2
//

 /// Assignment  https://www.ece.lsu.edu/koppel/v/2019/hw02.pdf

 /// Instructions:
  //
  // (1) Find the undergraduate workstation laboratory, room 2241 Patrick
  //     F. Taylor Hall. Machines to use are in the back.
  //
  // (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/2019/hw02
  //     to a directory ~/hw02 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/8299595/
  //    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.
  //
  // Unix Help (Very outdated.  Alternatives welcome.)
  //      https://www.ece.lsu.edu/koppel/v/4ltrwrd/


`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
 ///    Complete clz_tree so that it computes the clz of its input recursively.
//
//     [ ] Split the input between two recursive instantiations ..
//         .. and properly combine the results.
//     [ ] Don't forget the terminal case, maybe for w == 1.
//     [ ] For maximum credit, avoid any use of adders ..
//         .. by making the width of hi module a power of 2.
//
//     [ ] Make sure that port connections are the correct size ..
//         .. mismatched ports are Verilog errors in this assignment.
//     [ ] Do not make port widths larger than needed.
//     [ ] Make sure that the testbench does not report errors.
//     [ ] Module must be synthesizable. Use command: genus -files syn.tcl
//
//     [ ] As always, avoid costly, slow, and confusing code.

module clz_tree
  #( int w = 19,
     int ww = $clog2(w+1) )
   ( output uwire [ww-1:0] nlz,
     input uwire [w-1:0] a );




endmodule


 /// A Behavioral CLZ Description
module clz
  #( int w = 19,
     int ww = $clog2(w+1) )
   ( output var logic [ww-1:0] nlz,
     input uwire logic [w-1:0] a );

   uwire [w:0] aa = { a, 1'b1 };
   always_comb for ( int i=0; i<=w; i++ ) if ( aa[i] ) nlz = w-i;

endmodule


 /// Solution To Homework 1 Using Generate Statements
module mult_tree
  #( int wa = 16, int wb = 16, int wp = wa + wb )
   ( output uwire [wp:1] prod,
     input uwire [wa:1] a,
     input uwire [wb:1] b );

   if ( wa == 1 ) begin

      assign prod = a ? b : 0;

   end else begin

      localparam int wn = wa / 2;
      localparam int wx = wb + wn;

      uwire [wx:1] prod_lo, prod_hi;

      mult_tree #(wn,wb) mlo( prod_lo, a[wn:1], b);
      mult_tree #(wn,wb) mhi( prod_hi, a[wa:wn+1], b);
      assign prod = prod_lo + ( prod_hi << wn );

   end

endmodule




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

// cadence translate_off

module testbench;

   // The widths (values of w) at which the modules will be instantiated.
   //
   localparam int widths[] = { 1, 2, 5, 8, 13, 15, 17 };

   //  localparam int nw = widths.size();
   localparam int nw = 7; // Cadence, please fix this.
   initial if ( nw != widths.size() )
     $fatal(1,"Constant nw should be %0d.\n",widths.size() );

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

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

   // Instantiate a testbench at each size.
   //
   for ( genvar i=0; i<nw; i++ )
     testbench_n #(widths[i]) t2( .done(d[i]), .start(d[i-1]) );

endmodule

module testbench_n
  #( int w = 20 )
   ( output logic done, input uwire start );

   localparam int ww = $clog2(w+1);

   localparam int n_tests = w * 10;

   uwire [ww:1] nlz;
   logic [w-1:0] a;
   clz_tree #(w) c0(nlz,a);

   initial begin

      automatic int n_errs = 0;

      wait( start );

      $write("** Starting tests for width %0d.\n",w);

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

         automatic int lz = {t} % ( w + 1 );
         a = { 1'b1, (w)'({$random}) } >> ( lz + 1 );

         #1;

         if ( nlz !== lz ) begin
            n_errs++; testbench.t_errs++;
            if ( testbench.t_errs < 5 || n_errs < 2 )
              $write("Error for width %2d: input %h:  %d != %0d (correct).\n",
                     w, a, nlz, lz);
         end

      end

      $write("Width %0d, done with %0d tests, %0d errors.\n",w,n_tests,n_errs);

      done = 1;

   end

endmodule

// cadence translate_on