////////////////////////////////////////////////////////////////////////////////
///
/// Solution to LSU EE 3755 Fall 2001 Homework 5
///


////////////////////////////////////////////////////////////////////////////////
/// Problem 1 Solution
///

module itod(double,int);
   input [51:0] int;
   output [63:0] double;

   reg [51:0]    abs_int;

   reg           sign;
   reg [10:0]    exp;
   reg [51:0]    frac;

   wire [5:0]    lz;

   clz60 c1(lz,{abs_int,8'b0});

   assign        double = {sign, exp, frac };

   always @( int or lz ) begin

      sign = int[51];
      abs_int = sign ? -int : int;
      frac = abs_int << lz + 1;

      exp = int ? 1023 + 51 - lz : 0;

   end

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Problem 2 Solution
///


module pop64(p,n,clk);
   input [63:0] n;
   input        clk;
   output [6:0] p;

   reg [6:0]   p;

   reg [31:0]   n32;
   reg [5:0]    pfirst;
   wire [5:0]   p32;
   reg          state;

   pop32 my_instance_of_module_pop32(p32,n32);

   initial state = 0;

   always @( posedge clk )
     case( state )
       0: begin p = pfirst + p32;  n32 = n[63:32];  state = 1;  end
       1: begin pfirst = p32;      n32 = n[31:0];   state = 0;  end
     endcase

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Modules Needed for Solutions
//

module clz60(lz,r);
   input [59:0] r;
   output [5:0] lz;

   // Set LZ to the number of leading zeros in r.

   reg [5:0]    lz;
   reg [63:0]   rcopy, mask;
   integer      i;
   reg [7:0]    j;

   always @( r ) begin

      rcopy = {r,4'h8};

      for(i=5; i>=0; i=i-1) begin

         for(j=0; j<64; j=j+1) mask[63-j] = j[i];

         lz[i] = ( rcopy & ~mask ) == 0;

         rcopy = lz[i] ? rcopy & mask : rcopy & ~mask;

      end

   end

endmodule


module pop32(p,n);
   input [31:0] n;
   output [5:0] p;

   // Set p to the number of 1's in n.

   reg [5:0]    p;

   integer     i;

   always @( n ) begin
      p = 0;
      for( i=0; i<32; i=i+1 ) p = p + n[i];
   end

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Problem 1 Testbench
///


module test_itod();

   parameter stop_on_error = 1;
   parameter verbose = 0;

   reg [51:0] int;
   wire [63:0] dbl;

   wire [5:0]  lz;
   itod i1(dbl,int);
   clz60 c(lz,{int,8'b0});

   integer     i, z, err;
   real       dshadow;

   initial begin

      err = 0;

      for(i=0; i<1000; i=i+1) begin

         z = ( $random >> 1 )% 52;
         int[50:0] = i ? {$random,$random} >> z : 0;
         int[51] = 0;
         if( int && $random & 1 ) begin
            dshadow = int * -1.0;
            int = -int;
         end else begin
            dshadow = int;
         end

         #1;

         if( verbose )
           $display("int 0x%x lz %d, Conversion %.f,  %h = %h",
                    int,lz,dshadow, dbl, $realtobits(dshadow));

         if( dbl !== $realtobits(dshadow) ) begin

            $display("FAIL: Wrong conversion %f,  %h != %h",
                     dshadow, dbl, $realtobits(dshadow));
            err = err + 1;
            if( stop_on_error) $stop;

         end

      end

      if( err == 0 ) $write("PASS: ");
      $display("All tests done, %d error%s detected.",
               err, err == 1 ? "" : "s");

   end

endmodule


module test_pop();

   parameter show_count = 4;
   parameter stop_on_error = 1;

   reg [63:0] n;
   wire [6:0] p;
   reg        clk;

   pop64 p64(p,n,clk);

   reg [6:0]  shadow_p;
   integer    errs, i;

   always begin clk = 0; #5; clk = 1; #5; end

   initial begin

      errs = 0;
      n = 64'b0;

      @( negedge clk );

      for(i=0; i<1000; i=i+1) begin:B

         integer s;
         reg bit;

         bit = $random;
         shadow_p = 0;

         for(s=0; s < 64; s = s ) begin:A
            integer r, e;

            case( 1 )
              i==0: begin bit = 0; r = 64; end
              i==1: begin bit = 1; r = 64; end
              i & 1: r = ( $random & 'h7 ) + 1;
              default:
                r = ( $random & 'h3f ) + 1;
            endcase

            if( r + s > 64 ) r = 64 - s;

            for( e = s + r; s < e; s = s + 1 ) n[s] = bit;

            if( bit ) shadow_p = shadow_p + r;

            bit = ~bit;

         end

         repeat( 4 ) @( negedge clk );

         if( p !== shadow_p ) begin

            errs = errs + 1;

            if( show_count >= errs )
            $display("FAIL: Wrong output for 0x%h: %d (correct) != %d (wrong)\n",
                     n, shadow_p, p);

            if( stop_on_error ) $stop;

         end

      end

      if( errs == 0 ) $write("PASS: ");
      $display("All %d tests completed, %d error%s found.\n",
               i, errs, errs == 1 ? "" : "s");
      $stop;

   end

endmodule