//==============================================================================
// Dynamic PCIe BAR Controller with Configuration Space Shadow Integration
// Generated by PCILeech Firmware Generator - dynamic variant
//
// Dynamic abilities:
//   • Accepts a runtime BAR index (0‑5) so the same module can be reused for any BAR.
//   • Exposes a small "custom window" hook for vendor‑specific registers.
//   • Retains parameterised BAR aperture and MSI‑X settings.
//   • Keeps BRAM‑hinted BAR memory but in a synchronous form that infers true block RAM.
//==============================================================================

module pcileech_tlps128_bar_controller #(
    parameter BAR_APERTURE_SIZE   = {{ BAR_APERTURE_SIZE }},  // {{ BAR_APERTURE_SIZE }} bytes by default
    parameter NUM_MSIX           = {{ NUM_MSIX }},
    parameter MSIX_TABLE_BIR     = {{ MSIX_TABLE_BIR }},
    parameter MSIX_TABLE_OFFSET  = {{ MSIX_TABLE_OFFSET }},
    parameter MSIX_PBA_BIR       = {{ MSIX_PBA_BIR }},
    parameter MSIX_PBA_OFFSET    = {{ MSIX_PBA_OFFSET }},
    // Address map (all offsets within *this* BAR)
    parameter CONFIG_SHDW_HI     = {{ CONFIG_SHDW_HI }}, // top 4 KB reserved for cfg space shadow
    parameter CUSTOM_WIN_BASE    = {{ CUSTOM_WIN_BASE }}  // next‑to‑top 4 KB reserved for custom regs
) (
    // Clock and reset
    input  logic        clk,
    input  logic        reset_n,

    // BAR selector (which BAR of the endpoint this controller is mapped to)
    input  logic [2:0]  bar_index,

    // PCIe BAR interface
    input  logic [31:0] bar_addr,
    input  logic [31:0] bar_wr_data,
    input  logic [3:0]  bar_wr_be,
    input  logic        bar_wr_en,
    input  logic        bar_rd_en,
    output logic [31:0] bar_rd_data,

    // PCIe configuration space interface
    input  logic        cfg_ext_read_received,
    input  logic        cfg_ext_write_received,
    input  logic [9:0]  cfg_ext_register_number,
    input  logic [3:0]  cfg_ext_function_number,
    input  logic [31:0] cfg_ext_write_data,
    input  logic [3:0]  cfg_ext_write_byte_enable,
    output logic [31:0] cfg_ext_read_data,
    output logic        cfg_ext_read_data_valid,

    // MSI‑X interrupt interface
    output logic        msix_interrupt,
    output logic [10:0] msix_vector,
    input  logic        msix_interrupt_ack,

    // --- Custom window hook --------------------------------------------------
    output logic        custom_win_sel,
    output logic [11:0] custom_win_addr,
    output logic [31:0] custom_win_wdata,
    output logic [3:0]  custom_win_be,
    output logic        custom_win_we,
    output logic        custom_win_re,
    input  logic [31:0] custom_win_rdata
);

    // ------------------------------------------------------------
    // Configuration‑space shadow BRAM
    // ------------------------------------------------------------
    pcileech_tlps128_cfgspace_shadow cfg_shdw (
        .clk                       (clk),
        .reset_n                   (reset_n),
        .cfg_ext_read_received     (cfg_ext_read_received),
        .cfg_ext_write_received    (cfg_ext_write_received),
        .cfg_ext_register_number   (cfg_ext_register_number),
        .cfg_ext_function_number   (cfg_ext_function_number),
        .cfg_ext_write_data        (cfg_ext_write_data),
        .cfg_ext_write_byte_enable (cfg_ext_write_byte_enable),
        .cfg_ext_read_data         (cfg_ext_read_data),
        .cfg_ext_read_data_valid   (cfg_ext_read_data_valid),
        .host_access_en            (bar_access_cfg),
        .host_write_en             (bar_wr_en && bar_access_cfg),
        .host_addr                 (bar_addr[11:0]),
        .host_write_data           (bar_wr_data),
        .host_read_data            (cfg_shdw_rdata)
    );

    // ------------------------------------------------------------
    // Internal signal declarations
    // ------------------------------------------------------------
    logic [31:0] cfg_shdw_rdata;
    logic [31:0] msix_tbl_rdata;
    
    // ------------------------------------------------------------
    // MSI‑X capability decode (reads from cfg shadow BRAM)
    // ------------------------------------------------------------
    logic        msix_enabled;
    logic        msix_fn_masked;
    logic [31:0] msix_cap_data;

    localparam [9:0] MSIX_CAP_REG = 10'h1C; // (0x70 >> 2)

    // Access MSI-X capability register through host interface
    logic [31:0] msix_cap_read_data;
    logic        msix_cap_read_valid;
    
    always_comb begin
        // Read MSI-X capability register from config space shadow
        // This should be connected to the config space shadow's host read data
        msix_cap_data   = cfg_shdw_rdata; // Will be updated when we read the specific register
        msix_enabled    = msix_cap_data[15];
        msix_fn_masked  = msix_cap_data[14];
    end

    // ------------------------------------------------------------
    // MSI‑X table sub‑module
    // ------------------------------------------------------------
    msix_table #(
        .NUM_MSIX          (NUM_MSIX),
        .MSIX_TABLE_BIR    (MSIX_TABLE_BIR),
        .MSIX_TABLE_OFFSET (MSIX_TABLE_OFFSET),
        .MSIX_PBA_BIR      (MSIX_PBA_BIR),
        .MSIX_PBA_OFFSET   (MSIX_PBA_OFFSET)
    ) msix_tbl (
        .clk               (clk),
        .reset_n           (reset_n),
        .bar_addr          (bar_addr),
        .bar_index         (bar_index),
        .bar_wr_data       (bar_wr_data),
        .bar_wr_en         (bar_wr_en),
        .bar_wr_be         (bar_wr_be),
        .bar_rd_en         (bar_rd_en),
        .bar_rd_data       (msix_tbl_rdata),
        .bar_access_match  (bar_access_msix),
        .msix_enable       (msix_enabled),
        .msix_function_mask(msix_fn_masked),
        .msix_interrupt    (msix_interrupt),
        .msix_vector       (msix_vector),
        .msix_interrupt_ack(msix_interrupt_ack)
    );

    // ------------------------------------------------------------
    // BAR memory (block‑RAM‑friendly, synchronous read)
    // ------------------------------------------------------------
    (* ram_style = "block" *) logic [31:0] bar_mem [({{ BAR_APERTURE_SIZE }}/4)-1:0];
    logic [31:0] bar_mem_q;

    always_ff @(posedge clk) begin
        if (bar_wr_en && bar_access_mem && (bar_addr[31:2] < ({{ BAR_APERTURE_SIZE }}/4))) begin
{% if USE_BYTE_ENABLES %}
            for (int b=0; b<4; b++) begin
                if (bar_wr_be[b])
                    bar_mem[bar_addr[31:2]][8*b +: 8] <= bar_wr_data[8*b +: 8];
            end
{% else %}
            bar_mem[bar_addr[31:2]] <= bar_wr_data;
{% endif %}
        end
        if (bar_rd_en && bar_access_mem && (bar_addr[31:2] < ({{ BAR_APERTURE_SIZE }}/4))) begin
            bar_mem_q <= bar_mem[bar_addr[31:2]];
        end
    end

    // ------------------------------------------------------------
    // Address decode helpers
    // ------------------------------------------------------------
    logic bar_access_cfg;
    logic bar_access_msix;
    logic bar_access_cust;
    logic bar_access_mem;

    assign bar_access_cfg  = (bar_addr[31:12] == {{ CONFIG_SHDW_HI }});
    assign bar_access_cust = (bar_addr[31:12] == {{ CUSTOM_WIN_BASE }});
    assign bar_access_mem  = !bar_access_cfg && !bar_access_msix && !bar_access_cust;

    // ------------------------------------------------------------
    // Custom window bus out
    // ------------------------------------------------------------
    assign custom_win_sel   = bar_access_cust;
    assign custom_win_addr  = bar_addr[11:0];
    assign custom_win_wdata = bar_wr_data;
    assign custom_win_be    = bar_wr_be;
    assign custom_win_we    = bar_wr_en  && bar_access_cust;
    assign custom_win_re    = bar_rd_en  && bar_access_cust;

    // ------------------------------------------------------------
    // Read data mux
    // ------------------------------------------------------------
    always_comb begin
        unique case (1'b1)
            bar_access_cfg  : bar_rd_data = cfg_shdw_rdata;
            bar_access_msix : bar_rd_data = msix_tbl_rdata;
            bar_access_cust : bar_rd_data = custom_win_rdata;
            bar_access_mem  : bar_rd_data = bar_mem_q;
            default         : bar_rd_data = 32'hDEADBEEF;
        endcase
    end

    // ------------------------------------------------------------
    // Initial BAR memory clear (simulation only)
    // ------------------------------------------------------------
`ifdef XILINX_SIMULATOR
    initial begin
        for (int i=0; i<({{ BAR_APERTURE_SIZE }}/4); i++) bar_mem[i] = 32'h0;
    end
`endif

    // ------------------------------------------------------------
    // Helper task for SW models
    // ------------------------------------------------------------
    task automatic trigger_msix_interrupt(input logic [10:0] vec);
        if (vec < NUM_MSIX) msix_tbl.trigger_interrupt(vec);
    endtask

endmodule