Signed Divider in Verilog: Signed Integer Division with Code

A signed divider computes the quotient and remainder of two two’s-complement numbers. Hardware dividers almost always work on unsigned magnitudes, so the practical recipe is: strip the signs off both operands, divide the absolute values with an ordinary unsigned restoring (or non-restoring) core, then re-apply the sign rules to the quotient and remainder at the end. This page gives working, simulator-verified signed divider Verilog code for both a clocked (one-bit-per-cycle) divider and a fully combinational version.

The subtle part is never the division itself; it is the sign convention. Verilog’s built-in / and % operators use truncating division, where the quotient rounds toward zero and the remainder carries the sign of the dividend. The examples below reproduce exactly that convention and self-check against it, including the awkward corners: a negative divisor, a zero dividend, and divide-by-zero.

This clocked divider latches a 16-bit signed dividend and an 8-bit signed divisor, converts both to magnitudes, runs a 16-cycle unsigned restoring loop (one quotient bit per clock), and fixes the signs in a final state before pulsing done. The testbench drives all four sign combinations and checks the quotient and remainder against Verilog’s own signed / and %, and verifies the division identity dividend = divisor*quotient + remainder.

// Example 1: clocked signed restoring divider, 16-bit / 8-bit
module signed_divider(
  input                    clk,
  input                    rst,
  input                    start,
  input  signed [15:0]     dividend,
  input  signed [7:0]      divisor,
  output reg signed [15:0] quotient,
  output reg signed [15:0] remainder,
  output reg               done
);
  localparam IDLE = 2'd0, RUN = 2'd1, FIN = 2'd2;
  reg [1:0]  state;
  reg [15:0] dvd_mag;   // |dividend| (unsigned magnitude)
  reg [7:0]  dsr_mag;   // |divisor|
  reg        q_neg, r_neg;
  reg [15:0] q_mag;     // quotient magnitude
  reg [15:0] rem_acc;   // running partial remainder
  reg [4:0]  cnt;       // bit counter 16..0

  // shift next dividend MSB into the partial remainder
  wire [15:0] rem_shift = {rem_acc[14:0], dvd_mag[15]};

  always @(posedge clk) begin
    if (rst) begin
      state     <= IDLE;
      done      <= 1'b0;
      quotient  <= 16'sd0;
      remainder <= 16'sd0;
    end else begin
      case (state)
        IDLE: begin
          done <= 1'b0;
          if (start) begin
            dvd_mag <= dividend[15] ? (~dividend + 16'sd1) : dividend;
            dsr_mag <= divisor[7]   ? (~divisor  + 8'sd1)  : divisor;
            q_neg   <= dividend[15] ^ divisor[7];
            r_neg   <= dividend[15];
            q_mag   <= 16'd0;
            rem_acc <= 16'd0;
            cnt     <= 5'd16;
            state   <= RUN;
          end
        end
        RUN: begin
          if (cnt == 5'd0) begin
            state <= FIN;
          end else begin
            if (rem_shift >= {8'd0, dsr_mag}) begin
              rem_acc <= rem_shift - {8'd0, dsr_mag};
              q_mag   <= {q_mag[14:0], 1'b1};
            end else begin
              rem_acc <= rem_shift;
              q_mag   <= {q_mag[14:0], 1'b0};
            end
            dvd_mag <= {dvd_mag[14:0], 1'b0};
            cnt     <= cnt - 5'd1;
          end
        end
        FIN: begin
          quotient  <= q_neg ? (~q_mag   + 16'd1) : q_mag;
          remainder <= r_neg ? (~rem_acc + 16'd1) : rem_acc;
          done      <= 1'b1;
          state     <= IDLE;
        end
        default: state <= IDLE;
      endcase
    end
  end
endmodule

module tb;
  reg               clk, rst, start;
  reg  signed [15:0] dividend;
  reg  signed [7:0]  divisor;
  wire signed [15:0] quotient, remainder;
  wire               done;

  integer errors;

  signed_divider dut(
    .clk(clk), .rst(rst), .start(start),
    .dividend(dividend), .divisor(divisor),
    .quotient(quotient), .remainder(remainder), .done(done)
  );

  always #5 clk = ~clk;

  task run_case;
    input signed [15:0] a;
    input signed [7:0]  b;
    reg   signed [15:0] ref_q;
    reg   signed [15:0] ref_r;
    begin
      @(negedge clk);
      dividend = a; divisor = b; start = 1'b1;
      @(negedge clk);
      start = 1'b0;
      // wait for the done pulse
      @(posedge done);
      @(negedge clk);
      ref_q = a / b;   // Verilog signed division truncates toward zero
      ref_r = a % b;   // remainder takes the sign of the dividend
      if (quotient !== ref_q || remainder !== ref_r) begin
        $display("FAIL a=%0d b=%0d : q=%0d (exp %0d) r=%0d (exp %0d)",
                 a, b, quotient, ref_q, remainder, ref_r);
        errors = errors + 1;
      end else if ((a !== (b*quotient + remainder))) begin
        $display("FAIL identity a=%0d b=%0d : b*q+r=%0d",
                 a, b, (b*quotient + remainder));
        errors = errors + 1;
      end else begin
        $display("PASS a=%0d b=%0d -> q=%0d r=%0d (a=b*q+r, sign(r)=sign(a))",
                 a, b, quotient, remainder);
      end
    end
  endtask

  initial begin
    clk = 1'b0; rst = 1'b1; start = 1'b0;
    dividend = 16'sd0; divisor = 8'sd1; errors = 0;
    @(negedge clk); @(negedge clk);
    rst = 1'b0;

    run_case( 16'sd1000, 8'sd7 );    // + +
    run_case( 16'sd1000, -8'sd7 );   // + -
    run_case(-16'sd1000, 8'sd7 );    // - +
    run_case(-16'sd1000, -8'sd7 );   // - -
    run_case( 16'sd9,    8'sd3 );    // exact, no remainder
    run_case(-16'sd9,    8'sd4 );    // - +, has remainder
    run_case( 16'sd255, -8'sd16 );   // + -
    run_case(-16'sd30000, 8'sd127 ); // large negative dividend

    if (errors == 0)
      $display("ALL TESTS PASSED");
    else
      $display("%0d TEST(S) FAILED", errors);
    $finish;
  end
endmodule
PASS a=1000 b=7 -> q=142 r=6 (a=b*q+r, sign(r)=sign(a))
PASS a=1000 b=-7 -> q=-142 r=6 (a=b*q+r, sign(r)=sign(a))
PASS a=-1000 b=7 -> q=-142 r=-6 (a=b*q+r, sign(r)=sign(a))
PASS a=-1000 b=-7 -> q=142 r=-6 (a=b*q+r, sign(r)=sign(a))
PASS a=9 b=3 -> q=3 r=0 (a=b*q+r, sign(r)=sign(a))
PASS a=-9 b=4 -> q=-2 r=-1 (a=b*q+r, sign(r)=sign(a))
PASS a=255 b=-16 -> q=-15 r=15 (a=b*q+r, sign(r)=sign(a))
PASS a=-30000 b=127 -> q=-236 r=-28 (a=b*q+r, sign(r)=sign(a))
ALL TESTS PASSED

Every PASS line confirms the DUT's quotient and remainder match Verilog's reference signed / and % for that operand pair, and that the division identity a = b*q + r holds exactly. The four leading cases exercise all sign combinations (+ +, + -, - +, - -), proving the sign-fix logic is correct; the final line, ALL TESTS PASSED, prints only when the error counter is zero.

This combinational variant is parameterized on operand width (here 8-bit / 4-bit) and produces the quotient, remainder, and a div_by_zero flag in a single evaluation using an unrolled restoring loop. Divide-by-zero follows the RISC-V convention (quotient = -1, remainder = dividend). The testbench checks directed corner cases and then sweeps the entire legal input space exhaustively.

// Example 2: combinational signed divider, parameterized widths.
// Divide-by-zero follows the RISC-V convention: quotient = -1, remainder = dividend.
module signed_divider_comb #(
  parameter DW = 8,   // dividend width (signed)
  parameter SW = 4    // divisor width (signed)
)(
  input      signed [DW-1:0] dividend,
  input      signed [SW-1:0] divisor,
  output reg signed [DW-1:0] quotient,
  output reg signed [DW-1:0] remainder,
  output reg                 div_by_zero
);
  reg  signed [DW-1:0] dsr_ext;   // divisor sign-extended to DW
  reg         [DW-1:0] dvd_mag;   // |dividend|
  reg         [DW-1:0] dsr_mag;   // |divisor|
  reg         [DW-1:0] q_mag;
  reg         [DW:0]   rem_acc;   // one guard bit
  reg                  q_neg, r_neg;
  integer i;

  always @(*) begin
    if (divisor == {SW{1'b0}}) begin
      div_by_zero = 1'b1;
      quotient    = {DW{1'b1}};   // -1
      remainder   = dividend;     // undivided dividend
    end else begin
      div_by_zero = 1'b0;
      dsr_ext = divisor;          // signed->signed assign sign-extends
      dvd_mag = dividend[DW-1] ? (~dividend + 1'b1) : dividend;
      dsr_mag = dsr_ext[DW-1]   ? (~dsr_ext  + 1'b1) : dsr_ext;
      q_neg   = dividend[DW-1] ^ divisor[SW-1];
      r_neg   = dividend[DW-1];

      q_mag   = {DW{1'b0}};
      rem_acc = {(DW+1){1'b0}};
      // restoring division, MSB first
      for (i = DW-1; i >= 0; i = i - 1) begin
        rem_acc = {rem_acc[DW-1:0], dvd_mag[i]};
        if (rem_acc >= {1'b0, dsr_mag}) begin
          rem_acc = rem_acc - {1'b0, dsr_mag};
          q_mag[i] = 1'b1;
        end else begin
          q_mag[i] = 1'b0;
        end
      end

      quotient  = q_neg ? (~q_mag + 1'b1) : q_mag;
      remainder = r_neg ? (~rem_acc[DW-1:0] + 1'b1) : rem_acc[DW-1:0];
    end
  end
endmodule

module tb;
  reg  signed [7:0] dividend;
  reg  signed [3:0] divisor;
  wire signed [7:0] quotient, remainder;
  wire              div_by_zero;

  integer errors;

  signed_divider_comb #(.DW(8), .SW(4)) dut(
    .dividend(dividend), .divisor(divisor),
    .quotient(quotient), .remainder(remainder), .div_by_zero(div_by_zero)
  );

  task run_case;
    input signed [7:0] a;
    input signed [3:0] b;
    reg   signed [7:0] ref_q, ref_r;
    begin
      dividend = a; divisor = b;
      #1;
      if (b == 4'sd0) begin
        // defined divide-by-zero behaviour (known answer)
        if (div_by_zero !== 1'b1 || quotient !== -8'sd1 || remainder !== a) begin
          $display("FAIL div0 a=%0d : dbz=%b q=%0d r=%0d", a, div_by_zero, quotient, remainder);
          errors = errors + 1;
        end else begin
          $display("PASS div0 a=%0d -> dbz=1 q=-1 r=%0d", a, remainder);
        end
      end else begin
        ref_q = a / b;
        ref_r = a % b;
        if (div_by_zero !== 1'b0 || quotient !== ref_q || remainder !== ref_r) begin
          $display("FAIL a=%0d b=%0d : q=%0d (exp %0d) r=%0d (exp %0d)",
                   a, b, quotient, ref_q, remainder, ref_r);
          errors = errors + 1;
        end else if (a !== (b*quotient + remainder)) begin
          $display("FAIL identity a=%0d b=%0d : b*q+r=%0d", a, b, (b*quotient + remainder));
          errors = errors + 1;
        end else begin
          $display("PASS a=%0d b=%0d -> q=%0d r=%0d", a, b, quotient, remainder);
        end
      end
    end
  endtask

  integer ia, ib;
  initial begin
    errors = 0;
    // directed corner cases
    run_case( 8'sd100,  4'sd7 );   // + +
    run_case( 8'sd100, -4'sd7 );   // + -   (negative divisor)
    run_case(-8'sd100,  4'sd7 );   // - +
    run_case(-8'sd100, -4'sd7 );   // - -
    run_case( 8'sd0,    4'sd5 );   // zero dividend
    run_case( 8'sd0,   -4'sd5 );   // zero dividend, negative divisor
    run_case( 8'sd50,   4'sd0 );   // divide by zero
    run_case(-8'sd50,   4'sd0 );   // divide by zero, negative dividend
    run_case(-8'sd128,  4'sd7 );   // most-negative dividend

    // exhaustive sweep over the full legal input space
    for (ia = -128; ia <= 127; ia = ia + 1) begin
      for (ib = -8; ib <= 7; ib = ib + 1) begin
        if (ib != 0) begin
          dividend = ia[7:0]; divisor = ib[3:0];
          #1;
          if (quotient !== (dividend / divisor) ||
              remainder !== (dividend % divisor) ||
              dividend !== (divisor*quotient + remainder)) begin
            $display("FAIL sweep a=%0d b=%0d q=%0d r=%0d", dividend, divisor, quotient, remainder);
            errors = errors + 1;
          end
        end
      end
    end

    if (errors == 0)
      $display("ALL TESTS PASSED (directed cases + full %0dx%0d sweep)", 256, 15);
    else
      $display("%0d TEST(S) FAILED", errors);
    $finish;
  end
endmodule
PASS a=100 b=7 -> q=14 r=2
PASS a=100 b=-7 -> q=-14 r=2
PASS a=-100 b=7 -> q=-14 r=-2
PASS a=-100 b=-7 -> q=14 r=-2
PASS a=0 b=5 -> q=0 r=0
PASS a=0 b=-5 -> q=0 r=0
PASS div0 a=50 -> dbz=1 q=-1 r=50
PASS div0 a=-50 -> dbz=1 q=-1 r=-50
PASS a=-128 b=7 -> q=-18 r=-2
ALL TESTS PASSED (directed cases + full 256x15 sweep)

The directed PASS lines confirm all sign combinations, a zero dividend (quotient and remainder both 0), and the two divide-by-zero cases (flag set, quotient = -1, remainder = dividend). The sweep then drives every one of the 256 signed dividends against all 15 non-zero signed divisors and checks each against Verilog's reference / and % plus the a = b*q + r identity, so the closing ALL TESTS PASSED line means the combinational divider is exhaustively correct over its entire input space.

Truncating vs flooring division: the sign rules that matter

There are two common conventions for integer division of signed numbers, and they only differ when exactly one operand is negative.

  • Truncating division rounds the quotient toward zero. The remainder then takes the sign of the dividend. This is what C, SystemVerilog, and Verilog's / and % operators do. Example: -7 / 2 = -3 remainder -1.
  • Flooring (Euclidean-style) division rounds the quotient toward negative infinity. The remainder then takes the sign of the divisor (and is always non-negative for a positive divisor). Example: -7 / 2 = -4 remainder +1 under floored division.

The signed divider Verilog code on this page implements truncating division, matching hardware ISAs such as RISC-V and most CPU DIV instructions. If you need floored results instead, add a correction step after the truncating divide: when the remainder is non-zero and the operand signs differ, subtract one from the quotient and add the divisor to the remainder.

How the magnitude-then-fix-sign approach works

Both designs follow the same three-step structure:

  • Strip the signs. Take the two's-complement absolute value of each operand with ~x + 1 when the sign bit is set. Record the dividend sign and divisor sign separately.
  • Divide the magnitudes. Run an ordinary unsigned restoring divider. Restoring division shifts the dividend one bit at a time into a partial remainder, compares it against the divisor, and sets the quotient bit to 1 (subtracting) when the divisor fits, or 0 otherwise.
  • Re-apply the signs. The quotient is negative when the operand signs differ, so q_neg = sign(dividend) XOR sign(divisor). The remainder always carries the dividend's sign under truncating division, so r_neg = sign(dividend). Negate each magnitude when its sign flag is set.

Why not just use the signed subtract directly?

You can build a non-restoring signed divider that operates on two's-complement values without taking magnitudes, but the control logic for the final remainder correction is fiddly and easy to get wrong on the sign corners. Magnitude-then-fix keeps the datapath unsigned and isolates all the sign handling into three small expressions, which is far easier to verify, exactly as the exhaustive sweep in Example 2 demonstrates.

Remainder sign convention and divide-by-zero

The single most common bug in a signed divider is getting the remainder sign wrong. Under truncating division the invariant is:

dividend == divisor * quotient + remainder
0 <= |remainder| < |divisor|
sign(remainder) == sign(dividend)   (when remainder != 0)

Both testbenches assert the first identity on every case, which catches any sign-fix mistake immediately.

Hardware division has no architectural exception for divide-by-zero, so the divider must produce a defined output. Example 2 adopts the RISC-V convention: a signed divide by zero returns a quotient of all ones (-1) and a remainder equal to the dividend. Other reasonable conventions exist (returning zero, or asserting an error flag and holding the previous value), but whatever you choose must be deterministic and documented, because downstream logic will sample it. Never leave the output as x.

Latency and synthesis notes

The two designs trade area against speed in the classic way.

  • Clocked restoring divider (Example 1): one quotient bit per clock, so an N-bit dividend takes roughly N cycles plus a couple of states for setup and sign-fix. For 16 bits that is about 18 cycles. It uses a single subtractor and comparator, so the area and critical path are small and the clock can run fast. Use this when throughput is modest and area or timing closure matters.
  • Combinational divider (Example 2): produces a result in zero cycles but unrolls N subtract-compare stages into one long combinational chain. The critical path grows with the width, so for anything beyond a handful of bits you will usually register it as a pipeline rather than leave it purely combinational. It is the right choice for narrow operands or when you genuinely need a same-cycle answer.

Synthesis pointers

  • The magnitude step ~x + 1 on the most-negative value (for example -128 in 8 bits) produces a magnitude of 128, which needs one more bit than the signed input. The datapath registers are sized unsigned and wide enough to hold it; keep that in mind if you re-parameterize.
  • Writing a / b and a % b directly in RTL will also synthesize, but the tool infers a generic divider whose structure and latency you do not control. Rolling your own, as here, lets you pick the cycle count and pipeline depth.
  • Keep the testbench's reference operators out of synthesizable code; they belong only in the tb module.

FAQ

Does Verilog's / operator already handle signed division?

Yes, if both operands are declared signed, / and % perform truncating signed division. The custom divider matters when you need a specific latency, a pipeline, a divide-by-zero policy, or control over the generated hardware rather than whatever the synthesizer infers.

Why does -7 % 2 give -1 and not +1 in my divider?

Because truncating division makes the remainder take the sign of the dividend. -7 = 2*(-3) + (-1). A +1 remainder would be the floored convention, which pairs with a quotient of -4. Both are mathematically valid; pick the one your ISA or downstream logic expects.

How do I extend this to wider operands?

For the combinational version, just change the DW and SW parameters; the restoring loop and sign logic scale automatically. For the clocked version, widen the registers and set the bit counter to the new dividend width. Re-run the self-checking testbench after any width change.

Is restoring or non-restoring division better in hardware?

Non-restoring division avoids the restore subtraction and can be slightly faster per step, but restoring division is simpler to read and verify and is a fine default for teaching and for low-to-moderate throughput. For high-radix or high-throughput needs, look at SRT division, which retires multiple quotient bits per cycle.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD