Given two integers, A and B, derives all the possible arithmetic
predictates (equal, greater-than, less-than-equal, etc...) as both signed
and unsigned comparisons.
This code implements "How the Computer Sets the Comparison Predicates" in
Section 2-12 of Henry S. Warren, Jr.'s Hacker's
Delight, which describes how to compute all the
integer comparisons, based on the condition flags generated after
a (2's-complement) subtraction A-B.
`default_nettype none
module Arithmetic_Predicates_Binary
#(
parameter WORD_WIDTH = 0
)
(
input wire [WORD_WIDTH-1:0] A,
input wire [WORD_WIDTH-1:0] B,
output reg A_eq_B,
output reg A_lt_B_unsigned,
output reg A_lte_B_unsigned,
output reg A_gt_B_unsigned,
output reg A_gte_B_unsigned,
output reg A_lt_B_signed,
output reg A_lte_B_signed,
output reg A_gt_B_signed,
output reg A_gte_B_signed
);
localparam ZERO = {WORD_WIDTH{1'b0}};
initial begin
A_eq_B = 1'b0;
A_lt_B_unsigned = 1'b0;
A_lte_B_unsigned = 1'b0;
A_gt_B_unsigned = 1'b0;
A_gte_B_unsigned = 1'b0;
A_lt_B_signed = 1'b0;
A_lte_B_signed = 1'b0;
A_gt_B_signed = 1'b0;
A_gte_B_signed = 1'b0;
end
First, let's subtract B from A, and get the the carry-out and overflow bits.
wire [WORD_WIDTH-1:0] difference;
wire carry_out;
wire overflow_signed;
Adder_Subtractor_Binary
#(
.WORD_WIDTH (WORD_WIDTH)
)
subtraction
(
.add_sub (1'b1), // 0/1 -> A+B/A-B
.carry_in (1'b0),
.A (A),
.B (B),
.sum (difference),
.carry_out (carry_out),
// verilator lint_off PINCONNECTEMPTY
.carries (),
// verilator lint_on PINCONNECTEMPTY
.overflow (overflow_signed)
);
We now have enough information to compute all the arithmetic predicates. Note that in 2's-complement subtraction, the meaning of the carry-out bit is reversed, and that special care must be taken for signed comparisons to distinguish the carry-out from an overflow. This code takes advantage of the sequential evaluation of blocking assignments in a Verilog procedural block to re-use and optimize the logic expressions.
reg negative = 1'b0;
always @(*) begin
negative = (difference[WORD_WIDTH-1] == 1'b1);
A_eq_B = (difference == ZERO);
A_lt_B_unsigned = (carry_out == 1'b0);
A_lte_B_unsigned = (A_lt_B_unsigned == 1'b1) || (A_eq_B == 1'b1);
A_gte_B_unsigned = (carry_out == 1'b1);
A_gt_B_unsigned = (A_gte_B_unsigned == 1'b1) && (A_eq_B == 1'b0);
A_lt_B_signed = (negative != overflow_signed);
A_lte_B_signed = (A_lt_B_signed == 1'b1) || (A_eq_B == 1'b1);
A_gte_B_signed = (negative == overflow_signed);
A_gt_B_signed = (A_gte_B_signed == 1'b1) && (A_eq_B == 1'b0);
end
endmodule