// **** 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
     

[ [ [ 'module',
      'mult16',
      '(',
      [['clk'], ['resetb'], ['start'], ['done'], ['ain'], ['bin'], ['yout']],
      ')',
      ';'],
    [ ['parameter', ['N', '=', '16'], ';'],
      ['input', 'clk', ';'],
      ['input', 'resetb', ';'],
      ['input', 'start', ';'],
      ['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'], 'negedge', ['resetb']], ')'],
        [ 'begin',
          [ [ 'if',
              ['(', '~', ['resetb'], ')'],
              [ 'begin',
                [ [[['a'], '<=', '0'], ';'],
                  [[['b'], '<=', '0'], ';'],
                  [[['yout'], '<=', '0'], ';'],
                  [[['done'], '<=', "1 'b 1"], ';']],
                'end'],
              'else',
              [ 'begin',
                [ [ 'if',
                    ['(', ['start'], ')'],
                    [ 'begin',
                      [ [[['a'], '<=', ['ain']], ';'],
                        [[['b'], '<=', ['bin']], ';'],
                        [[['yout'], '<=', '0'], ';'],
                        [[['done'], '<=', '0'], ';']],
                      'end'],
                    'else',
                    [ 'begin',
                      [ [ 'if',
                          ['(', '~', ['done'], ')'],
                          [ 'begin',
                            [ [ 'if',
                                ['(', ['b'], '!=', '0', ')'],
                                [ 'begin',
                                  [ [ 'if',
                                      ['(', ['b', ['[', '0', ']']], ')'],
                                      [ 'begin',
                                        [ [ [ ['yout'],
                                              '<=',
                                              ['yout'],
                                              '+',
                                              ['a']],
                                            ';']],
                                        'end']],
                                    [[['b'], '<=', ['b'], '>>', '1'], ';'],
                                    [[['a'], '<=', ['a'], '<<', '1'], ';']],
                                  'end'],
                                'else',
                                [ 'begin',
                                  [[[['done'], '<=', "1 'b 1"], ';']],
                                  'end']]],
                            'end']]],
                      'end']]],
                'end']]],
          'end']]],
    'endmodule'],
  [ ['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'], ';'],
            [ 'repeat',
              '(',
              ['MAX_TRIALS'],
              ')',
              [ 'begin',
                [['test_multiply', '(', ['$random'], ['$random'], ')', ';']],
                'end']],
            ['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']]
