blob: 41fd7f11408f3671fec50398f6ca220e61fb8ea6 [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
module add_sub(
input wire [31:0] in_x,
input wire [31:0] in_y,
input wire operation,
input wire [2:0] round_mode,
output wire [31:0] out_z,
output wire [4:0] exceptions);
wire sign_x, sign_y;
wire [7:0] exp_x, exp_y, exp_a, exp_b, subnorm_exp, norm_exp;
wire [22:0] mant_x, mant_y, mant_a, mant_b;
wire x_is_zero, x_is_inf, x_is_qNaN, x_is_sNaN;
wire y_is_zero, y_is_inf, y_is_qNaN, y_is_sNaN;
wire a_is_subnorm, b_is_subnorm;
wire hd_bit_a, hd_bit_b;
wire [26:0] arg1, arg2;
wire [26:0] rt_shift_mant;
wire [26:0] lt_shft_mant, norm_sum;
wire [26:0] mant_sum;
wire [23:0] rounded_mant;
wire [31:0] inter_result, of_result;
wire comp, exp_shft_comp;
wire operator_y, subtract;
wire cout, cout_check;
wire [7:0] exp_diff;
wire [4:0] ld_zero_cnt, inc_dec_exp_amt;
wire [7:0] inter_shft_amt, shft_amt;
wire round_of;
wire sign_z;
wire [7:0] exp_z;
wire [22:0] mant_z;
wire invalid_operation;
wire divide_by_zero;
wire overflow;
wire underflow;
wire inexact;
wire [9:0] x_check_res, y_check_res;
// checking inputs for special values
special_check #(8, 24) check_x (.in(in_x), .result(x_check_res));
special_check #(8, 24) check_y (.in(in_y), .result(y_check_res));
assign x_is_zero = x_check_res[3] | x_check_res[4];
assign x_is_inf = x_check_res[0] | x_check_res[7];
assign x_is_qNaN = x_check_res[9];
assign x_is_sNaN = x_check_res[8];
assign y_is_zero = y_check_res[3] | y_check_res[4];
assign y_is_inf = y_check_res[0] | y_check_res[7];
assign y_is_qNaN = y_check_res[9];
assign y_is_sNaN = y_check_res[8];
// unpacking inputs
assign sign_x = in_x[31];
assign sign_y = in_y[31];
assign exp_x = in_x[30:23];
assign exp_y = in_y[30:23];
assign mant_x = in_x[22:0];
assign mant_y = in_y[22:0];
// comparing both numbers
assign comp = (exp_y > exp_x) ? 1'b1 : (exp_y != exp_x) ? 1'b0 : (mant_y > mant_x);
// determining operation to be performed
assign operator_y = sign_y ^ operation;
assign subtract = sign_x ^ operator_y;
// determining output sign
assign sign_z = x_is_zero ? (operator_y) : (y_is_zero ? sign_x :
(subtract ? (comp ? operator_y : sign_x) : sign_x));
// swapping operands
assign {exp_a, mant_a} = comp ? {exp_y, mant_y} : {exp_x, mant_x};
assign {exp_b, mant_b} = comp ? {exp_x, mant_x} : {exp_y, mant_y};
// checking for subnormal numbers
assign a_is_subnorm = (|exp_a == 0);
assign b_is_subnorm = (|exp_b == 0);
// checking difference in exponents
assign exp_diff = (a_is_subnorm | b_is_subnorm) & (exp_a != exp_b) ? (exp_a - exp_b - 1)
: (exp_a - exp_b);
// generating hidden bits
assign hd_bit_a = !a_is_subnorm;
assign hd_bit_b = !b_is_subnorm;
// right shifting mantissa to make exponents equal
right_shifter exp_equalizer (.mantisa({hd_bit_b, mant_b, 3'b000}), .shift_amount(exp_diff),
.out(rt_shift_mant));
// computing sum of the mantissas
assign arg1 = {hd_bit_a, mant_a, 3'b0};
assign arg2 = subtract ? (~rt_shift_mant + 27'b1) : rt_shift_mant;
assign {cout, mant_sum} = {1'b0,arg1} + {1'b0,arg2};
assign cout_check = cout & ~subtract;
leading_zero norm_dist_checker (.in(mant_sum[26:3]), .out(ld_zero_cnt));
// computing the shift amount
assign inter_shft_amt = a_is_subnorm ? 8'b0 : {3'b0, ld_zero_cnt};
assign exp_shft_comp = (exp_a <= inter_shft_amt);
assign shft_amt = exp_shft_comp ? (exp_a - |exp_a) : inter_shft_amt;
left_shifter #(27) norm_shifter (.mantisa(mant_sum), .shift_amount(shft_amt),
.out(lt_shft_mant));
// determining the exponent increment/decrement
assign norm_sum = cout_check ? {cout, mant_sum[26:2], |mant_sum[1:0]} : lt_shft_mant;
assign inc_dec_exp_amt = a_is_subnorm ? 5'b0 : cout_check ? 5'b1 : shft_amt;
rounding add_sub_rounder (.sign(sign_z), .mantisa(norm_sum), .round_mode(round_mode),
.rounded_mantisa(rounded_mant), .rounding_overflow(round_of));
// determine exponent in case of normal numbers
assign norm_exp = cout_check ? (exp_a + inc_dec_exp_amt + round_of) :
(exp_a - inc_dec_exp_amt + round_of);
// determine exponent in case of subnormal numbers
assign subnorm_exp = (rounded_mant[23] & !(|norm_exp)) ? 8'b1 :
(norm_exp - ((hd_bit_a | hd_bit_b) & exp_shft_comp & !rounded_mant[23]));
assign {exp_z, mant_z} = x_is_zero ? {exp_y, mant_y} : (y_is_zero ? {exp_x, mant_x} :
((mant_x == mant_y) & (exp_x == exp_y) & subtract ? 'd0 :
{subnorm_exp, rounded_mant[22:0]}));
// result check for special numbers
assign inter_result = (x_is_qNaN | y_is_qNaN) ? {1'h0, 8'hff, 23'h400000} :
((x_is_inf | y_is_inf) ? {sign_z, 8'hff, 23'h0} : ((exp_z == 8'hff) ?
{sign_z, exp_z, 23'd0} : {sign_z, exp_z, mant_z}));
assign invalid_operation = !(x_is_qNaN | y_is_qNaN) & (x_is_inf & y_is_inf & subtract) |
x_is_sNaN | y_is_sNaN;
// does not occur in addition subtraction
assign divide_by_zero = 0;
assign overflow = !(x_is_qNaN | y_is_qNaN) & &exp_z & !(x_is_inf | y_is_inf | x_is_qNaN |
y_is_qNaN | x_is_sNaN | y_is_sNaN);
// determining result in case of overflow
assign of_result = ({32{(round_mode == 3'h0) | (round_mode == 3'h4)}} & {sign_z, 8'hff, 23'h0}) |
({32{round_mode == 3'h1}} & {sign_z, 8'hfe, 23'h7fffff}) |
({32{round_mode == 3'h2}} & (sign_z ? {1'h1, 8'hff, 23'h0} :
{1'h0, 8'hfe, 23'h7fffff})) |
({32{round_mode == 3'h3}} & (sign_z ? {1'h1, 8'hfe, 23'h7fffff} :
{1'h0, 8'hff, 23'h0}));
// does not occur in addition subtraction
assign underflow = 0;
assign inexact = !(x_is_qNaN | y_is_qNaN) & (|norm_sum[2:0] | overflow | underflow) &
!(x_is_zero | y_is_zero | x_is_qNaN | y_is_qNaN | x_is_sNaN | y_is_sNaN |
x_is_inf | y_is_inf);
assign exceptions = {invalid_operation, divide_by_zero, overflow, underflow, inexact};
// assign output
assign out_z = overflow ? of_result : underflow ? 32'd0 : invalid_operation ?
{1'h0, 8'hff, 23'h400000} : inter_result;
endmodule