////////////////////////////////////////////////////////////////////////////////
///
/// Template for LSU EE 3755 Spring 2002 Homework 4
///

 /// Due: Monday, 25 March 2002

 /// Name:

 /// Instructions:
  //
  // Copy this to a file named hw04.v to directory ~/hw in your
  // class account. (~ is your home directory.)  Use this
  // file for your solution.  Your entire solution should be in
  // this file.
  //
  // Do not rename the modules in this file and be sure to use the
  // directory and filename given above.

  // Local copy of this file: 
  //     /home/classes/ee3755/com/v/hw04.v
  //
  // Instruction on Simulator, Emacs, etc:
  //    http://www.ece.lsu.edu/ee3755/proc.html
  //
  // Documentation for Simulator, Unix, etc.
  //    http://www.ece.lsu.edu/ee3755/ref.html


  // See problems below for further instructions.

  // Warning: Problem 3 is more difficult than 1 and 2.


 /// Grading Criteria
//
//   Passes the testbench.
//   If it fails, partial credit will be given based on the type of
//   problem.  A large amount of credit will be deducted for
//   compilation errors.
//
//   Follows the guidelines.
//   Be sure to instantiate the correct number of modules per module.
//
//   Coded cleanly.
//   Points will be deducted for code that is correct but much more
//   complex, unreadable, or inefficient than it has to be.
//   For example, points would be deducted for the second assign below.
`ifdef XXX
assign x = a & b;  // Use assign on this line instead of one on line below.
assign x = a == 1'b1 ? ( b == 1'b1 ? 1'b1 : 1'b0 ) : ( b == 1'b1 ? 1'b0 : 1'b0 );
`endif


////////////////////////////////////////////////////////////////////////////////
/// Problem 1
///

// Complete the module below so that it performs 18 bit x 9 bit
// unsigned integer multiplication using a radix-8 multiplier using
// the same handshaking as mult_18x9_sim (and other modules presented
// in class).
//
// Hint: The solution is a straightforward extension of
// imult_ord_radix_4 from l07
// http://www.ece.lsu.edu/ee3755/2002/l07.html, also further down in
// this file) to radix 8.


module mult_radix_8(prod,ready,cand,multiplier,start,clk);
   input [17:0]  cand;
   input [8:0]   multiplier;
   input         start, clk;
   output [26:0] prod;
   output        ready;

   /// Implement a 18 x 9 unsigned integer multiplier
   //
   //  [ ] Can use structural and synthesizable behavioral code.
   //  [ ] Do not instantiate other modules.
   //  [ ] Solution can use the addition operator.
   //  [ ] Solution CANNOT use the multiplication operator.
   //  [ ] Test using module test_8.
   //
   //  Can be solved by adding 8 lines to imult_ord_radix_4 (in this
   //  file) and modifying several other lines.

   
endmodule


////////////////////////////////////////////////////////////////////////////////
/// Problem 2
///

// Complete the module below so that it performs an 18 x 18 bit
// unsigned integer multiplication using two mult_prob_2 18 x 9 bit
// multiplier modules.  All modules use the same handshaking.


module mult_faster(prod,ready,cand,multiplier,start,clk);
   input [17:0]  cand;
   input [17:0]  multiplier;
   input         start, clk;
   output [35:0] prod;
   output        ready;

   // Implement an 18 x 18 bit multiplier using two 18 x 9 bit multipliers.
   //
   // [ ] Instantiate TWO mult_prob_2 modules.
   // [ ] Can use structural and synthesizable procedural code.
   // [ ] Can use + operator, and can assume addition is very fast.
   // [ ] The two 18 x 9 bit multipliers must work simultaneously.
   // [ ] Assume that the two multipliers take the same amount of time.
   // [ ] Test using test_faster.
   //
   // Can be solved with just one line of code, plus declarations.  
   

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Problem 3
///

