//
// Integer Multicycle Divide circuit (divide a 16-bit number by a 16-bit number in 16 cycles).
//
// a / b = q with remainder r
//
// Where a is 16-bits,
// Where b is 16 bits
//
// Module is actually parameterized if you want other widths.
//
// *** Test the ranges of values for which you'll use this.  For example, you
//     can't divide FFFF by FF without underflow (overflow?).  Mess with
//     the testbench.  You may need to widen some thing.  ***
//
// The answer is 16-bits and the remainder is also 16-bits.
// After the start pulse, the module requires 16 cycles to complete.
// The q/r outputs stay the same until next start pulse.
// Start pulse should be a single cycle.
// Division by zero results in a quotient equal to FFFF and remainder equal to 'a'.
//
// 
// Written by tom coonan.
//
// Notes:
//    - This ain't fancy.  I wanted something straight-forward quickly.  Go study
//      more elaborate algorithms if you want to optimize area or speed.  If you
//      have an isolated divide and can spare N cycles for N bits; this may meet your needs.
//    - You might want to think more about the sizes of things.  I wanted a basic estimate
//      of gates plus I specifically needed to divide 16-bits (not even full range)
//      by 8-bits.
//    - Handle divide by zero at higher level..
//    - I needed a remainder so I could easily to truncate and rounding stuff,
//      but remove this to save gates if you don't need a remainder.
//    - This is about 800 asic gates (0.25um, Standard Cell, 27Mhz).  27Mhz
//      is my system clock and NOT the maximum it can go..
//    - I tried to keep everything parameterized by N, but I only worked through
//      the N=16 case because that's what I needed...
//
module div16 (clk, resetb, start, a, b, q, r, done);

parameter N = 16;	// a/b = q remainder r, where all operands are N wide.

input		clk;
input		resetb;	// Asynchronous, active low reset.
input		start;	// Pulse this to start the division.
input [N-1:0]	a;	// This is the number we are dividing (the dividend)
input [N-1:0]	b;	// This is the 'divisor'
output [N-1:0]	q;	// This is the 'quotient'
output [N-1:0]	r;	// Here is the remainder.
output		done;	// Will be asserted when q and r are available.

// Registered q
reg [N-1:0]	q;
reg		done;

// Power is the current 2^n bit we are considering.  Power is a shifting
// '1' that starts at the highest power of 2 and goes all the way down
// to ...00001  Shift this until it is zero at which point we stop.
//
reg [N-1:0]	power;

// This is the accumulator.  We are start with the accumulator set to 'a' (the dividend).
// For each (divisor*2^N) term, we see if we can subtract (divisor*2^N) from the accumulator.
// We subtract these terms as long as adding in the term doesn't cause the accumulator
// to exceed a.  When we are done, whatever is left in the accumulator is the remainder.
//
reg [N-1:0]	accum;

// This is the divisor*2^N term.  Essentually, we are taking the divisor ('b'), initially
// shifting it all the way to the left, and shifting it 1 bit at a time to the right.
//
reg [(2*N-1):0]	bpower;

// Remainder will be whatever is left in the accumulator.
assign r = accum;

// Do this addition here for resource sharing.  
// ** Note that 'accum' is N bits wide, but bpower is 2*N-1 bits wide **
//
wire [2*N-1:0] accum_minus_bpower = accum - bpower;

always @(posedge clk or negedge resetb) begin
   if (~resetb) begin
      q <= 0;
      accum <= 0;
      power <= 0;
      bpower <= 0;
      done <= 0;
   end
   else begin
      if (start) begin
         // Reinitialize the divide circuit.
         q      <= 0;
         accum  <= a; // Accumulator initially gets the dividend.  
         power[N-1] <= 1'b1; // We start with highest power of 2 (which is a '1' in MSB)
         bpower <= b << N-1; // Start with highest bpower, which is (divisor * 2^(N-1))
         done <= 0;
      end
      else begin
         // Go until power is zero.
         //
         if (power != 0) begin
            //
            // Can we add this divisor*2^(power) to the accumulator without going negative?
            // Just test the MSB of the subtraction.  If it is '1', then it must be negative.
            //
            if ( ~accum_minus_bpower[2*N-1]) begin
               // Yes!  Set this power of 2 in the quotieny and
               // then actually comitt to the subtraction from our accumulator.
               //
               q     <= q | power;
               accum <= accum_minus_bpower;
            end
            // Regardless, always go to next lower power of 2.
            //
            power  <= power >> 1;
            bpower <= bpower >> 1;
         end
         else begin
            // We're done.  Set done flag.
            done <= 1;
         end
      end
   end
