//==============================================================================
// MSI-X Capability Register Management
// Generated by PCILeech Firmware Generator
//
// Handles dynamic configuration and control of MSI-X functionality
//==============================================================================

// MSI-X Message Control register fields
reg        msix_enable_reg;           // MSI-X Enable bit
reg        msix_function_mask_reg;    // Function Mask bit
reg [10:0] msix_table_size_reg;       // Table Size field (read-only)

// MSI-X Table Offset/BIR and PBA Offset/BIR registers (read-only)
reg [31:0] msix_table_offset_bir;
reg [31:0] msix_pba_offset_bir;

// Initialize MSI-X capability registers
initial begin
    msix_enable_reg = 1'b0;
    msix_function_mask_reg = 1'b0;
    msix_table_size_reg = {{ table_size_minus_one }};  // Table size - 1
    msix_table_offset_bir = {{ table_offset_bir }};
    msix_pba_offset_bir = {{ pba_offset_bir }};
end

// MSI-X capability register read/write logic
always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        msix_enable_reg <= 1'b0;
        msix_function_mask_reg <= 1'b0;
    end else if (msix_cap_wr) begin
        // Handle writes to MSI-X Message Control register
        case (msix_cap_addr[3:0])
            4'h2: begin  // Message Control register (offset 2)
                if (msix_cap_be[1]) msix_enable_reg <= msix_cap_wdata[15];
                if (msix_cap_be[1]) msix_function_mask_reg <= msix_cap_wdata[14];
                // Table size is read-only, ignore writes
            end
            // Table Offset/BIR and PBA Offset/BIR are read-only
            default: begin
                // Other offsets are read-only or reserved
            end
        endcase
    end
end

// MSI-X capability register read data multiplexer
always_comb begin
    case (msix_cap_addr[3:0])
        4'h0: msix_cap_rdata = {8'h00, 8'h11};  // Next pointer and Capability ID
        4'h2: msix_cap_rdata = {msix_enable_reg, msix_function_mask_reg, 3'b000, msix_table_size_reg};
        4'h4: msix_cap_rdata = msix_table_offset_bir;
        4'h8: msix_cap_rdata = msix_pba_offset_bir;
        default: msix_cap_rdata = 32'h00000000;
    endcase
end

// Connect control signals to the registers
assign msix_enabled = msix_enable_reg;
assign msix_function_mask = msix_function_mask_reg;
assign msix_table_size = msix_table_size_reg;

// MSI-X vector validation
function logic is_valid_msix_vector(input logic [10:0] vector);
    return (vector < NUM_MSIX) && msix_enabled && !msix_function_mask;
endfunction

// Enhanced MSI-X interrupt delivery with proper validation
task msix_deliver_interrupt_validated(input logic [10:0] vector);
    logic vector_masked;
    logic [31:0] table_addr;
    logic [31:0] control_dword;
    logic [31:0] pba_dword_idx;
    logic [4:0] pba_bit_idx;

    // Validate vector number
    if (!is_valid_msix_vector(vector)) begin
        $display("MSI-X Error: Invalid vector %0d or MSI-X disabled", vector);
        return;
    end

    // Get control DWORD (fourth DWORD in the entry)
    table_addr = vector * 4 + 3;
    control_dword = msix_table[table_addr];

    // Check if vector is masked (bit 0 of control DWORD)
    vector_masked = control_dword[0];

    if (!vector_masked) begin
        // Vector is enabled and not masked - deliver interrupt
        logic [63:0] message_address;
        logic [31:0] message_data;

        // Extract message address from MSI-X table entry
        message_address[31:0] = msix_table[vector * 4];      // Lower address DWORD
        message_address[63:32] = msix_table[vector * 4 + 1]; // Upper address DWORD

        // Extract message data from MSI-X table entry
        message_data = msix_table[vector * 4 + 2];

        // Set interrupt outputs
        msix_interrupt <= 1'b1;
        msix_vector <= vector;
        msix_msg_addr <= message_address;
        msix_msg_data <= message_data;

        $display("MSI-X Interrupt: vector=%0d, addr=0x%016h, data=0x%08h",
                 vector, message_address, message_data);
    end else begin
        // Vector is masked - set pending bit in PBA
        pba_dword_idx = vector >> 5;  // Divide by 32 to get DWORD index
        pba_bit_idx = vector & 5'h1F;  // Modulo 32 to get bit position

        if (pba_dword_idx < PBA_SIZE) begin
            msix_pba[pba_dword_idx] <= msix_pba[pba_dword_idx] | (32'h1 << pba_bit_idx);
            $display("MSI-X Pending: vector=%0d set in PBA[%0d][%0d]",
                     vector, pba_dword_idx, pba_bit_idx);
        end
    end
endtask