//==============================================================================
// BAR Dispatcher - Address Decoder and Region Multiplexer
// Generated by PCILeech Firmware Generator
//
// This module serves as the central routing hub for the PCILeech BAR controller:
// - Decodes TLP addresses to determine target memory regions
// - Routes requests to appropriate region handler modules
// - Aggregates responses from region modules
// - Handles address range validation and error reporting
// - Provides centralized access control and monitoring
//==============================================================================

`include "../tlp_pkg.svh"
`include "../bar_layout_pkg.svh"

module bar_dispatcher
    import tlp_pkg::*;
    import bar_layout_pkg::*;
#(
    parameter bit ENABLE_ACCESS_LOGGING = 1'b1,
    parameter bit ENABLE_RANGE_CHECKING = 1'b1
) (
    // Clock and Reset
    input  logic                clk,
    input  logic                reset_n,
    
    // Input from TLP Frontend
    input  logic                parsed_tlp_valid,
    input  tlp_header_t         parsed_tlp_header,
    input  logic [63:0]         parsed_tlp_address,
    input  logic [127:0]        parsed_tlp_data,
    input  logic                parsed_tlp_has_data,
    input  logic [11:0]         parsed_tlp_byte_count,
    input  logic                parsed_tlp_error,
    output logic                parsed_tlp_ready,
    
    // Region Interface - Device Control
    output logic                device_ctrl_req_valid,
    output logic [31:0]         device_ctrl_req_addr,
    output logic [31:0]         device_ctrl_req_data,
    output logic [3:0]          device_ctrl_req_be,
    output logic                device_ctrl_req_write,
    output tlp_header_t         device_ctrl_req_header,
    input  logic                device_ctrl_req_ready,
    input  logic                device_ctrl_resp_valid,
    input  logic [31:0]         device_ctrl_resp_data,
    input  logic                device_ctrl_resp_error,
    output logic                device_ctrl_resp_ready,
    
    // Region Interface - Status Registers
    output logic                status_reg_req_valid,
    output logic [31:0]         status_reg_req_addr,
    output logic [31:0]         status_reg_req_data,
    output logic [3:0]          status_reg_req_be,
    output logic                status_reg_req_write,
    output tlp_header_t         status_reg_req_header,
    input  logic                status_reg_req_ready,
    input  logic                status_reg_resp_valid,
    input  logic [31:0]         status_reg_resp_data,
    input  logic                status_reg_resp_error,
    output logic                status_reg_resp_ready,
    
    // Region Interface - Data Buffer
    output logic                data_buffer_req_valid,
    output logic [31:0]         data_buffer_req_addr,
    output logic [31:0]         data_buffer_req_data,
    output logic [3:0]          data_buffer_req_be,
    output logic                data_buffer_req_write,
    output tlp_header_t         data_buffer_req_header,
    input  logic                data_buffer_req_ready,
    input  logic                data_buffer_resp_valid,
    input  logic [31:0]         data_buffer_resp_data,
    input  logic                data_buffer_resp_error,
    output logic                data_buffer_resp_ready,
    
    // Region Interface - Custom PIO
    output logic                custom_pio_req_valid,
    output logic [31:0]         custom_pio_req_addr,
    output logic [31:0]         custom_pio_req_data,
    output logic [3:0]          custom_pio_req_be,
    output logic                custom_pio_req_write,
    output tlp_header_t         custom_pio_req_header,
    input  logic                custom_pio_req_ready,
    input  logic                custom_pio_resp_valid,
    input  logic [31:0]         custom_pio_resp_data,
    input  logic                custom_pio_resp_error,
    output logic                custom_pio_resp_ready,
    
    // Region Interface - MSI-X Engine
    output logic                msix_req_valid,
    output logic [31:0]         msix_req_addr,
    output logic [31:0]         msix_req_data,
    output logic [3:0]          msix_req_be,
    output logic                msix_req_write,
    output tlp_header_t         msix_req_header,
    input  logic                msix_req_ready,
    input  logic                msix_resp_valid,
    input  logic [31:0]         msix_resp_data,
    input  logic                msix_resp_error,
    output logic                msix_resp_ready,
    
    // Completion Output Interface
    output logic                completion_valid,
    output tlp_completion_header_t completion_header,
    output logic [127:0]        completion_data,
    output logic                completion_has_data,
    input  logic                completion_ready,
    
    // Status and Debug
    output logic [31:0]         access_count,
    output logic [31:0]         error_count,
    output region_select_t      current_region,
    output logic [31:0]         current_address
);

    // ========================================================================
    // Local Parameters and Types
    // ========================================================================
    
    typedef enum logic [2:0] {
        DISPATCH_IDLE,
        DISPATCH_DECODE,
        DISPATCH_REQUEST,
        DISPATCH_WAIT_RESP,
        DISPATCH_COMPLETION,
        DISPATCH_ERROR
    } dispatch_state_t;

    // ========================================================================
    // Internal Signals
    // ========================================================================
    
    // State machine
    dispatch_state_t        current_state, next_state;
    
    // Address decoding
    region_select_t         target_region;
    logic [31:0]            region_offset;
    logic                   address_valid;
    logic                   region_access_error;
    
    // Request storage
    tlp_header_t            stored_header;
    logic [31:0]            stored_address;
    logic [31:0]            stored_data;
    logic [3:0]             stored_be;
    logic                   stored_write;
    logic [11:0]            stored_byte_count;
    
    // Response aggregation
    logic                   any_resp_valid;
    logic [31:0]            aggregated_resp_data;
    logic                   aggregated_resp_error;
    
    // Counters
    logic [31:0]            access_count_reg;
    logic [31:0]            error_count_reg;
    
    // ========================================================================
    // Address Decoding Logic
    // ========================================================================
    
    always_comb begin
        target_region = REGION_INVALID;
        region_offset = '0;
        address_valid = 1'b0;
        region_access_error = 1'b0;
        
        // Use utility functions from bar_layout_pkg for clean address decoding
        priority if (is_device_ctrl_region(parsed_tlp_address[31:0])) begin
            target_region = REGION_DEVICE_CTRL;
            region_offset = get_region_offset(parsed_tlp_address[31:0], DEVICE_CTRL_BASE);
            address_valid = 1'b1;
        end else if (is_status_reg_region(parsed_tlp_address[31:0])) begin
            target_region = REGION_STATUS_REG;
            region_offset = get_region_offset(parsed_tlp_address[31:0], STATUS_REG_BASE);
            address_valid = 1'b1;
        end else if (is_data_buffer_region(parsed_tlp_address[31:0])) begin
            target_region = REGION_DATA_BUFFER;
            region_offset = get_region_offset(parsed_tlp_address[31:0], DATA_BUFFER_BASE);
            address_valid = 1'b1;
        end else if (is_custom_region(parsed_tlp_address[31:0])) begin
            target_region = REGION_CUSTOM_PIO;
            region_offset = get_region_offset(parsed_tlp_address[31:0], CUSTOM_REGION_BASE);
            address_valid = 1'b1;
        end else if (is_msix_table_region(parsed_tlp_address[31:0]) || 
                     is_msix_pba_region(parsed_tlp_address[31:0])) begin
            target_region = REGION_MSIX_TABLE;
            region_offset = get_region_offset(parsed_tlp_address[31:0], MSIX_TABLE_OFFSET);
            address_valid = 1'b1;
        end else if (parsed_tlp_address[31:0] < BAR_APERTURE_SIZE) begin
            target_region = REGION_GENERAL_MEM;
            region_offset = parsed_tlp_address[31:0];
            address_valid = 1'b1;
        end else begin
            // Address out of range
            region_access_error = ENABLE_RANGE_CHECKING;
        end
    end

    // ========================================================================
    // Main State Machine
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= DISPATCH_IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    always_comb begin
        next_state = current_state;
        parsed_tlp_ready = 1'b0;
        
        unique case (current_state)
            DISPATCH_IDLE: begin
                parsed_tlp_ready = 1'b1;
                if (parsed_tlp_valid) begin
                    if (parsed_tlp_error) begin
                        next_state = DISPATCH_ERROR;
                    end else begin
                        next_state = DISPATCH_DECODE;
                    end
                end
            end
            
            DISPATCH_DECODE: begin
                if (address_valid && !region_access_error) begin
                    next_state = DISPATCH_REQUEST;
                end else begin
                    next_state = DISPATCH_ERROR;
                end
            end
            
            DISPATCH_REQUEST: begin
                // Check if target region is ready to accept request
                case (target_region)
                    REGION_DEVICE_CTRL: begin
                        if (device_ctrl_req_ready) begin
                            next_state = DISPATCH_WAIT_RESP;
                        end
                    end
                    REGION_STATUS_REG: begin
                        if (status_reg_req_ready) begin
                            next_state = DISPATCH_WAIT_RESP;
                        end
                    end
                    REGION_DATA_BUFFER: begin
                        if (data_buffer_req_ready) begin
                            next_state = DISPATCH_WAIT_RESP;
                        end
                    end
                    REGION_CUSTOM_PIO: begin
                        if (custom_pio_req_ready) begin
                            next_state = DISPATCH_WAIT_RESP;
                        end
                    end
                    REGION_MSIX_TABLE: begin
                        if (msix_req_ready) begin
                            next_state = DISPATCH_WAIT_RESP;
                        end
                    end
                    default: begin
                        next_state = DISPATCH_ERROR;
                    end
                endcase
            end
            
            DISPATCH_WAIT_RESP: begin
                if (any_resp_valid) begin
                    next_state = DISPATCH_COMPLETION;
                end
            end
            
            DISPATCH_COMPLETION: begin
                if (completion_ready) begin
                    next_state = DISPATCH_IDLE;
                end
            end
            
            DISPATCH_ERROR: begin
                if (completion_ready) begin
                    next_state = DISPATCH_IDLE;
                end
            end
        endcase
    end

    // ========================================================================
    // Request Storage and Distribution
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            stored_header <= '0;
            stored_address <= '0;
            stored_data <= '0;
            stored_be <= '0;
            stored_write <= 1'b0;
            stored_byte_count <= '0;
        end else if (current_state == DISPATCH_DECODE) begin
            stored_header <= parsed_tlp_header;
            stored_address <= region_offset;
            stored_data <= parsed_tlp_data[31:0];
            stored_be <= parsed_tlp_header.first_be;
            stored_write <= is_write_request(parsed_tlp_header.fmt_type);
            stored_byte_count <= parsed_tlp_byte_count;
        end
    end
    
    // Request distribution to regions
    always_comb begin
        // Default all region requests to inactive
        device_ctrl_req_valid = 1'b0;
        status_reg_req_valid = 1'b0;
        data_buffer_req_valid = 1'b0;
        custom_pio_req_valid = 1'b0;
        msix_req_valid = 1'b0;
        
        // Common request data for all regions
        device_ctrl_req_addr = stored_address;
        device_ctrl_req_data = stored_data;
        device_ctrl_req_be = stored_be;
        device_ctrl_req_write = stored_write;
        device_ctrl_req_header = stored_header;
        
        status_reg_req_addr = stored_address;
        status_reg_req_data = stored_data;
        status_reg_req_be = stored_be;
        status_reg_req_write = stored_write;
        status_reg_req_header = stored_header;
        
        data_buffer_req_addr = stored_address;
        data_buffer_req_data = stored_data;
        data_buffer_req_be = stored_be;
        data_buffer_req_write = stored_write;
        data_buffer_req_header = stored_header;
        
        custom_pio_req_addr = stored_address;
        custom_pio_req_data = stored_data;
        custom_pio_req_be = stored_be;
        custom_pio_req_write = stored_write;
        custom_pio_req_header = stored_header;
        
        msix_req_addr = stored_address;
        msix_req_data = stored_data;
        msix_req_be = stored_be;
        msix_req_write = stored_write;
        msix_req_header = stored_header;
        
        // Activate appropriate region based on target
        if (current_state == DISPATCH_REQUEST) begin
            unique case (target_region)
                REGION_DEVICE_CTRL: device_ctrl_req_valid = 1'b1;
                REGION_STATUS_REG:  status_reg_req_valid = 1'b1;
                REGION_DATA_BUFFER: data_buffer_req_valid = 1'b1;
                REGION_CUSTOM_PIO:  custom_pio_req_valid = 1'b1;
                REGION_MSIX_TABLE:  msix_req_valid = 1'b1;
                default: begin
                    // Invalid region - will be handled in error state
                end
            endcase
        end
    end

    // ========================================================================
    // Response Aggregation
    // ========================================================================
    
    always_comb begin
        any_resp_valid = device_ctrl_resp_valid || status_reg_resp_valid || 
                        data_buffer_resp_valid || custom_pio_resp_valid || 
                        msix_resp_valid;
        
        aggregated_resp_data = '0;
        aggregated_resp_error = 1'b0;
        
        // Response ready signals - only assert for active region
        device_ctrl_resp_ready = 1'b0;
        status_reg_resp_ready = 1'b0;
        data_buffer_resp_ready = 1'b0;
        custom_pio_resp_ready = 1'b0;
        msix_resp_ready = 1'b0;
        
        if (current_state == DISPATCH_WAIT_RESP || current_state == DISPATCH_COMPLETION) begin
            unique case (target_region)
                REGION_DEVICE_CTRL: begin
                    aggregated_resp_data = device_ctrl_resp_data;
                    aggregated_resp_error = device_ctrl_resp_error;
                    device_ctrl_resp_ready = completion_ready;
                end
                REGION_STATUS_REG: begin
                    aggregated_resp_data = status_reg_resp_data;
                    aggregated_resp_error = status_reg_resp_error;
                    status_reg_resp_ready = completion_ready;
                end
                REGION_DATA_BUFFER: begin
                    aggregated_resp_data = data_buffer_resp_data;
                    aggregated_resp_error = data_buffer_resp_error;
                    data_buffer_resp_ready = completion_ready;
                end
                REGION_CUSTOM_PIO: begin
                    aggregated_resp_data = custom_pio_resp_data;
                    aggregated_resp_error = custom_pio_resp_error;
                    custom_pio_resp_ready = completion_ready;
                end
                REGION_MSIX_TABLE: begin
                    aggregated_resp_data = msix_resp_data;
                    aggregated_resp_error = msix_resp_error;
                    msix_resp_ready = completion_ready;
                end
                default: begin
                    aggregated_resp_error = 1'b1;
                end
            endcase
        end
    end

    // ========================================================================
    // Completion Generation
    // ========================================================================
    
    always_comb begin
        completion_valid = (current_state == DISPATCH_COMPLETION) || 
                          (current_state == DISPATCH_ERROR);
        
        completion_has_data = !stored_write && !aggregated_resp_error && 
                             (current_state == DISPATCH_COMPLETION);
        
        completion_data = {96'h0, aggregated_resp_data};
        
        // Generate completion header
        if (current_state == DISPATCH_ERROR || aggregated_resp_error) begin
            completion_header = generate_completion_header(
                stored_header,
                16'h0000,  // Completer ID (should be parameterized)
                3'b001,    // Unsupported Request
                12'h000    // No data on error
            );
        end else begin
            completion_header = generate_completion_header(
                stored_header,
                16'h0000,  // Completer ID
                3'b000,    // Successful Completion
                stored_write ? 12'h000 : stored_byte_count
            );
        end
    end

    // ========================================================================
    // Performance Counters and Status
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_count_reg <= '0;
            error_count_reg <= '0;
        end else begin
            // Count successful accesses
            if (current_state == DISPATCH_COMPLETION && completion_ready) begin
                access_count_reg <= access_count_reg + 1;
            end
            
            // Count errors
            if (current_state == DISPATCH_ERROR && completion_ready) begin
                error_count_reg <= error_count_reg + 1;
            end
        end
    end
    
    // Status outputs
    assign access_count = access_count_reg;
    assign error_count = error_count_reg;
    assign current_region = target_region;
    assign current_address = stored_address;

    // ========================================================================
    // Access Logging (Optional)
    // ========================================================================
    
`ifdef SIMULATION
    generate
        if (ENABLE_ACCESS_LOGGING) begin : gen_access_logging
            always @(posedge clk) begin
                if (current_state == DISPATCH_REQUEST) begin
                    $display("[BAR Dispatcher] %s access to region %s, addr=0x%08X, data=0x%08X at time %0t",
                             stored_write ? "WRITE" : "READ",
                             target_region.name(),
                             stored_address,
                             stored_data,
                             $time);
                end
                
                if (current_state == DISPATCH_ERROR) begin
                    $display("[BAR Dispatcher] ERROR: Invalid access to addr=0x%08X at time %0t",
                             parsed_tlp_address[31:0],
                             $time);
                end
            end
        end
    endgenerate
`endif

    // ========================================================================
    // Assertions for Design Verification
    // ========================================================================
    
`ifdef SIMULATION
    // Check that only one region responds at a time
    property single_region_response;
        @(posedge clk) disable iff (!reset_n)
        $onehot0({device_ctrl_resp_valid, status_reg_resp_valid, 
                  data_buffer_resp_valid, custom_pio_resp_valid, msix_resp_valid});
    endproperty
    assert property (single_region_response) else 
        $error("BAR Dispatcher: Multiple regions responding simultaneously");
    
    // Check that address decoding is mutually exclusive
    property address_decode_exclusive;
        @(posedge clk) disable iff (!reset_n)
        $onehot0({is_device_ctrl_region(parsed_tlp_address[31:0]),
                  is_status_reg_region(parsed_tlp_address[31:0]),
                  is_data_buffer_region(parsed_tlp_address[31:0]),
                  is_custom_region(parsed_tlp_address[31:0]),
                  is_msix_table_region(parsed_tlp_address[31:0])});
    endproperty
    assert property (address_decode_exclusive) else 
        $error("BAR Dispatcher: Overlapping address regions detected");
    
    // Check completion generation
    property completion_generation;
        @(posedge clk) disable iff (!reset_n)
        (current_state == DISPATCH_COMPLETION || current_state == DISPATCH_ERROR) 
        |-> completion_valid;
    endproperty
    assert property (completion_generation) else 
        $error("BAR Dispatcher: Completion not generated when required");
`endif

endmodule