// Complete the module below so that it performs an 18 x 18 bit
// unsigned integer multiplication using one mult_prob_3 18 x 9 bit
// multiplier module.  All modules use the same handshaking.
//
// Hint: Use a state machine to control the instantiated multiplier.


module mult_cheaper(prod,ready,cand,multiplier,start,clk);
   input [17:0]  cand;
   input [17:0]  multiplier;
   input         start, clk;
   output [35:0] prod;
   output        ready;

   // Implement an 18 x 18 bit multiplier using one 18 x 9 bit multiplier.
   //
   // [ ] Instantiate ONE mult_prob_3 module.
   // [ ] Do not assume that mult_prob_3 takes a fixed amount of time.
   // [ ] Can use structural and synthesizable procedural code.
   // [ ] Can use + operator, and can assume addition is very fast.
   // [ ] Test using test_cheaper.
   //
   // A straightforward solution uses 18 lines of code, plus declarations.  



endmodule


////////////////////////////////////////////////////////////////////////////////
/// Radix-4 Multiplication Module
///

// The solution to Problem 1 above can be based on the module below.

module imult_ord_radix_4(prod,ready,multiplicand,multiplier,start,clk);

   input [15:0]  multiplicand, multiplier;
   input         start, clk;
   output        prod;
   output        ready;

   reg [32:0]    product;
   wire [31:0]   prod = product[31:0];

   reg [4:0]     bit; 
   wire          ready = !bit;

   reg [17:0]    pp;
   
   initial bit = 0;

   wire [17:0]   multiplicand_X_1 = {1'b0,multiplicand};
   wire [17:0]   multiplicand_X_2 = {multiplicand,1'b0};
   wire [17:0]   multiplicand_X_3 = multiplicand_X_2 + multiplicand_X_1;

   always @( posedge clk )

     if( ready && start ) begin

        bit     = 8;
        product = { 16'd0, multiplier };
        
     end else if( bit ) begin

        case ( {product[1:0]} )
          2'd0: pp = {2'b0, product[31:16] };
          2'd1: pp = {2'b0, product[31:16] } + multiplicand_X_1;
          2'd2: pp = {2'b0, product[31:16] } + multiplicand_X_2;
          2'd3: pp = {2'b0, product[31:16] } + multiplicand_X_3;
        endcase

        product = { pp, product[15:2] };
        bit     = bit - 1;

     end

endmodule


////////////////////////////////////////////////////////////////////////////////
/// Modules Instantiated In Solutions
///

// exemplar translate_off

module mult_prob_2(prod,ready,cand,multiplier,start,clk);
   input [17:0]  cand;
   input [8:0]   multiplier;
   input         start, clk;
   output [26:0] prod;
   output        ready;

   mult_18x9_sim #(0) the_worlds_mult(prod,ready,cand,multiplier,start,clk);

endmodule


module mult_prob_3(prod,ready,cand,multiplier,start,clk);
   input [17:0]  cand;
   input [8:0]   multiplier;
   input         start, clk;
   output [26:0] prod;
   output        ready;

   mult_18x9_sim #(1) the_worlds_mult(prod,ready,cand,multiplier,start,clk);

endmodule


module mult_18x9_sim(prod,ready,cand,multiplier,start,clk);

   input [17:0]  cand;
   input [8:0]   multiplier;
   input         start, clk;
   output [26:0] prod;
   output        ready;

   parameter     random_delay = 0;

   reg [26:0]    prod;

   reg [3:0]     bit;  // Not necessarily size needed for other modules.
   wire          ready = !bit;
   
   initial bit <= 0;

   always @( posedge clk )

     if( ready && start ) begin

        bit <= random_delay ? 1 + $random : 3;
        
     end else if( bit ) begin

        bit <= bit - 1;
        if( bit == 1 ) prod <= cand * multiplier;

     end

endmodule


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

module test_faster();
   testmults #(0) tm();
endmodule

