// =============================================================================
// mat2x2_inv.sv : 2x2 matrix inverse (Q7.25)
//   M = | a b |    M^-1 = (1/det) * |  d -b |
//       | c d |                     | -c  a |
//   det = a*d - b*c
// =============================================================================
`default_nettype none

module mat2x2_inv (
    input  wire                clk,
    input  wire                rst_n,
    input  wire                start,
    input  wire signed [31:0]  m00, m01, m10, m11,
    output reg  signed [31:0]  inv00, inv01, inv10, inv11,
    output reg                 done
);

    // ---- Stage 1: compute det = m00*m11 - m01*m10 ------------------------
    wire signed [31:0] ad, bc, det_w;
    q_mult   u_ad  (.a(m00), .b(m11), .p(ad));
    q_mult   u_bc  (.a(m01), .b(m10), .p(bc));
    q_addsub u_det (.sub(1'b1), .a(ad), .b(bc), .y(det_w));

    // ---- Reciprocal of det ------------------------------------------------
    // Pass det combinationally to q_recip so the correct det value is sampled
    // on the same cycle recip_start is asserted.
    reg                 recip_start;
    wire signed [31:0]  inv_det;
    wire                recip_done;

    q_recip u_recip (
        .clk(clk), .rst_n(rst_n),
        .start(recip_start),
        .d(det_w),
        .y(inv_det),
        .done(recip_done)
    );

    // ---- Stage 3: cofactor matrix * (1/det) -------------------------------
    reg  signed [31:0] cof00, cof01, cof10, cof11;
    wire signed [31:0] r00, r01, r10, r11;
    q_mult u_r00 (.a(cof00), .b(inv_det), .p(r00));
    q_mult u_r01 (.a(cof01), .b(inv_det), .p(r01));
    q_mult u_r10 (.a(cof10), .b(inv_det), .p(r10));
    q_mult u_r11 (.a(cof11), .b(inv_det), .p(r11));

    // ---- FSM --------------------------------------------------------------
    localparam [1:0] S_IDLE  = 2'd0;
    localparam [1:0] S_RECIP = 2'd1;
    localparam [1:0] S_MULT  = 2'd2;
    localparam [1:0] S_DONE  = 2'd3;

    reg [1:0] state, nstate;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) state <= S_IDLE;
        else        state <= nstate;
    end

    always @* begin
        nstate      = state;
        recip_start = 1'b0;
        case (state)
            S_IDLE : if (start)        nstate = S_RECIP;
            S_RECIP: if (recip_done)   nstate = S_MULT;
            S_MULT :                   nstate = S_DONE;
            S_DONE :                   nstate = S_IDLE;
            default:                   nstate = S_IDLE;
        endcase
        if (state == S_IDLE && start) recip_start = 1'b1;
    end

    // ---- Datapath registers -----------------------------------------------
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cof00 <= 32'd0; cof01 <= 32'd0; cof10 <= 32'd0; cof11 <= 32'd0;
            inv00 <= 32'd0; inv01 <= 32'd0; inv10 <= 32'd0; inv11 <= 32'd0;
            done  <= 1'b0;
        end
        else begin
            done <= 1'b0;

            if (state == S_IDLE && start) begin
                cof00 <=  m11;
                cof01 <= -m01;
                cof10 <= -m10;
                cof11 <=  m00;
            end

            if (state == S_MULT) begin
                inv00 <= r00;
                inv01 <= r01;
                inv10 <= r10;
                inv11 <= r11;
            end

            if (state == S_MULT && nstate == S_DONE) begin
                done <= 1'b1;
            end
        end
    end

endmodule

`default_nettype wire