div16.v.txt
上传用户:saul_905
上传日期:2013-11-27
资源大小:184k
文件大小:7k
源码类别:

VHDL/FPGA/Verilog

开发平台:

Visual C++

  1. //
  2. // Integer Multicycle Divide circuit (divide a 16-bit number by a 16-bit number in 16 cycles).
  3. //
  4. // a / b = q with remainder r
  5. //
  6. // Where a is 16-bits,
  7. // Where b is 16 bits
  8. //
  9. // Module is actually parameterized if you want other widths.
  10. //
  11. // *** Test the ranges of values for which you'll use this.  For example, you
  12. //     can't divide FFFF by FF without underflow (overflow?).  Mess with
  13. //     the testbench.  You may need to widen some thing.  ***
  14. //
  15. // The answer is 16-bits and the remainder is also 16-bits.
  16. // After the start pulse, the module requires 16 cycles to complete.
  17. // The q/r outputs stay the same until next start pulse.
  18. // Start pulse should be a single cycle.
  19. // Division by zero results in a quotient equal to FFFF and remainder equal to 'a'.
  20. //
  21. // 
  22. // Written by tom coonan.
  23. //
  24. // Notes:
  25. //    - This ain't fancy.  I wanted something straight-forward quickly.  Go study
  26. //      more elaborate algorithms if you want to optimize area or speed.  If you
  27. //      have an isolated divide and can spare N cycles for N bits; this may meet your needs.
  28. //    - You might want to think more about the sizes of things.  I wanted a basic estimate
  29. //      of gates plus I specifically needed to divide 16-bits (not even full range)
  30. //      by 8-bits.
  31. //    - Handle divide by zero at higher level..
  32. //    - I needed a remainder so I could easily to truncate and rounding stuff,
  33. //      but remove this to save gates if you don't need a remainder.
  34. //    - This is about 800 asic gates (0.25um, Standard Cell, 27Mhz).  27Mhz
  35. //      is my system clock and NOT the maximum it can go..
  36. //    - I tried to keep everything parameterized by N, but I only worked through
  37. //      the N=16 case because that's what I needed...
  38. //
  39. module div16 (clk, resetb, start, a, b, q, r, done);
  40. parameter N = 16; // a/b = q remainder r, where all operands are N wide.
  41. input clk;
  42. input resetb; // Asynchronous, active low reset.
  43. input start; // Pulse this to start the division.
  44. input [N-1:0] a; // This is the number we are dividing (the dividend)
  45. input [N-1:0] b; // This is the 'divisor'
  46. output [N-1:0] q; // This is the 'quotient'
  47. output [N-1:0] r; // Here is the remainder.
  48. output done; // Will be asserted when q and r are available.
  49. // Registered q
  50. reg [N-1:0] q;
  51. reg done;
  52. // Power is the current 2^n bit we are considering.  Power is a shifting
  53. // '1' that starts at the highest power of 2 and goes all the way down
  54. // to ...00001  Shift this until it is zero at which point we stop.
  55. //
  56. reg [N-1:0] power;
  57. // This is the accumulator.  We are start with the accumulator set to 'a' (the dividend).
  58. // For each (divisor*2^N) term, we see if we can subtract (divisor*2^N) from the accumulator.
  59. // We subtract these terms as long as adding in the term doesn't cause the accumulator
  60. // to exceed a.  When we are done, whatever is left in the accumulator is the remainder.
  61. //
  62. reg [N-1:0] accum;
  63. // This is the divisor*2^N term.  Essentually, we are taking the divisor ('b'), initially
  64. // shifting it all the way to the left, and shifting it 1 bit at a time to the right.
  65. //
  66. reg [(2*N-1):0] bpower;
  67. // Remainder will be whatever is left in the accumulator.
  68. assign r = accum;
  69. // Do this addition here for resource sharing.  
  70. // ** Note that 'accum' is N bits wide, but bpower is 2*N-1 bits wide **
  71. //
  72. wire [2*N-1:0] accum_minus_bpower = accum - bpower;
  73. always @(posedge clk or negedge resetb) begin
  74.    if (~resetb) begin
  75.       q <= 0;
  76.       accum <= 0;
  77.       power <= 0;
  78.       bpower <= 0;
  79.       done <= 0;
  80.    end
  81.    else begin
  82.       if (start) begin
  83.          // Reinitialize the divide circuit.
  84.          q      <= 0;
  85.          accum  <= a; // Accumulator initially gets the dividend.  
  86.          power[N-1] <= 1'b1; // We start with highest power of 2 (which is a '1' in MSB)
  87.          bpower <= b << N-1; // Start with highest bpower, which is (divisor * 2^(N-1))
  88.          done <= 0;
  89.       end
  90.       else begin
  91.          // Go until power is zero.
  92.          //
  93.          if (power != 0) begin
  94.             //
  95.             // Can we add this divisor*2^(power) to the accumulator without going negative?
  96.             // Just test the MSB of the subtraction.  If it is '1', then it must be negative.
  97.             //
  98.             if ( ~accum_minus_bpower[2*N-1]) begin
  99.                // Yes!  Set this power of 2 in the quotieny and
  100.                // then actually comitt to the subtraction from our accumulator.
  101.                //
  102.                q     <= q | power;
  103.                accum <= accum_minus_bpower;
  104.             end
  105.             // Regardless, always go to next lower power of 2.
  106.             //
  107.             power  <= power >> 1;
  108.             bpower <= bpower >> 1;
  109.          end
  110.          else begin
  111.             // We're done.  Set done flag.
  112.             done <= 1;
  113.          end
  114.       end
  115.    end
  116. end
  117. endmodule
  118. // synopsys translate_off
  119. module test_div16;
  120. reg clk;
  121. reg resetb;
  122. reg start;
  123. reg [15:0] a;
  124. reg [15:0] b;
  125. wire [15:0] q;
  126. wire [15:0] r;
  127. wire done;
  128. integer num_errors;
  129. div16 div16 (
  130.    .clk(clk),
  131.    .resetb(resetb),
  132.    .start(start),
  133.    .a(a),
  134.    .b(b),
  135.    .q(q),
  136.    .r(r),
  137.    .done(done)
  138. );
  139. initial begin
  140.    num_errors = 0;
  141.    
  142.    start = 0;
  143.    
  144.    // Wait till reset is completely over.
  145.    #200;
  146.    
  147.    // Do some divisions where divisor is constrained to 8-bits and dividend is 16-bits
  148.    $display ("16-bit Dividend, 8-bit divisor");
  149.    repeat (25) begin
  150.       do_divide ($random, $random & 255);
  151.    end
  152.    
  153.    // Do some divisions where divisor is constrained to 12-bits and dividend is 16-bits
  154.    $display ("n16-bit Dividend, 12-bit divisor");
  155.    repeat (25) begin
  156.       do_divide ($random, $random & 4095);
  157.    end
  158.    
  159.    // Do some divisions where both divisor and dividend is 16-bits
  160.    $display ("n16-bit Dividend, 16-bit divisor");
  161.    repeat (25) begin
  162.       do_divide ($random, $random);
  163.    end
  164.    
  165.    // Special cases
  166.    $display ("nSpecial Cases:");
  167.    do_divide (16'hFFFF,  16'hFFFF); // largest possible quotient
  168.    do_divide (312,  1); // divide by 1
  169.    do_divide (  0, 42); // divide 0 by something else
  170.    do_divide (312,  0); // divide by zero
  171.    // That's all.  Summarize the test.
  172.    if (num_errors === 0) begin
  173.       $display ("nnSUCCESS.  There were %0d Errors.", num_errors);
  174.    end
  175.    else begin
  176.       $display ("nnFAILURE!!  There were %0d Errors.", num_errors);
  177.    end
  178.    
  179.    $finish;
  180. end
  181. task do_divide;
  182.    input [15:0] arga;
  183.    input [15:0] argb;
  184.    
  185.    begin
  186.       a = arga;
  187.       b = argb;
  188.       @(posedge clk);
  189.       #1 start = 1;
  190.       @(posedge clk);
  191.       #1 start = 0;
  192.       while (~done) @(posedge clk);
  193.       #1;
  194.       
  195.       $display ("Circuit: %0d / %0d = %0d, rem = %0d tt......... Reality: %0d, rem = %0d", arga, argb, q, r, a/b, a%b);
  196.       if (b !== 0) begin
  197.          if (q !== a/b) begin
  198.             $display ("   Error!  Unexpected Quotientnn");
  199.             num_errors = num_errors + 1;
  200.          end
  201.          if (r !== a % b) begin
  202.             $display ("   Error!  Unexpected Remaindernn");
  203.             num_errors = num_errors + 1;
  204.          end
  205.       end
  206.    end
  207. endtask
  208. initial begin
  209.    clk = 0;
  210.    forever begin
  211.       #10 clk = 1;
  212.       #10 clk = 0;
  213.    end
  214. end
  215. initial begin
  216.    resetb = 0;
  217.    #133 resetb = 1;
  218. end
  219. initial begin
  220.    $dumpfile ("test_div16.vcd");
  221.    $dumpvars (0,test_div16);   
  222. end
  223. endmodule