end
endmodule

// synopsys translate_off
module test_div16;
reg		clk;
reg		resetb;
reg		start;
reg [15:0]	a;
reg [15:0]	b;
wire [15:0]	q;
wire [15:0]	r;
wire		done;

integer		num_errors;

div16 div16 (
   .clk(clk),
   .resetb(resetb),
   .start(start),
   .a(a),
   .b(b),
   .q(q),
   .r(r),
   .done(done)
);

initial begin
   num_errors = 0;
   
   start = 0;
   
   // Wait till reset is completely over.
   #200;
   
   // Do some divisions where divisor is constrained to 8-bits and dividend is 16-bits
   $display ("16-bit Dividend, 8-bit divisor");
   repeat (25) begin
      do_divide ($random, $random & 255);
   end
   
   // Do some divisions where divisor is constrained to 12-bits and dividend is 16-bits
   $display ("\n16-bit Dividend, 12-bit divisor");
   repeat (25) begin
      do_divide ($random, $random & 4095);
   end
   
   // Do some divisions where both divisor and dividend is 16-bits
   $display ("\n16-bit Dividend, 16-bit divisor");
   repeat (25) begin
      do_divide ($random, $random);
   end
   
   // Special cases
   $display ("\nSpecial Cases:");
   do_divide (16'hFFFF,  16'hFFFF); // largest possible quotient
   do_divide (312,  1); // divide by 1
   do_divide (  0, 42); // divide 0 by something else
   do_divide (312,  0); // divide by zero

   // That's all.  Summarize the test.
   if (num_errors === 0) begin
      $display ("\n\nSUCCESS.  There were %0d Errors.", num_errors);
   end
   else begin
      $display ("\n\nFAILURE!!  There were %0d Errors.", num_errors);
   end
   
   $finish;
end

task do_divide;
   input [15:0] arga;
   input [15:0] argb;
   
   begin
      a = arga;
      b = argb;
      @(posedge clk);
      #1 start = 1;
      @(posedge clk);
      #1 start = 0;
      while (~done) @(posedge clk);
      #1;
      
      $display ("Circuit: %0d / %0d = %0d, rem = %0d \t\t......... Reality: %0d, rem = %0d", arga, argb, q, r, a/b, a%b);
      if (b !== 0) begin
         if (q !== a/b) begin
            $display ("   Error!  Unexpected Quotient\n\n");
            num_errors = num_errors + 1;
         end
         if (r !== a % b) begin
            $display ("   Error!  Unexpected Remainder\n\n");
            num_errors = num_errors + 1;
         end
      end
   end
endtask

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

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

initial begin
   $dumpfile ("test_div16.vcd");
   $dumpvars (0,test_div16);   
end

endmodule


