//==============================================================================
// MSI-X Table and PBA implementation
// Generated by PCILeech Firmware Generator
//
// Table size: {{ table_size }} entries
// Table BIR: {{ table_bir }}
// Table offset: 0x{{ "%x" | format(table_offset) }}
// PBA BIR: {{ pba_bir }}
// PBA offset: 0x{{ "%x" | format(pba_offset) }}
//==============================================================================

// MSI-X Table parameters
localparam NUM_MSIX = {{ table_size }};
localparam MSIX_TABLE_BIR = {{ table_bir }};
localparam MSIX_TABLE_OFFSET = 32'h{{ "%X" | format(table_offset) }};
localparam MSIX_PBA_BIR = {{ pba_bir }};
localparam MSIX_PBA_OFFSET = 32'h{{ "%X" | format(pba_offset) }};
localparam MSIX_ENABLED = {{ enabled_val }};
localparam MSIX_FUNCTION_MASK = {{ function_mask_val }};
localparam PBA_SIZE = {{ pba_size }};  // Number of 32-bit words needed for PBA

{% if alignment_warning %}
{{ alignment_warning }}
{% endif %}

// MSI-X Table storage
(* ram_style="block" *) reg [31:0] msix_table[0:NUM_MSIX*4-1];  // 4 DWORDs per entry

// MSI-X PBA storage
reg [31:0] msix_pba[0:{{ pba_size_minus_one }}];

// MSI-X control registers - dynamically connected to configuration space
// These signals are properly driven by the actual MSI-X capability registers
input wire msix_enabled;        // Connected to MSI-X Message Control Enable bit (bit 15)
input wire msix_function_mask;  // Connected to MSI-X Message Control Function Mask bit (bit 14)
input wire [10:0] msix_table_size; // Connected to MSI-X Message Control Table Size field (bits 10:0)

// MSI-X capability register interface for dynamic control
input wire        msix_cap_wr;     // Write strobe for MSI-X capability registers
input wire [31:0] msix_cap_addr;   // Address within MSI-X capability space
input wire [31:0] msix_cap_wdata;  // Write data for MSI-X capability registers
input wire [3:0]  msix_cap_be;     // Byte enables for MSI-X capability writes
output reg [31:0] msix_cap_rdata;  // Read data from MSI-X capability registers

// MSI-X interrupt generation interface
output reg        msix_interrupt;  // MSI-X interrupt request
output reg [10:0] msix_vector;     // MSI-X vector number
output reg [63:0] msix_msg_addr;   // MSI-X message address
output reg [31:0] msix_msg_data;   // MSI-X message data

// MSI-X Table access logic
function logic is_msix_table_access(input logic [31:0] addr, input logic [2:0] bar_index);
    return (bar_index == MSIX_TABLE_BIR) &&
           (addr >= MSIX_TABLE_OFFSET) &&
           (addr < (MSIX_TABLE_OFFSET + NUM_MSIX * 16));
endfunction

// MSI-X PBA access logic
function logic is_msix_pba_access(input logic [31:0] addr, input logic [2:0] bar_index);
    return (bar_index == MSIX_PBA_BIR) &&
           (addr >= MSIX_PBA_OFFSET) &&
           (addr < (MSIX_PBA_OFFSET + {{ pba_size }} * 4));
endfunction

// MSI-X Table read logic
function logic [31:0] msix_table_read(input logic [31:0] addr);
    logic [31:0] table_addr;
    table_addr = (addr - MSIX_TABLE_OFFSET) >> 2;  // Convert to DWORD index
    return msix_table[table_addr];
endfunction

// MSI-X Table write logic with byte enables
task msix_table_write(input logic [31:0] addr, input logic [31:0] data, input logic [3:0] byte_enable);
    logic [31:0] table_addr;
    logic [31:0] current_value;

    table_addr = (addr - MSIX_TABLE_OFFSET) >> 2;  // Convert to DWORD index
    current_value = msix_table[table_addr];

    // Apply byte enables
    if (byte_enable[0]) current_value[7:0] = data[7:0];
    if (byte_enable[1]) current_value[15:8] = data[15:8];
    if (byte_enable[2]) current_value[23:16] = data[23:16];
    if (byte_enable[3]) current_value[31:24] = data[31:24];

    msix_table[table_addr] = current_value;
endtask

// MSI-X PBA read logic
function logic [31:0] msix_pba_read(input logic [31:0] addr);
    logic [31:0] pba_addr;
    pba_addr = (addr - MSIX_PBA_OFFSET) >> 2;  // Convert to DWORD index
    return msix_pba[pba_addr];
endfunction

// MSI-X PBA write logic (typically read-only, but implemented for completeness)
task msix_pba_write(input logic [31:0] addr, input logic [31:0] data, input logic [3:0] byte_enable);
    logic [31:0] pba_addr;
    logic [31:0] current_value;

    pba_addr = (addr - MSIX_PBA_OFFSET) >> 2;  // Convert to DWORD index
    current_value = msix_pba[pba_addr];

    // Apply byte enables
    if (byte_enable[0]) current_value[7:0] = data[7:0];
    if (byte_enable[1]) current_value[15:8] = data[15:8];
    if (byte_enable[2]) current_value[23:16] = data[23:16];
    if (byte_enable[3]) current_value[31:24] = data[31:24];

    msix_pba[pba_addr] = current_value;
endtask

// Legacy MSI-X interrupt delivery logic (deprecated - use msix_deliver_interrupt_validated)
task msix_deliver_interrupt(input logic [10:0] vector);
    // Redirect to validated version for backward compatibility
    msix_deliver_interrupt_validated(vector);
endtask

// MSI-X table and PBA initialization is handled in reset logic
// Add this to an always_ff block with reset handling