// **** Here's a simple, sequential multiplier.  Very simple, unsigned..
// Not very well tested, play with testbench, use at your own risk, blah blah blah..
//

//
// Unsigned 16-bit multiply (multiply two 16-bit inputs to get a 32-bit output)
//
// Present data and assert start synchronous with clk.
// Assert start for ONLY one cycle.
// Wait N cycles for answer (at most).  Answer will remain stable until next start.
// You may use DONE signal as handshake.
//
// Written by tom coonan
//
module mult16 (clk, resetb, start, done, ain, bin, yout);
parameter N = 16;
input			clk;
input			resetb;
input			start; // Register the ain and bin inputs (they can change afterwards)
input [N-1:0]		ain;
input [N-1:0]		bin;
output [2*N-1:0]	yout;
output			done;

reg [2*N-1:0]		a;
reg [N-1:0]		b;
reg [2*N-1:0]		yout;

reg		done;

always @(posedge clk or negedge resetb) begin
   if (~resetb) begin
      a <= 0;
      b <= 0;
      yout <= 0;
      done <= 1'b1;
   end
   else begin
      // Load will register the input and clear the counter.
      if (start) begin
         a    <= ain;
         b    <= bin;
         yout <= 0;
         done <= 0;
      end
      else begin
         // Go until b is zero
         if (~done) begin
            if (b != 0) begin
               // If '1' then add a to sum
               if (b[0]) begin
                  yout <= yout + a;
               end
               b <= b >> 1;
               a <= a << 1;
               //$display ("a = %b, b = %b, yout = %b", a,b,yout);
            end
            else begin
               done <= 1'b1;
            end
         end
      end
   end
end
endmodule

// synopsys translate_off
//`define TESTMULT16
`ifdef TESTMULT16

module testmult16;
reg clk, resetb, start;
reg [15:0] a;
reg [15:0] b;
wire [31:0] y;
wire done;

mult16 mult16inst (clk, resetb, start, done, a, b, y);

initial begin
   clk = 0;
   forever begin
      #10 clk = ~clk;
   end
end

initial begin
   resetb = 0;
   #30 resetb = 1;
end

integer num_errors;
parameter MAX_TRIALS = 1000;

initial begin
   $dumpfile ("multdiv.vcd");
   $dumpvars (0,testmult16);   
   num_errors = 0;

   #100;

   // Do a bunch of random multiplies
   repeat (MAX_TRIALS) begin
      test_multiply ($random, $random);
   end
   
   // Special cases
   test_multiply ($random, 1);
   test_multiply (1, $random);
   test_multiply ($random, 0);
   test_multiply (0, $random);
   
   $display ("Done.  %0d Errors", num_errors);
   #800;
   $finish;
end

task test_multiply;
   input [15:0] aarg;
   input [15:0] barg;
   
   integer expected_answer;
   
   begin
      if (~done) begin
         $display ("Multiplier is Busy!!");
      end
      else begin
         @(negedge clk);
         start = 1;
         a = aarg;
         b = barg;
         @(negedge clk) start = 0;
         @(posedge done); 
         expected_answer = a*b;
         $display ("%0d * %0d = %0h, Reality = %0h", a, b, y, expected_answer);
         if (y !== expected_answer) begin
            $display ("   FAILURE!");
            num_errors = num_errors + 1;
         end
      end
   end
endtask

endmodule
`endif
     