//==============================================================================
// Active Device Interrupt Controller
// Generated by PCILeech Firmware Generator
//
// This module implements a comprehensive interrupt controller with support for:
// - Legacy INTx interrupts
// - MSI (Message Signaled Interrupts) via cfg interface
// - MSI-X (Extended Message Signaled Interrupts) via TLP construction
// - Periodic "active device" interrupt generation
// - Configurable interrupt sources and priorities
// - Integration with PCILeech architecture
//
// Features:
// - Configurable timer for periodic interrupts
// - Priority-based interrupt arbitration
// - Support for multiple interrupt sources
// - Proper state machines for reliable interrupt delivery
// - Error handling and timeout mechanisms
//==============================================================================

`include "../tlp_pkg.svh"

module active_device_interrupt 
    import tlp_pkg::*;
#(
    // Timer Configuration
    parameter TIMER_PERIOD = {{ active_device_config.timer_period }},      // Clock cycles between periodic interrupts
    parameter TIMER_ENABLE = {{ active_device_config.timer_enable | sv_bool }},      // Enable periodic timer
    
    // MSI Configuration
    parameter MSI_VECTOR_WIDTH = {{ active_device_config.msi_vector_width }},   // Number of MSI vectors (2^WIDTH)
    parameter MSI_64BIT_ADDR = {{ active_device_config.msi_64bit_addr | sv_bool }},       // Use 64-bit MSI addresses
    
    // MSI-X Configuration
    parameter NUM_MSIX = {{ active_device_config.num_msix }},                    // Number of MSI-X vectors
    parameter MSIX_TABLE_BIR = {{ active_device_config.msix_table_bir }},       // BAR for MSI-X table
    parameter MSIX_TABLE_OFFSET = {{ active_device_config.msix_table_offset }},  // Offset in BAR
    parameter MSIX_PBA_BIR = {{ active_device_config.msix_pba_bir }},           // BAR for PBA
    parameter MSIX_PBA_OFFSET = {{ active_device_config.msix_pba_offset }},     // PBA offset
    
    // Device Configuration
    parameter DEVICE_ID = {{ active_device_config.device_id }},           // PCIe Device ID
    parameter VENDOR_ID = {{ active_device_config.vendor_id }},           // PCIe Vendor ID
    parameter COMPLETER_ID = {{ active_device_config.completer_id }},     // Bus/Dev/Func
    
    // Interrupt Source Configuration
    parameter NUM_INTERRUPT_SOURCES = {{ active_device_config.num_sources }},    // Number of interrupt sources
    parameter DEFAULT_PRIORITY = {{ active_device_config.default_priority }}     // Default interrupt priority
) (
    // Clock and Reset
    input  logic                    clk,
    input  logic                    reset_n,
    
    // Interrupt Source Interface
    input  logic [NUM_INTERRUPT_SOURCES-1:0] interrupt_sources,  // Interrupt request inputs
    input  logic [3:0]              source_priority[NUM_INTERRUPT_SOURCES], // Priority per source
    output logic [NUM_INTERRUPT_SOURCES-1:0] interrupt_ack,      // Acknowledge to sources
    
    // Configuration Space Interface (for MSI)
    output logic                    cfg_interrupt,          // Legacy interrupt request
    output logic                    cfg_interrupt_assert,   // Assert interrupt
    output logic                    cfg_interrupt_di,       // Interrupt disable
    input  logic                    cfg_interrupt_rdy,      // Ready for interrupt
    input  logic                    cfg_interrupt_mmenable, // Multiple message enable
    input  logic                    cfg_interrupt_msienable,// MSI enable
    input  logic                    cfg_interrupt_msixenable,// MSI-X enable
    input  logic                    cfg_interrupt_msixfm,   // MSI-X function mask
    output logic [7:0]              cfg_interrupt_do,       // Interrupt data out
    input  logic [7:0]              cfg_interrupt_di_in,    // Interrupt data in
    output logic                    cfg_interrupt_stat,     // Interrupt status
    output logic [4:0]              cfg_pciecap_interrupt_msgnum, // Message number
    
    // TLP Interface (for MSI-X)
    output logic                    tlp_tx_valid,
    output logic [127:0]            tlp_tx_data,
    output logic                    tlp_tx_sop,
    output logic                    tlp_tx_eop,
    output logic [3:0]              tlp_tx_empty,
    input  logic                    tlp_tx_ready,
    
    // MSI-X Table Interface
    input  logic [31:0]             msix_table_addr,
    input  logic                    msix_table_wr_en,
    input  logic [31:0]             msix_table_wr_data,
    input  logic [3:0]              msix_table_wr_be,
    input  logic                    msix_table_rd_en,
    output logic [31:0]             msix_table_rd_data,
    
    // Control and Status
    input  logic                    interrupt_enable,       // Global interrupt enable
    input  logic [31:0]             timer_reload_value,     // Timer reload value
    output logic                    timer_interrupt_pending,// Timer interrupt is pending
    output logic [31:0]             interrupt_count,        // Total interrupts delivered
    output logic [31:0]             interrupt_status,       // Current interrupt status
    output logic [7:0]              current_vector,         // Current interrupt vector
    output logic [3:0]              debug_state             // Debug state output
);

    // ========================================================================
    // Type Definitions and Local Parameters
    // ========================================================================
    
    // Interrupt Controller States
    typedef enum logic [3:0] {
        INTR_IDLE,
        INTR_ARBITRATE,
        INTR_MSI_SETUP,
        INTR_MSI_SEND,
        INTR_MSI_WAIT,
        INTR_MSIX_SETUP,
        INTR_MSIX_TLP_HDR,
        INTR_MSIX_TLP_DATA,
        INTR_MSIX_WAIT,
        INTR_LEGACY_ASSERT,
        INTR_LEGACY_WAIT,
        INTR_ACK_SOURCE,
        INTR_ERROR
    } intr_state_t;
    
    // MSI-X Table Entry Structure
    typedef struct packed {
        logic [31:0] msg_addr_lo;    // Message Address Low
        logic [31:0] msg_addr_hi;    // Message Address High
        logic [31:0] msg_data;       // Message Data
        logic [31:0] vector_ctrl;    // Vector Control (bit 0 = mask)
    } msix_entry_t;
    
    // TLP Header for MSI-X Memory Write
    typedef struct packed {
        logic [1:0]  fmt;            // Format (10b = 3DW header, no data)
        logic [4:0]  type_field;     // Type (00000b = Memory Write)
        logic [2:0]  tc;             // Traffic Class
        logic        td;             // TLP Digest
        logic        ep;             // Poisoned
        logic [1:0]  attr;           // Attributes
        logic        th;             // TLP Hints
        logic [9:0]  length;         // Length in DW
        logic [15:0] requester_id;   // Requester ID
        logic [7:0]  tag;            // Tag
        logic [3:0]  first_be;       // First DW BE
        logic [3:0]  last_be;        // Last DW BE
    } msix_tlp_header_t;
    
    // ========================================================================
    // Internal Signals
    // ========================================================================
    
    // State Machine
    intr_state_t current_state, next_state;
    
    // Timer
    logic [31:0] timer_counter;
    logic        timer_expired;
    logic        timer_interrupt_req;
    
    // Interrupt Arbitration
    logic [NUM_INTERRUPT_SOURCES-1:0] pending_interrupts;
    logic [NUM_INTERRUPT_SOURCES-1:0] masked_interrupts;
    logic [$clog2(NUM_INTERRUPT_SOURCES)-1:0] selected_source;
    logic [3:0]  selected_priority;
    logic        interrupt_pending;
    
    // MSI Control
    logic        msi_pending;
    logic [4:0]  msi_vector;
    logic        msi_sent;
    
    // MSI-X Control
    logic        msix_pending;
    logic [10:0] msix_vector_num;
    msix_entry_t msix_entry;
    logic        msix_vector_masked;
    logic [127:0] msix_tlp_data;
    logic        msix_tlp_valid;
    
    // MSI-X Table Memory
    (* ram_style = "block" *)
    logic [31:0] msix_table_mem[0:NUM_MSIX*4-1];
    
    // MSI-X PBA Memory
    localparam PBA_SIZE = (NUM_MSIX + 31) / 32;
    logic [31:0] msix_pba_mem[0:PBA_SIZE-1];
    
    // Performance Counters
    logic [31:0] interrupt_count_reg;
    logic [31:0] msi_count;
    logic [31:0] msix_count;
    logic [31:0] legacy_count;
    
    // ========================================================================
    // Timer Logic
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            timer_counter <= 32'h0;
            timer_expired <= 1'b0;
            timer_interrupt_req <= 1'b0;
        end else begin
            if (TIMER_ENABLE && interrupt_enable) begin
                if (timer_counter >= TIMER_PERIOD) begin
                    timer_counter <= timer_reload_value;
                    timer_expired <= 1'b1;
                    timer_interrupt_req <= 1'b1;
                end else begin
                    timer_counter <= timer_counter + 1'b1;
                    timer_expired <= 1'b0;
                end
            end else begin
                timer_counter <= 32'h0;
                timer_expired <= 1'b0;
            end
            
            // Clear timer interrupt request when acknowledged
            if (timer_interrupt_req && (selected_source == NUM_INTERRUPT_SOURCES-1) &&
                (current_state == INTR_ACK_SOURCE)) begin
                timer_interrupt_req <= 1'b0;
            end
        end
    end
    
    assign timer_interrupt_pending = timer_interrupt_req;
    
    // ========================================================================
    // Interrupt Source Management
    // ========================================================================
    
    // Latch interrupt requests
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            pending_interrupts <= '0;
        end else begin
            // Set pending bits for new interrupts
            pending_interrupts <= pending_interrupts | interrupt_sources;
            
            // Clear acknowledged interrupts
            pending_interrupts <= pending_interrupts & ~interrupt_ack;
        end
    end
    
    // Combine all interrupt sources including timer
    logic [NUM_INTERRUPT_SOURCES:0] all_interrupts;
    assign all_interrupts = {timer_interrupt_req, pending_interrupts};
    
    // ========================================================================
    // Priority Arbitration
    // ========================================================================
    
    always_comb begin
        selected_source = '0;
        selected_priority = '0;
        interrupt_pending = 1'b0;
        
        // Check timer first (highest priority)
        if (timer_interrupt_req) begin
            selected_source = NUM_INTERRUPT_SOURCES;
            selected_priority = 4'hF;  // Highest priority
            interrupt_pending = 1'b1;
        end else begin
            // Find highest priority pending interrupt
            for (int i = 0; i < NUM_INTERRUPT_SOURCES; i++) begin
                if (pending_interrupts[i] && !masked_interrupts[i]) begin
                    if (!interrupt_pending || source_priority[i] > selected_priority) begin
                        selected_source = i;
                        selected_priority = source_priority[i];
                        interrupt_pending = 1'b1;
                    end
                end
            end
        end
    end
    
    // ========================================================================
    // MSI-X Table Access
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            msix_table_rd_data <= 32'h0;
        end else begin
            if (msix_table_rd_en) begin
                msix_table_rd_data <= msix_table_mem[msix_table_addr[11:2]];
            end
            
            if (msix_table_wr_en) begin
                if (msix_table_wr_be[0]) msix_table_mem[msix_table_addr[11:2]][7:0]   <= msix_table_wr_data[7:0];
                if (msix_table_wr_be[1]) msix_table_mem[msix_table_addr[11:2]][15:8]  <= msix_table_wr_data[15:8];
                if (msix_table_wr_be[2]) msix_table_mem[msix_table_addr[11:2]][23:16] <= msix_table_wr_data[23:16];
                if (msix_table_wr_be[3]) msix_table_mem[msix_table_addr[11:2]][31:24] <= msix_table_wr_data[31:24];
            end
        end
    end
    
    // Read MSI-X entry for current vector
    always_comb begin
        msix_entry.msg_addr_lo = msix_table_mem[msix_vector_num * 4 + 0];
        msix_entry.msg_addr_hi = msix_table_mem[msix_vector_num * 4 + 1];
        msix_entry.msg_data    = msix_table_mem[msix_vector_num * 4 + 2];
        msix_entry.vector_ctrl = msix_table_mem[msix_vector_num * 4 + 3];
        msix_vector_masked = msix_entry.vector_ctrl[0];
    end
    
    // ========================================================================
    // Main State Machine
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            current_state <= INTR_IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    always_comb begin
        next_state = current_state;
        
        case (current_state)
            INTR_IDLE: begin
                if (interrupt_pending && interrupt_enable) begin
                    next_state = INTR_ARBITRATE;
                end
            end
            
            INTR_ARBITRATE: begin
                if (cfg_interrupt_msixenable && !cfg_interrupt_msixfm) begin
                    next_state = INTR_MSIX_SETUP;
                end else if (cfg_interrupt_msienable) begin
                    next_state = INTR_MSI_SETUP;
                end else begin
                    next_state = INTR_LEGACY_ASSERT;
                end
            end
            
            // MSI States
            INTR_MSI_SETUP: begin
                next_state = INTR_MSI_SEND;
            end
            
            INTR_MSI_SEND: begin
                if (cfg_interrupt_rdy) begin
                    next_state = INTR_MSI_WAIT;
                end
            end
            
            INTR_MSI_WAIT: begin
                if (!cfg_interrupt_rdy) begin
                    next_state = INTR_ACK_SOURCE;
                end
            end
            
            // MSI-X States
            INTR_MSIX_SETUP: begin
                if (!msix_vector_masked) begin
                    next_state = INTR_MSIX_TLP_HDR;
                end else begin
                    next_state = INTR_ACK_SOURCE;  // Skip if masked
                end
            end
            
            INTR_MSIX_TLP_HDR: begin
                if (tlp_tx_ready) begin
                    next_state = INTR_MSIX_TLP_DATA;
                end
            end
            
            INTR_MSIX_TLP_DATA: begin
                if (tlp_tx_ready) begin
                    next_state = INTR_MSIX_WAIT;
                end
            end
            
            INTR_MSIX_WAIT: begin
                next_state = INTR_ACK_SOURCE;
            end
            
            // Legacy Interrupt States
            INTR_LEGACY_ASSERT: begin
                if (cfg_interrupt_rdy) begin
                    next_state = INTR_LEGACY_WAIT;
                end
            end
            
            INTR_LEGACY_WAIT: begin
                next_state = INTR_ACK_SOURCE;
            end
            
            INTR_ACK_SOURCE: begin
                next_state = INTR_IDLE;
            end
            
            INTR_ERROR: begin
                next_state = INTR_IDLE;
            end
            
            default: next_state = INTR_IDLE;
        endcase
    end
    
    // ========================================================================
    // MSI Logic
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            cfg_interrupt <= 1'b0;
            cfg_interrupt_assert <= 1'b0;
            cfg_interrupt_di <= 1'b0;
            cfg_interrupt_do <= 8'h0;
            cfg_pciecap_interrupt_msgnum <= 5'h0;
            msi_sent <= 1'b0;
        end else begin
            case (current_state)
                INTR_MSI_SETUP: begin
                    cfg_interrupt <= 1'b1;
                    cfg_interrupt_assert <= 1'b1;
                    cfg_interrupt_di <= 1'b0;
                    cfg_interrupt_do <= {3'b0, selected_source[4:0]};
                    cfg_pciecap_interrupt_msgnum <= selected_source[4:0];
                end
                
                INTR_MSI_SEND: begin
                    if (cfg_interrupt_rdy) begin
                        msi_sent <= 1'b1;
                    end
                end
                
                INTR_MSI_WAIT: begin
                    cfg_interrupt <= 1'b0;
                    cfg_interrupt_assert <= 1'b0;
                    msi_sent <= 1'b0;
                end
                
                default: begin
                    cfg_interrupt <= 1'b0;
                    cfg_interrupt_assert <= 1'b0;
                end
            endcase
        end
    end
    
    // ========================================================================
    // MSI-X TLP Generation
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            tlp_tx_valid <= 1'b0;
            tlp_tx_data <= 128'h0;
            tlp_tx_sop <= 1'b0;
            tlp_tx_eop <= 1'b0;
            tlp_tx_empty <= 4'h0;
            msix_vector_num <= 11'h0;
        end else begin
            case (current_state)
                INTR_MSIX_SETUP: begin
                    // Map interrupt source to MSI-X vector
                    if (selected_source == NUM_INTERRUPT_SOURCES) begin
                        msix_vector_num <= 11'h0;  // Timer uses vector 0
                    end else begin
                        msix_vector_num <= selected_source[10:0];
                    end
                end
                
                INTR_MSIX_TLP_HDR: begin
                    if (tlp_tx_ready) begin
                        // Generate MSI-X memory write TLP header
                        tlp_tx_valid <= 1'b1;
                        tlp_tx_sop <= 1'b1;
                        tlp_tx_eop <= 1'b0;
                        
                        // Build TLP header for 32-bit memory write
                        tlp_tx_data[127:96] <= {
                            3'b010,           // Format: 3DW header with data
                            5'b00000,         // Type: Memory Write
                            1'b0,             // Reserved
                            3'b000,           // TC
                            4'b0000,          // Reserved
                            1'b0,             // TD
                            1'b0,             // EP
                            2'b00,            // Attr
                            2'b00,            // Reserved
                            10'h001           // Length: 1 DW
                        };
                        
                        tlp_tx_data[95:64] <= {
                            COMPLETER_ID,     // Requester ID
                            8'h00,            // Tag
                            4'hF,             // Last BE
                            4'hF              // First BE
                        };
                        
                        // Address (32-bit)
                        tlp_tx_data[63:32] <= msix_entry.msg_addr_lo;
                        
                        // Data (first part)
                        tlp_tx_data[31:0] <= msix_entry.msg_data;
                    end else begin
                        tlp_tx_valid <= 1'b0;
                        tlp_tx_sop <= 1'b0;
                    end
                end
                
                INTR_MSIX_TLP_DATA: begin
                    if (tlp_tx_ready) begin
                        tlp_tx_valid <= 1'b1;
                        tlp_tx_sop <= 1'b0;
                        tlp_tx_eop <= 1'b1;
                        tlp_tx_empty <= 4'hC;  // Only first DW valid
                        tlp_tx_data <= 128'h0;
                    end
                end
                
                default: begin
                    tlp_tx_valid <= 1'b0;
                    tlp_tx_sop <= 1'b0;
                    tlp_tx_eop <= 1'b0;
                end
            endcase
        end
    end
    
    // ========================================================================
    // Legacy Interrupt Logic
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            cfg_interrupt_stat <= 1'b0;
        end else begin
            case (current_state)
                INTR_LEGACY_ASSERT: begin
                    cfg_interrupt_stat <= 1'b1;
                end
                
                INTR_LEGACY_WAIT: begin
                    cfg_interrupt_stat <= 1'b0;
                end
                
                default: begin
                    cfg_interrupt_stat <= 1'b0;
                end
            endcase
        end
    end
    
    // ========================================================================
    // Interrupt Acknowledge
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            interrupt_ack <= '0;
        end else begin
            interrupt_ack <= '0;
            
            if (current_state == INTR_ACK_SOURCE) begin
                if (selected_source < NUM_INTERRUPT_SOURCES) begin
                    interrupt_ack[selected_source] <= 1'b1;
                end
            end
        end
    end
    
    // ========================================================================
    // Performance Counters and Status
    // ========================================================================
    
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            interrupt_count_reg <= 32'h0;
            msi_count <= 32'h0;
            msix_count <= 32'h0;
            legacy_count <= 32'h0;
        end else begin
            if (current_state == INTR_ACK_SOURCE) begin
                interrupt_count_reg <= interrupt_count_reg + 1'b1;
                
                case (next_state)
                    INTR_MSI_WAIT: msi_count <= msi_count + 1'b1;
                    INTR_MSIX_WAIT: msix_count <= msix_count + 1'b1;
                    INTR_LEGACY_WAIT: legacy_count <= legacy_count + 1'b1;
                    default: ;
                endcase
            end
        end
    end
    
    assign interrupt_count = interrupt_count_reg;
    assign current_vector = selected_source[7:0];
    assign debug_state = current_state;
    
    // Status register
    assign interrupt_status = {
        8'h0,                          // [31:24] Reserved
        legacy_count[7:0],             // [23:16] Legacy interrupt count
        msix_count[7:0],               // [15:8]  MSI-X count
        msi_count[7:0]                 // [7:0]   MSI count
    };
    
    // ========================================================================
    // Assertions
    // ========================================================================
    
    // synthesis translate_off
    property p_no_simultaneous_interrupts;
        @(posedge clk) disable iff (!reset_n)
        (cfg_interrupt_msienable && cfg_interrupt_msixenable) == 1'b0;
    endproperty
    
    assert property (p_no_simultaneous_interrupts)
        else $error("MSI and MSI-X cannot be enabled simultaneously");
    
    property p_valid_msix_vector;
        @(posedge clk) disable iff (!reset_n)
        (current_state == INTR_MSIX_SETUP) |-> (msix_vector_num < NUM_MSIX);
    endproperty
    
    assert property (p_valid_msix_vector)
        else $error("MSI-X vector number exceeds configured range");
    // synthesis translate_on

endmodule