// fp_mult.sv
// Floating-Point Multiplier Core (IEEE-754 single precision)

module fp_mult (
    input  logic [31:0] a,       // input operand A
    input  logic [31:0] b,       // input operand B
    input  logic [2:0]  round,   // rounding mode
    output logic [31:0] z,       // result
    output logic [7:0]  status,  // status flags
    input  logic        clk,
    input  logic        rst
);

    // Stage 0: Decode inputs
    logic        sign;
    logic [7:0]  exp_a, exp_b;
    logic [23:0] mant_a, mant_b;
    logic [47:0] s0_mantissa;
    logic [9:0]  s0_exponent;

    always_comb begin
        sign  = a[31] ^ b[31];
        exp_a = a[30:23];
        exp_b = b[30:23];
        s0_exponent = exp_a + exp_b - 127;

        mant_a = (exp_a == 0) ? {1'b0, a[22:0]} : {1'b1, a[22:0]};
        mant_b = (exp_b == 0) ? {1'b0, b[22:0]} : {1'b1, b[22:0]};
        s0_mantissa = mant_a * mant_b;
    end

    // === Stage 1: Normalize ===
    logic [47:0] s1_mant_in;
    logic [9:0]  s1_exp_in;
    logic [22:0] s1_mant_out;
    logic [9:0]  s1_exp_out;
    logic        s1_guard_out, s1_sticky_out;
    logic [31:0] s1_a, s1_b;
    logic        s1_sign;

    normalize_mult normalize_inst (
        .mantissa_in(s1_mant_in),
        .exponent_in(s1_exp_in),
        .mantissa_out(s1_mant_out),
        .exponent_out(s1_exp_out),
        .guard_bit(s1_guard_out),
        .sticky_bit(s1_sticky_out)
    );

    always_ff @(posedge clk or negedge rst) begin
        if (!rst) begin
            s1_mant_in <= 0;
            s1_exp_in <= 0;
            s1_a <= 0;
            s1_b <= 0;
            s1_sign <= 0;
        end else begin
            s1_mant_in <= s0_mantissa;
            s1_exp_in <= s0_exponent;
            s1_a <= a;
            s1_b <= b;
            s1_sign <= sign;
        end
    end

    // === Stage 2: Round ===
    logic [22:0] s2_mant_in;
    logic [9:0]  s2_exp_in;
    logic        s2_guard_in, s2_sticky_in;
    logic [31:0] s2_a, s2_b;
    logic        s2_sign;
    logic [24:0] s2_mant_out;
    logic [9:0]  s2_exp_out;
    logic        s2_inexact;

    round_mult round_inst (
        .mantissa_in(s2_mant_in),
        .exponent_in(s2_exp_in),
        .sign_in(s2_sign),
        .guard_bit(s2_guard_in),
        .sticky_bit(s2_sticky_in),
        .round(round),
        .mantissa_out(s2_mant_out),
        .exponent_out(s2_exp_out),
        .inexact(s2_inexact)
    );

    always_ff @(posedge clk or negedge rst) begin
        if (!rst) begin
            s2_mant_in <= 0;
            s2_exp_in <= 0;
            s2_guard_in <= 0;
            s2_sticky_in <= 0;
            s2_a <= 0;
            s2_b <= 0;
            s2_sign <= 0;
        end else begin
            s2_mant_in <= s1_mant_out;
            s2_exp_in <= s1_exp_out;
            s2_guard_in <= s1_guard_out;
            s2_sticky_in <= s1_sticky_out;
            s2_a <= s1_a;
            s2_b <= s1_b;
            s2_sign <= s1_sign;
        end
    end

    // === Stage 3: Exception Handling ===
    logic [31:0] s3_a, s3_b;
    logic [31:0] s3_z_calc_in;
    logic        s3_ovf_in, s3_unf_in;
    logic        s3_inexact;
    logic [31:0] s3_z_out;
    logic [7:0]  s3_status_out;

    exception_mult exception_handler (
        .a(s3_a),
        .b(s3_b),
        .z_calc(s3_z_calc_in),
        .ovf(s3_ovf_in),
        .unf(s3_unf_in),
        .inexact(s3_inexact),
        .round(round),
        .z(s3_z_out),
        .zero_f(s3_status_out[0]),
        .inf_f(s3_status_out[1]),
        .nan_f(s3_status_out[2]),
        .tiny_f(s3_status_out[3]),
        .huge_f(s3_status_out[4]),
        .inexact_f(s3_status_out[5])
    );

    always_ff @(posedge clk or negedge rst) begin
        if (!rst) begin
            s3_a <= 0;
            s3_b <= 0;
            s3_z_calc_in <= 0;
            s3_ovf_in <= 0;
            s3_unf_in <= 0;
            s3_inexact <= 0; 
            z <= 0;
            status <= 0;
        end else begin
            s3_a <= s2_a;
            s3_b <= s2_b;
            s3_z_calc_in <= {s2_sign, s2_exp_out[7:0], s2_mant_out[22:0]};
            s3_ovf_in <= (s2_exp_out > 8'd254);
            s3_unf_in <= (s2_exp_out < 8'd1);
            s3_inexact <= s2_inexact;
            z <= s3_z_out;
            status <= s3_status_out;
        end
    end

endmodule