[ [ [ 'module',
      'div16',
      '(',
      [['clk'], ['resetb'], ['start'], ['a'], ['b'], ['q'], ['r'], ['done']],
      ')',
      ';'],
    [ ['parameter', ['N', '=', '16'], ';'],
      ['input', 'clk', ';'],
      ['input', 'resetb', ';'],
      ['input', 'start', ';'],
      ['input', '[', ['N'], '-', '1', ':', '0', ']', 'a', ';'],
      ['input', '[', ['N'], '-', '1', ':', '0', ']', 'b', ';'],
      ['output', '[', ['N'], '-', '1', ':', '0', ']', 'q', ';'],
      ['output', '[', ['N'], '-', '1', ':', '0', ']', 'r', ';'],
      ['output', 'done', ';'],
      ['reg', '[', ['N'], '-', '1', ':', '0', ']', ['q'], ';'],
      ['reg', ['done'], ';'],
      ['reg', '[', ['N'], '-', '1', ':', '0', ']', ['power'], ';'],
      ['reg', '[', ['N'], '-', '1', ':', '0', ']', ['accum'], ';'],
      [ 'reg',
        '[',
        '(',
        ['2', '*', ['N'], '-', '1'],
        ')',
        ':',
        '0',
        ']',
        ['bpower'],
        ';'],
      ['assign', [['r'], '=', ['accum']], ';'],
      [ 'wire',
        '[',
        '2',
        '*',
        ['N'],
        '-',
        '1',
        ':',
        '0',
        ']',
        [[['accum_minus_bpower'], '=', ['accum'], '-', ['bpower']]],
        ';'],
      [ 'always',
        ['@', '(', ['posedge', ['clk'], 'negedge', ['resetb']], ')'],
        [ 'begin',
          [ [ 'if',
              ['(', '~', ['resetb'], ')'],
              [ 'begin',
                [ [[['q'], '<=', '0'], ';'],
                  [[['accum'], '<=', '0'], ';'],
                  [[['power'], '<=', '0'], ';'],
                  [[['bpower'], '<=', '0'], ';'],
                  [[['done'], '<=', '0'], ';']],
                'end'],
              'else',
              [ 'begin',
                [ [ 'if',
                    ['(', ['start'], ')'],
                    [ 'begin',
                      [ [[['q'], '<=', '0'], ';'],
                        [[['accum'], '<=', ['a']], ';'],
                        [ [ ['power', ['[', ['N'], '-', '1', ']']],
                            '<=',
                            "1 'b 1"],
                          ';'],
                        [[['bpower'], '<=', ['b'], '<<', ['N'], '-', '1'], ';'],
                        [[['done'], '<=', '0'], ';']],
                      'end'],
                    'else',
                    [ 'begin',
                      [ [ 'if',
                          ['(', ['power'], '!=', '0', ')'],
                          [ 'begin',
                            [ [ 'if',
                                [ '(',
                                  '~',
                                  [ 'accum_minus_bpower',
                                    ['[', '2', '*', ['N'], '-', '1', ']']],
                                  ')'],
                                [ 'begin',
                                  [ [[['q'], '<=', ['q'], '|', ['power']], ';'],
                                    [ [['accum'], '<=', ['accum_minus_bpower']],
                                      ';']],
                                  'end']],
                              [[['power'], '<=', ['power'], '>>', '1'], ';'],
                              [[['bpower'], '<=', ['bpower'], '>>', '1'], ';']],
                            'end'],
                          'else',
                          ['begin', [[[['done'], '<=', '1'], ';']], 'end']]],
                      'end']]],
                'end']]],
          'end']]],
    'endmodule'],
  [ ['module', 'test_div16', ';'],
    [ ['reg', ['clk'], ';'],
      ['reg', ['resetb'], ';'],
      ['reg', ['start'], ';'],
      ['reg', '[', '15', ':', '0', ']', ['a'], ';'],
      ['reg', '[', '15', ':', '0', ']', ['b'], ';'],
      ['wire', '[', '15', ':', '0', ']', ['q'], ';'],
      ['wire', '[', '15', ':', '0', ']', ['r'], ';'],
      ['wire', ['done'], ';'],
      ['integer', ['num_errors'], ';'],
      [ 'div16',
        [ ['div16'],
          [ '(',
            ['.', 'clk', '(', ['clk'], ')'],
            ['.', 'resetb', '(', ['resetb'], ')'],
            ['.', 'start', '(', ['start'], ')'],
            ['.', 'a', '(', ['a'], ')'],
            ['.', 'b', '(', ['b'], ')'],
            ['.', 'q', '(', ['q'], ')'],
            ['.', 'r', '(', ['r'], ')'],
            ['.', 'done', '(', ['done'], ')'],
            ')']],
        ';'],
      [ 'initial',
        [ 'begin',
          [ [[['num_errors'], '=', '0'], ';'],
            [[['start'], '=', '0'], ';'],
            [['#', '200'], ';'],
            ['$display', '(', '"16-bit Dividend, 8-bit divisor"', ')', ';'],
            [ 'repeat',
              '(',
              '25',
              ')',
              [ 'begin',
                [ [ 'do_divide',
                    '(',
                    ['$random'],
                    ['$random'],
                    '&',
                    '255',
                    ')',
                    ';']],
                'end']],
            ['$display', '(', '"\\n16-bit Dividend, 12-bit divisor"', ')', ';'],
            [ 'repeat',
              '(',
              '25',
              ')',
              [ 'begin',
                [ [ 'do_divide',
                    '(',
                    ['$random'],
                    ['$random'],
                    '&',
                    '4095',
                    ')',
                    ';']],
                'end']],
            ['$display', '(', '"\\n16-bit Dividend, 16-bit divisor"', ')', ';'],
            [ 'repeat',
              '(',
              '25',
              ')',
              [ 'begin',
                [['do_divide', '(', ['$random'], ['$random'], ')', ';']],
                'end']],
            ['$display', '(', '"\\nSpecial Cases:"', ')', ';'],
            ['do_divide', '(', "16 'h FFFF", "16 'h FFFF", ')', ';'],
            ['do_divide', '(', '312', '1', ')', ';'],
            ['do_divide', '(', '0', '42', ')', ';'],
            ['do_divide', '(', '312', '0', ')', ';'],
            [ 'if',
              ['(', ['num_errors'], '===', '0', ')'],
              [ 'begin',
                [ [ '$display',
                    '(',
                    '"\\n\\nSUCCESS.  There were %0d Errors."',
                    ['num_errors'],
                    ')',
                    ';']],
                'end'],
              'else',
              [ 'begin',
                [ [ '$display',
                    '(',
                    '"\\n\\nFAILURE!!  There were %0d Errors."',
                    ['num_errors'],
                    ')',
                    ';']],
                'end']],
            ['$finish', ';']],
          'end']],
      [ 'task',
        'do_divide',
        ';',
        ['input', '[', '15', ':', '0', ']', 'arga', ';'],
        ['input', '[', '15', ':', '0', ']', 'argb', ';'],
        [ 'begin',
          [ [[['a'], '=', ['arga']], ';'],
            [[['b'], '=', ['argb']], ';'],
            [['@', '(', ['posedge', ['clk']], ')'], ';'],
            [['#', '1'], [[['start'], '=', '1'], ';']],
            [['@', '(', ['posedge', ['clk']], ')'], ';'],
            [['#', '1'], [[['start'], '=', '0'], ';']],
            [ 'while',
              '(',
              '~',
              ['done'],
              ')',
              [['@', '(', ['posedge', ['clk']], ')'], ';']],
            [['#', '1'], ';'],
            [ '$display',
              '(',
              '"Circuit: %0d / %0d = %0d, rem = %0d \\t\\t......... Reality: '
              '%0d, rem = %0d"',
              ['arga'],
              ['argb'],
              ['q'],
              ['r'],
              ['a'],
              '/',
              ['b'],
              ['a'],
              '%',
              ['b'],
              ')',
              ';'],
            [ 'if',
              ['(', ['b'], '!==', '0', ')'],
              [ 'begin',
                [ [ 'if',
                    ['(', ['q'], '!==', ['a'], '/', ['b'], ')'],
                    [ 'begin',
                      [ [ '$display',
                          '(',
                          '"   Error!  Unexpected Quotient\\n\\n"',
                          ')',
                          ';'],
                        [[['num_errors'], '=', ['num_errors'], '+', '1'], ';']],
                      'end']],
                  [ 'if',
                    ['(', ['r'], '!==', ['a'], '%', ['b'], ')'],
                    [ 'begin',
                      [ [ '$display',
                          '(',
                          '"   Error!  Unexpected Remainder\\n\\n"',
                          ')',
                          ';'],
                        [[['num_errors'], '=', ['num_errors'], '+', '1'], ';']],
                      'end']]],
                'end']]],
          'end'],
        'endtask'],
      [ 'initial',
        [ 'begin',
          [ [[['clk'], '=', '0'], ';'],
            [ 'forever',
              [ 'begin',
                [ [['#', '10'], [[['clk'], '=', '1'], ';']],
                  [['#', '10'], [[['clk'], '=', '0'], ';']]],
                'end']]],
          'end']],
      [ 'initial',
        [ 'begin',
          [ [[['resetb'], '=', '0'], ';'],
            [['#', '133'], [[['resetb'], '=', '1'], ';']]],
          'end']],
      [ 'initial',
        [ 'begin',
          [ ['$dumpfile', '(', '"test_div16.vcd"', ')', ';'],
            ['$dumpvars', '(', '0', ['test_div16'], ')', ';']],
          'end']]],
    'endmodule']]
