`ifndef DESIGN_SV
`define DESIGN_SV

`timescale 1ns / 1ps

// 4-bit Carry-Lookahead Adder
module cla_4 (
    input  wire [3:0] a,
    input  wire [3:0] b,
    input  wire       cin,
    output wire [3:0] sum,
    output wire       cout
);
    wire [3:0] p = a ^ b;
    wire [3:0] g = a & b;

    wire [3:0] c;
    assign c[0] = cin;
    assign c[1] = g[0] | (p[0] & c[0]);
    assign c[2] = g[1] | (p[1] & g[0]) | (p[1] & p[0] & c[0]);
    assign c[3] = g[2] | (p[2] & g[1]) | (p[2] & p[1] & g[0]) | (p[2] & p[1] & p[0] & c[0]);

    assign cout = g[3] | (p[3] & g[2]) | (p[3] & p[2] & g[1]) | (p[3] & p[2] & p[1] & g[0]) | (p[3] & p[2] & p[1] & p[0] & c[0]);

    assign sum = p ^ c;
endmodule

// Generic Parameterized CLA
module cla_generic #(
    parameter WIDTH = 24
) (
    input  wire [WIDTH-1:0] a,
    input  wire [WIDTH-1:0] b,
    input  wire             cin,
    output wire [WIDTH-1:0] sum,
    output wire             cout
);
    localparam NUM_BLOCKS = (WIDTH + 3) / 4;
    wire [NUM_BLOCKS*4-1:0] a_ext = {{(NUM_BLOCKS*4 - WIDTH){1'b0}}, a};
    wire [NUM_BLOCKS*4-1:0] b_ext = {{(NUM_BLOCKS*4 - WIDTH){1'b0}}, b};
    wire [NUM_BLOCKS*4-1:0] sum_ext;
    wire [NUM_BLOCKS:0] c;

    assign c[0] = cin;

    genvar i;
    generate
        for (i = 0; i < NUM_BLOCKS; i = i + 1) begin : cla_block
            cla_4 u_cla_4 (
                .a(a_ext[i*4 +: 4]),
                .b(b_ext[i*4 +: 4]),
                .cin(c[i]),
                .sum(sum_ext[i*4 +: 4]),
                .cout(c[i+1])
            );
        end
    endgenerate

    assign sum = sum_ext[WIDTH-1:0];
    assign cout = c[NUM_BLOCKS];
endmodule

// Made by Sidda
module input_processing (
    input  wire [31:0] a,
    input  wire [31:0] b,
    output wire sign_a,
    output wire sign_b,
    output wire [7:0] exp_a,
    output wire [7:0] exp_b,
    output wire [23:0] mant_a,
    output wire [23:0] mant_b,
    output wire is_zero
);
    assign sign_a = a[31];
    assign sign_b = b[31];
    assign exp_a  = a[30:23];
    assign exp_b  = b[30:23];
   
    assign mant_a = (exp_a == 8'd0) ? {1'b0, a[22:0]} : {1'b1, a[22:0]};
    assign mant_b = (exp_b == 8'd0) ? {1'b0, b[22:0]} : {1'b1, b[22:0]};
   
    assign is_zero = (a[30:0] == 31'b0 || b[30:0] == 31'b0) ? 1'b1 : 1'b0;
endmodule

// Made by Neeraf.
module mantissa_multiplier (
    input  wire [23:0] mant_a,
    input  wire [23:0] mant_b,
    output wire [47:0] mant_prod
);
    wire [47:0] pp [23:0];
    wire [47:0] partial_sum [23:0];

    genvar i;
    generate
        for (i = 0; i < 24; i = i + 1) begin : pp_gen
            assign pp[i] = mant_b[i] ? ({{24{1'b0}}, mant_a} << i) : 48'b0;
        end
    endgenerate

    assign partial_sum[0] = pp[0];

    generate
        for (i = 1; i < 24; i = i + 1) begin : add_gen
            cla_generic #(.WIDTH(48)) u_cla_add (
                .a(partial_sum[i-1]),
                .b(pp[i]),
                .cin(1'b0),
                .sum(partial_sum[i]),
                .cout()
            );
        end
    endgenerate

    assign mant_prod = partial_sum[23];
endmodule

// Made by Kanav
module normalization_unit (
    input  wire [47:0] mant_prod,
    output reg  [22:0] mant_final,
    output reg  norm_shift_flag
);
    wire bit47 = mant_prod[47];

    wire [23:0] raw_mant = bit47 ? mant_prod[47:24] : mant_prod[46:23];
    wire guard_bit     = bit47 ? mant_prod[23]    : mant_prod[22];
    wire round_bit     = bit47 ? mant_prod[22]    : mant_prod[21];
    wire sticky_bit    = bit47 ? (|mant_prod[21:0]) : (|mant_prod[20:0]);

    wire round_up = guard_bit & (round_bit | sticky_bit | raw_mant[0]);

    wire [23:0] rounded_mant;
    wire round_overflow;

    cla_generic #(.WIDTH(24)) u_cla_round (
        .a(raw_mant),
        .b(24'b0),
        .cin(round_up),
        .sum(rounded_mant),
        .cout(round_overflow)
    );

    always @(*) begin
        if (round_overflow) begin
            mant_final = rounded_mant[23:1];
            norm_shift_flag = 1'b1;
        end else begin
            if (mant_prod[47] == 1'b1) begin
                mant_final = rounded_mant[22:0];
                norm_shift_flag = 1'b1;        
            end else begin
                mant_final = rounded_mant[22:0];
                norm_shift_flag = 1'b0;
            end
        end
    end
endmodule

// Made by Srijan
module sign_exponent_unit (
    input  wire sign_a,
    input  wire sign_b,
    input  wire [7:0] exp_a,
    input  wire [7:0] exp_b,
    input  wire norm_shift_flag,
    output wire sign_out,
    output reg  [7:0] final_exp,
    output reg  overflow,
    output reg  underflow
);
    assign sign_out = sign_a ^ sign_b;
   
    wire [8:0] sum_ab;
    wire [8:0] sub_bias;
    wire [8:0] base_exp_wire;

    cla_generic #(.WIDTH(9)) u_cla_exp_sum (
        .a({1'b0, exp_a}),
        .b({1'b0, exp_b}),
        .cin(1'b0),
        .sum(sum_ab),
        .cout()
    );

    cla_generic #(.WIDTH(9)) u_cla_sub_bias (
        .a(sum_ab),
        .b(9'b110000001),
        .cin(1'b0),
        .sum(sub_bias),
        .cout()
    );

    cla_generic #(.WIDTH(9)) u_cla_norm_adj (
        .a(sub_bias),
        .b({8'b0, norm_shift_flag}),
        .cin(1'b0),
        .sum(base_exp_wire),
        .cout()
    );

    always @(*) begin
        if (base_exp_wire[8] || base_exp_wire == 9'd0) begin
            underflow = 1'b1;
            overflow  = 1'b0;
            final_exp = 8'd0;
        end else if (base_exp_wire >= 9'd255) begin
            overflow  = 1'b1;
            underflow = 1'b0;
            final_exp = 8'd255;
        end else begin
            overflow  = 1'b0;
            underflow = 1'b0;
            final_exp = base_exp_wire[7:0];
        end
    end
endmodule

module fp_multiplier_top (
    input  wire [31:0] a,
    input  wire [31:0] b,
    output reg  [31:0] result
);
    wire sign_a, sign_b;
    wire [7:0] exp_a, exp_b;
    wire [23:0] mant_a, mant_b;
    wire is_zero;

    wire [47:0] mant_prod;
    wire [22:0] mant_final;
    wire norm_shift_flag;

    wire sign_out;
    wire [7:0] final_exp;
    wire overflow, underflow;

    input_processing u_input (
        .a(a),
        .b(b),
        .sign_a(sign_a),
        .sign_b(sign_b),
        .exp_a(exp_a),
        .exp_b(exp_b),
        .mant_a(mant_a),
        .mant_b(mant_b),
        .is_zero(is_zero)
    );

    mantissa_multiplier u_mult (
        .mant_a(mant_a),
        .mant_b(mant_b),
        .mant_prod(mant_prod)
    );

    normalization_unit u_norm (
        .mant_prod(mant_prod),
        .mant_final(mant_final),
        .norm_shift_flag(norm_shift_flag)
    );

    sign_exponent_unit u_sign_exp (
        .sign_a(sign_a),
        .sign_b(sign_b),
        .exp_a(exp_a),
        .exp_b(exp_b),
        .norm_shift_flag(norm_shift_flag),
        .sign_out(sign_out),
        .final_exp(final_exp),
        .overflow(overflow),
        .underflow(underflow)
    );

    wire is_nan_a = (exp_a == 8'hFF) && (a[22:0] != 23'b0);
    wire is_nan_b = (exp_b == 8'hFF) && (b[22:0] != 23'b0);
    wire is_inf_a = (exp_a == 8'hFF) && (a[22:0] == 23'b0);
    wire is_inf_b = (exp_b == 8'hFF) && (b[22:0] == 23'b0);

    always @(*) begin
        if (is_nan_a || is_nan_b) begin
            result = 32'h7FC00000;
        end else if ((is_inf_a && is_zero) || (is_inf_b && is_zero)) begin
            result = 32'h7FC00000;
        end else if (is_inf_a || is_inf_b) begin
            result = {sign_out, 8'hFF, 23'b0};
        end else if (is_zero || underflow) begin
            result = {sign_out, 31'b0};
        end else if (overflow) begin
            result = {sign_out, 8'hFF, 23'b0};
        end else begin
            result = {sign_out, final_exp, mant_final};
        end
    end
endmodule

`endif