module test_cheaper();
   testmults #(1) tm();
endmodule

module test_8();
   testmults #(2) tm();
endmodule

module testmults();

   parameter DUT = 0;

   parameter stop_on_error = 1;

   parameter DUT_faster = 0;
   parameter DUT_cheaper = 1;
   parameter DUT_eight = 2;

   reg [35:0] product;
   wire [35:0] product_c, product_f;
   wire [26:0]  product_8;
   wire        ready_c, ready_f, ready_8;
   reg         ready;
   reg [17:0]  multiplier;
   reg [17:0]  multiplicand;
   reg         start, clk;

   reg [35:0]  shadow_product;
   integer     i;

   always @( product_c or product_f or product_8
             or ready_c or ready_f or ready_8 )
     case( DUT )
       DUT_faster:  begin  product = product_f;  ready = ready_f; end
       DUT_cheaper: begin  product = product_c;  ready = ready_c; end
       DUT_eight:   begin  product = product_8;  ready = ready_8; end
     endcase

   mult_radix_8 m8(product_8,ready_8,multiplicand,multiplier[8:0],start,clk);
   mult_cheaper mc(product_c,ready_c,multiplicand,multiplier,start,clk);
   mult_faster  mf(product_f,ready_f,multiplicand,multiplier,start,clk);

   always #10 clk = !clk;

   real cycles;
   initial begin cycles = 0; forever @( posedge clk ) cycles <= cycles + 1; end

   reg [17:0] lier_mask;
   reg [7*8-1:0] name;
   real          start_time;
   real          cpm;

   integer       err;

   initial begin

      case( DUT )
        DUT_faster:  begin  lier_mask = 18'o777777; name = "Faster"; end
        DUT_cheaper: begin  lier_mask = 18'o777777; name = "Cheaper"; end
        DUT_eight:   begin  lier_mask = 18'o000777; name = "Radix 8"; end
      endcase

      clk = 0;
      start = 0;
      err = 0;

      #25;

      start_time = cycles;

      $display("Starting test for %s",name);

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

         wait( ready );

         #5;

         multiplier = $random & lier_mask;
         multiplicand = $random & 18'o777777;

         shadow_product = multiplier * multiplicand;
         
         #1;

         start = 1;
         wait( !ready );
         start = 0;
         wait( ready );
         #1;

         if( shadow_product !== product ) begin
            $display("FAIL: %s %o x %o = %o(correct) != %o\n",
                     name,
                     multiplier, multiplicand,
                     shadow_product, product );
            err = err + 1;
            if( stop_on_error && err >= stop_on_error ) $stop;
         end

      end

      cpm = (cycles - start_time) / ( i ? i : 1 );

      $display("Finished at speed of %f cycles per multiplication.", cpm);

      case ( DUT )
        DUT_faster:
          if( cpm > 5.1 ) begin
             $display("FAIL: Faster multiplier taking too long.");
             err = err + 1;
          end else if ( cpm < 3.0 ) begin
             $display("FAIL: Faster multiplier too fast.");
             err = err + 1;
          end
        DUT_eight:
          if( cpm > 4.1 ) begin
             $display("FAIL: Radix 8 multiplier taking too long.");
             err = err + 1;
          end else if ( cpm < 3.0 ) begin
             $display("FAIL: Radix 8 multiplier too fast.");
             err = err + 1;
          end
        DUT_cheaper:
          if( cpm > 22 ) begin
             $display("FAIL: Cheaper multiplier taking too long.");
             err = err + 1;
          end else if ( cpm < 15 ) begin
             $display("FAIL: Cheaper multiplier too fast.");
             err = err + 1;
          end
      endcase

      if( err )
        $display("FAIL: %s, encountered %d errors.",name,err);
      else
        $display("PASS: %s, no errors found.",name);

      $display("Done with tests.");
      
      $stop;

   end

endmodule

// exemplar translate_on