{#- PCIe Configuration Space Shadow BRAM Template -#}
{{ header | safe }}

module pcileech_tlps128_cfgspace_shadow #(
    parameter CONFIG_SPACE_SIZE = {{ CONFIG_SPACE_SIZE }},  // {{ CONFIG_SPACE_SIZE }} byte configuration space
    parameter OVERLAY_ENTRIES = {{ OVERLAY_ENTRIES }},      // Number of overlay RAM entries
    parameter EXT_CFG_CAP_PTR = {{ EXT_CFG_CAP_PTR | default(256) }},     // Extended capability pointer (default 0x100)
    parameter EXT_CFG_XP_CAP_PTR = {{ EXT_CFG_XP_CAP_PTR | default(256) }} // Express capability pointer in extended space
) (
    // Clock and reset
    input  logic        clk,
    input  logic        reset_n,
    
    // Port A - PCIe configuration access
    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,
    
    // CFGTLP control inputs
    input  logic        cfgtlp_wren,
    input  logic        cfgtlp_zero,
    input  logic        cfgtlp_pcie_write_en,
    input  logic [1:0]  cfg_a7,
    
    // Port B - Host access for initialization and monitoring
    input  logic        host_access_en,
    input  logic        host_write_en,
    input  logic [11:0] host_addr,
    input  logic [31:0] host_write_data,
    output logic [31:0] host_read_data{% if DUAL_PORT %},
    
    // Port C - Additional dual-port access
    input  logic        clkB,
    input  logic        enB,
    input  logic        weB,
    input  logic [11:0] addrB,
    input  logic [31:0] dinB,
    output logic [31:0] doutB{% endif %}
);

    // Main configuration space BRAM ({{ CONFIG_SPACE_SIZE }} bytes = {{ CONFIG_SPACE_SIZE // 4 }} dwords)
    (* ram_style="block" *) logic [31:0] config_space_ram[0:{{ (CONFIG_SPACE_SIZE // 4) - 1 }}];
    
    // Overlay RAM for writable fields ({{ OVERLAY_ENTRIES }} entries)
    logic [31:0] overlay_ram[0:OVERLAY_ENTRIES-1];
    
    // Auto-generated overlay constants
    {%- if OVERLAY_MAP is mapping -%}
        {%- for reg, mask in OVERLAY_MAP.items() -%}
            localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
            localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(mask) }};
        {%- endfor -%}
    {%- elif OVERLAY_MAP is sequence -%}
        {%- for pair in OVERLAY_MAP -%}
            {%- set reg = pair[0] if pair|length > 0 else 0 -%}
            {%- set mask = pair[1] if pair|length > 1 else pair[0] if pair|length>0 else 0 -%}
            localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
            localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(mask) }};
        {%- endfor -%}
    {%- else -%}
        // No overlays defined
    {%- endif -%}
    
    // Overlay RAM address mapping lookup table
    logic [31:0] overlay_indices[0:1023];
    logic [31:0] overlay_masks[0:1023];
    
    // Lookup tables are initialized in reset logic below
    
    // Overlay lookup functions
    function int get_overlay_index(input logic [9:0] reg_num);
        return (overlay_indices[reg_num] == 32'hFFFFFFFF) ? -1 : int'(overlay_indices[reg_num]);
    endfunction
    
    function logic [31:0] get_overlay_mask(input logic [9:0] reg_num);
        return overlay_masks[reg_num];
    endfunction
    
    // Function to determine if shadow should take over from hardware
    function logic use_shadow_cfg(input logic [11:0] addr);
        // Shadow takes over for extended configuration space based on pointer values
        if (addr >= EXT_CFG_CAP_PTR) begin
            return 1'b1;  // Use shadow for all extended config space
        end else if (addr >= EXT_CFG_XP_CAP_PTR && addr < CONFIG_SPACE_SIZE) begin
            return 1'b1;  // Use shadow for express capabilities
        end else begin
            return 1'b0;  // Use hardware for standard config space below pointers
        end
    endfunction
    
    // Function to calculate effective register number with cfg_a7
    function logic [9:0] get_effective_reg_num(input logic [9:0] reg_num, input logic [1:0] cfg_a7);
        // Modify bit 7 of the register number based on cfg_a7
        return {reg_num[9:8], cfg_a7[1] ? cfg_a7[0] : reg_num[7], reg_num[6:0]};
    endfunction
    
    // PCIe configuration access state machine
    typedef enum logic [1:0] {
        CFG_IDLE,
        CFG_READ,
        CFG_WRITE,
        CFG_COMPLETE
    } cfg_state_t;
    
    cfg_state_t cfg_state;
    logic [9:0] current_reg_num;
    logic [9:0] effective_reg_num;
    logic [31:0] read_data;
    logic read_data_valid;
    
    // Variables for overlay processing
    logic [31:0] overlay_idx_reg;
    logic [31:0] overlay_mask_reg;
    logic [31:0] current_value_reg;
    
    // Combinational variables for overlay processing
    int overlay_idx_comb;
    logic [31:0] overlay_mask_comb;
    
    // Configuration access state machine
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            cfg_state <= CFG_IDLE;
            read_data_valid <= 1'b0;
            read_data <= 32'h0;
            current_reg_num <= 10'h0;
            overlay_idx_reg <= 32'hFFFFFFFF;
            overlay_mask_reg <= 32'h0;
            
            // Initialize lookup tables (replacing initial block)
            for (int i = 0; i < 1024; i++) begin
                overlay_indices[i] <= 32'hFFFFFFFF;
                overlay_masks[i] <= 32'h00000000;
            end
            
            // Set valid entries from OVERLAY_MAP
            {%- if OVERLAY_MAP is mapping -%}
                {%- for reg, mask in OVERLAY_MAP.items() -%}
                    overlay_indices[{{ reg }}] <= OVR_IDX_{{ "%03X"|format(reg) }};
                    overlay_masks[{{ reg }}] <= OVR_MASK_{{ "%03X"|format(reg) }};
                {%- endfor -%}
            {%- elif OVERLAY_MAP is sequence -%}
                {%- for pair in OVERLAY_MAP -%}
                    {%- set reg = pair[0] if pair|length>0 else 0 -%}
                    overlay_indices[{{ reg }}] <= OVR_IDX_{{ "%03X"|format(reg) }};
                    overlay_masks[{{ reg }}] <= OVR_MASK_{{ "%03X"|format(reg) }};
                {%- endfor -%}
            {%- endif -%}

            // Initialize overlay RAM to zeros (replacing second initial block)
            for (int i = 0; i < OVERLAY_ENTRIES; i++) begin
                overlay_ram[i] <= 32'h0;
            end
            
            // Note: $readmemh for config_space_ram initialization cannot be directly
            // replaced in reset logic. For synthesis, consider using a memory
            // initialization file in your synthesis tool or initializing with
            // default values here.
            current_value_reg <= 32'h0;
        end else begin
            case (cfg_state)
                CFG_IDLE: begin
                    read_data_valid <= 1'b0;
                    
                    if (cfg_ext_read_received) begin
                        cfg_state <= CFG_READ;
                        current_reg_num <= cfg_ext_register_number;
                        effective_reg_num <= get_effective_reg_num(cfg_ext_register_number, cfg_a7);
                    end else if (cfg_ext_write_received) begin
                        cfg_state <= CFG_WRITE;
                        current_reg_num <= cfg_ext_register_number;
                        effective_reg_num <= get_effective_reg_num(cfg_ext_register_number, cfg_a7);
                    end
                end
                
                CFG_READ: begin
                    // Check if we should use shadow configuration
                    if (use_shadow_cfg({2'b00, effective_reg_num} << 2)) begin
                        // Read from configuration space shadow using effective register number
                        read_data <= config_space_ram[effective_reg_num];
                        
                        // Check if CFGTLP zero data is enabled
                        if (cfgtlp_zero) begin
                            read_data <= 32'h00000000;  // Return zero data when cfgtlp_zero is set
                        end else begin
                            // Check if this register has an overlay entry
                            overlay_idx_reg <= overlay_indices[effective_reg_num];
                            overlay_mask_reg <= overlay_masks[effective_reg_num];
                            
                            if (overlay_indices[effective_reg_num] != 32'hFFFFFFFF) begin
                                // Apply overlay data for writable bits
                                read_data <= (config_space_ram[effective_reg_num] & ~overlay_masks[effective_reg_num]) |
                                             (overlay_ram[overlay_indices[effective_reg_num]] & overlay_masks[effective_reg_num]);
                            end
                        end
                        
                        read_data_valid <= 1'b1;
                    end else begin
                        // For addresses below the pointer, let hardware handle it
                        read_data_valid <= 1'b0;  // Indicate shadow doesn't handle this
                    end
                    
                    cfg_state <= CFG_COMPLETE;
                end
                
                CFG_WRITE: begin
                    // Check if we should use shadow configuration and if PCIe writes are enabled
                    if (use_shadow_cfg({2'b00, effective_reg_num} << 2) && cfgtlp_pcie_write_en) begin
                        // Handle write to configuration space shadow
                        overlay_idx_reg <= overlay_indices[effective_reg_num];
                        overlay_mask_reg <= overlay_masks[effective_reg_num];
                        
                        if (overlay_indices[effective_reg_num] != 32'hFFFFFFFF) begin
                            // Only update writable bits in the overlay RAM
                            current_value_reg <= overlay_ram[overlay_indices[effective_reg_num]];
                            
                            // Apply byte enables - build new value
                            if (cfg_ext_write_byte_enable[0])
                                current_value_reg[7:0] <= (cfg_ext_write_data[7:0] & overlay_masks[effective_reg_num][7:0]) |
                                                         (overlay_ram[overlay_indices[effective_reg_num]][7:0] & ~overlay_masks[effective_reg_num][7:0]);
                            if (cfg_ext_write_byte_enable[1])
                                current_value_reg[15:8] <= (cfg_ext_write_data[15:8] & overlay_masks[effective_reg_num][15:8]) |
                                                          (overlay_ram[overlay_indices[effective_reg_num]][15:8] & ~overlay_masks[effective_reg_num][15:8]);
                            if (cfg_ext_write_byte_enable[2])
                                current_value_reg[23:16] <= (cfg_ext_write_data[23:16] & overlay_masks[effective_reg_num][23:16]) |
                                                           (overlay_ram[overlay_indices[effective_reg_num]][23:16] & ~overlay_masks[effective_reg_num][23:16]);
                            if (cfg_ext_write_byte_enable[3])
                                current_value_reg[31:24] <= (cfg_ext_write_data[31:24] & overlay_masks[effective_reg_num][31:24]) |
                                                           (overlay_ram[overlay_indices[effective_reg_num]][31:24] & ~overlay_masks[effective_reg_num][31:24]);
                            
                            overlay_ram[overlay_indices[effective_reg_num]] <= current_value_reg;
                        end
                    end
                    // If not using shadow or PCIe writes disabled, hardware handles the write
                    
                    cfg_state <= CFG_COMPLETE;
                end
                
                CFG_COMPLETE: begin
                    read_data_valid <= 1'b0;
                    cfg_state <= CFG_IDLE;
                end
                
                default: cfg_state <= CFG_IDLE;
            endcase
        end
    end
    
    // Output assignments
    assign cfg_ext_read_data = read_data;
    assign cfg_ext_read_data_valid = read_data_valid;
    
    // Host access port (Port B) - synchronous read/write
    always_ff @(posedge clk) begin
        if (!reset_n) begin
            host_read_data <= 32'h0;
        end else if (host_access_en) begin
            if (host_write_en) begin
                // Host write to configuration space
                config_space_ram[host_addr[11:2]] <= host_write_data;
            end else begin
                // Host read from configuration space
                host_read_data <= config_space_ram[host_addr[11:2]];
                
                // Check if this register has an overlay entry
                if (overlay_indices[host_addr[11:2]] != 32'hFFFFFFFF) begin
                    // Apply overlay data for writable bits
                    host_read_data <= (config_space_ram[host_addr[11:2]] & ~overlay_masks[host_addr[11:2]]) |
                                     (overlay_ram[overlay_indices[host_addr[11:2]]] & overlay_masks[host_addr[11:2]]);
                end
            end
        end
    end
    
{% if DUAL_PORT %}
    // Additional dual-port access (Port C)
    always_ff @(posedge clkB) begin
        if (enB) begin
            if (weB) begin
                // Port B write to configuration space
                config_space_ram[addrB[11:2]] <= dinB;
            end else begin
                // Port B read from configuration space
                doutB <= config_space_ram[addrB[11:2]];
                
                // Check if this register has an overlay entry
                if (overlay_indices[addrB[11:2]] != 32'hFFFFFFFF) begin
                    // Apply overlay data for writable bits
                    doutB <= (config_space_ram[addrB[11:2]] & ~overlay_masks[addrB[11:2]]) |
                             (overlay_ram[overlay_indices[addrB[11:2]]] & overlay_masks[addrB[11:2]]);
                end
            end
        end
    end
    
{% endif %}
    // NOTE: For synthesis, memory initialization should be done in reset logic
    // The following is for simulation only and may cause synthesis issues:
    // initial begin
    //     $readmemh("config_space_init.hex", config_space_ram);
    // end
    
    // For synthesis, memory initialization should be handled in the reset logic
    // or through memory initialization parameters in your synthesis tool

endmodule