Files
iverilog/ivtest/contrib/div16.v
T
Stephen Williams cea237b407 Add ivtest to the iverilog source tree
By adding ivtest to the iverilog source tree, it is easier to keep
the regression test synchronized with the source that is being tested.
This should be especially helpful for PRs that add a new feature, and
have a matching ivtest PR with the regression test for that feature.
2022-01-15 10:18:50 -08:00

261 lines
7.9 KiB
Verilog

//
// Copyright (c) 1999 Thomas Coonan ([email protected])
//
// 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