A signed binary integer adder/subtractor, with carry_in
, carry_out
,
overflow
, and all the intermediate carries
into each bit position (see
Carry-In Calculator for their uses).
Addition/subtraction is selected with add_sub
: 0 for an add
(A+B+carry_in
), and 1 for a subtract (A-B-carry_in
). This assignment
conveniently matches the convention of sign bits. Note that the overflow
bit is only meaningful for signed numbers. For unsigned numbers, use
carry_out
instead.
On FPGAs, you are much better off letting the CAD tool infer the
add/subtract circuitry from the +
or -
operator itself, rather than
structurally describing it in Boolean logic, as the latter may not get
mapped to the fast, dedicated ripple-carry hardware. Wrapping all this into
a module hides the width adjustments necessary to get a warning-free
synthesis of carry logic, and enables correct carry and overflow
calculations.
Because we handle the carry bits ourselves and do everything through an unsigned addition, we don't depend on the tricky Verilog behaviour where all terms of an expression must be declared signed else the expression is silently evaluated as unsigned!
`default_nettype none module Adder_Subtractor_Binary #( parameter WORD_WIDTH = 0 ) ( input wire add_sub, // 0/1 -> A+B/A-B input wire carry_in, input wire [WORD_WIDTH-1:0] A, input wire [WORD_WIDTH-1:0] B, output reg [WORD_WIDTH-1:0] sum, output reg carry_out, output wire [WORD_WIDTH-1:0] carries, output reg overflow ); localparam ZERO = {WORD_WIDTH{1'b0}}; localparam ONE = {{WORD_WIDTH-1{1'b0}},1'b1}; initial begin sum = ZERO; carry_out = 1'b0; overflow = 1'b0; end
Extend the carry_in
to the extended word width, as both signed (0 or -1)
and unsigned (0 or 1) words, so we don't have width mismatches nor rely on
sign extension, which is full of pitfalls, and would trigger useless
warnings in the CAD tools.
wire [WORD_WIDTH-1:0] carry_in_extended_unsigned; wire [WORD_WIDTH-1:0] carry_in_extended_signed; Width_Adjuster #( .WORD_WIDTH_IN (1), .SIGNED (0), .WORD_WIDTH_OUT (WORD_WIDTH) ) extend_carry_in_unsigned ( .original_input (carry_in), .adjusted_output (carry_in_extended_unsigned) ); Width_Adjuster #( .WORD_WIDTH_IN (1), .SIGNED (1), .WORD_WIDTH_OUT (WORD_WIDTH) ) extend_carry_in_signed ( .original_input (carry_in), .adjusted_output (carry_in_extended_signed) );
Depending on the value of add_sub
, generate the bit-negation of B
and
the necessary offset for B
's 2's-complement arithmetic negation, and
select the correct carry_in
value. We do this separately to have the
negated B
available later for the calculations of the carries
and of
the overflow
.
reg [WORD_WIDTH-1:0] B_selected = ZERO; reg [WORD_WIDTH-1:0] negation_offset = ZERO; reg [WORD_WIDTH-1:0] carry_in_selected = ZERO; always @(*) begin B_selected = (add_sub == 1'b0) ? B : ~B; negation_offset = (add_sub == 1'b0) ? ZERO : ONE; carry_in_selected = (add_sub == 1'b0) ? carry_in_extended_unsigned : carry_in_extended_signed; end
And add as usual, with subtraction expressed as A+((~B)+1)
, so as to
generate the correct carries
for each bit position.
Since the left-hand side is one bit wider to hold carry_out
, all other
terms are implicitly extended to that width (see Verilog LRM, IEEE
1364-2001, Section 4.4, "Expression bit lengths"). However, since I avoid
implicit width extension as a way to reduce warnings and prevent bugs,
let's prepend a zero to all the unsigned right-hand terms to make all
widths match and force a simple, unsigned addition.
We could have done this more concisely by first widening all terms to
WORD_WIDTH+1
, then selecting addition/subtraction in one line, but we
need the possibly negated B
later for the carries
and overflow
calculation.
always @(*) begin {carry_out, sum} = {1'b0, A} + {1'b0, B_selected} + {1'b0, negation_offset} + {1'b0, carry_in_selected}; end
Finally, recover the carry into each bit from the selected addition
terms. The first bit of carries
is the same as carry_in
. We must do
this here rather than in the enclosing module, since if you are
subtracting, the negated B
term is not externally available.
CarryIn_Binary #( .WORD_WIDTH (WORD_WIDTH) ) per_bit ( .A (A), .B (B_selected), .sum (sum), .carryin (carries) );
And compute the signed overflow, which happens when the carry into and out from the MSB do not agree.
always @(*) begin overflow = (carries [WORD_WIDTH-1] != carry_out); end endmodule