261 lines
7.9 KiB
Verilog
261 lines
7.9 KiB
Verilog
//
|
|
// Copyright (c) 1999 Thomas Coonan (tcoonan@mindspring.com)
|
|
//
|
|
// This source code is free software; you can redistribute it
|
|
// and/or modify it in source code form under the terms of the GNU
|
|
// General Public License as published by the Free Software
|
|
// Foundation; either version 2 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
//
|
|
//
|
|
// 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\nPASSED");
|
|
end
|
|
else begin
|
|
$display ("\n\nFAILED - 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
|