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

`default_nettype none

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 in bytes (default 0x100)
    parameter EXT_CFG_XP_CAP_PTR = {{ EXT_CFG_XP_CAP_PTR | default(256) }}, // Express capability pointer in bytes
    // Enhanced architecture parameters (values provided via context fallbacks)
    parameter HASH_TABLE_SIZE = {{ HASH_TABLE_SIZE }},       // Hash table size for sparse mapping
    parameter ENABLE_SPARSE_MAP = {{ ENABLE_SPARSE_MAP }},   // Enable sparse mapping architecture
    parameter ENABLE_BIT_TYPES = {{ ENABLE_BIT_TYPES }},     // Enable bit-type support
    // Optional configuration-space initialization HEX file (for simulation and BRAM init capable tools)
    parameter string CFG_INIT_HEX = ""
) (
    // Clock and reset
    input  wire         clk,
    input  wire         reset_n,
    
    // Port A - PCIe configuration access
    input  wire         cfg_ext_read_received,
    input  wire         cfg_ext_write_received,
    input  wire  [9:0]  cfg_ext_register_number,
    input  wire  [3:0]  cfg_ext_function_number,
    input  wire  [31:0] cfg_ext_write_data,
    input  wire  [3:0]  cfg_ext_write_byte_enable,
    output wire  [31:0] cfg_ext_read_data,
    output wire         cfg_ext_read_data_valid,
    output wire         shadow_handled,         // Indicates shadow handled the request
    
    // CFGTLP control inputs
    input  wire         cfgtlp_wren,      // Kept for interface compatibility (currently unused)
    input  wire         cfgtlp_zero,
    input  wire         cfgtlp_pcie_write_en,
    input  wire  [1:0]  cfg_a7,
    
    // Port B - Host access for initialization and monitoring
    input  wire         host_access_en,
    input  wire         host_write_en,
    input  wire  [11:0] host_addr,         // Byte address (12 bits = 4KB)
    input  wire  [31:0] host_write_data,
    output logic [31:0] host_read_data{% if DUAL_PORT %},
    
    // Port C - Additional dual-port access
    input  wire         clkB,
    input  wire         enB,
    input  wire         weB,
    input  wire  [11:0] addrB,
    input  wire  [31:0] dinB,
    output logic [31:0] doutB{% endif %}
);

    // =====================================================================
    // Bit Type Definitions (Phase 1: Core Infrastructure)
    // =====================================================================
    
    // Bit type encoding (4 bits per byte)
    localparam logic [3:0] BIT_TYPE_RO     = 4'b0000;  // Read-only
    localparam logic [3:0] BIT_TYPE_RW     = 4'b0001;  // Read-write
    localparam logic [3:0] BIT_TYPE_RW1C   = 4'b0010;  // Write-1-to-clear
    localparam logic [3:0] BIT_TYPE_RW1S   = 4'b0011;  // Write-1-to-set
    localparam logic [3:0] BIT_TYPE_RSVDP  = 4'b0100;  // Reserved, preserve on write
    localparam logic [3:0] BIT_TYPE_RSVDZ  = 4'b0101;  // Reserved, return zero
    localparam logic [3:0] BIT_TYPE_HWINIT = 4'b0110;  // Hardware init, software RO
    localparam logic [3:0] BIT_TYPE_STICKY = 4'b0111;  // Sticky across soft reset

    // Sentinel returned on out-of-range accesses (context-driven for uniqueness)
    localparam logic [31:0] OUT_OF_RANGE_SENTINEL = 32'h{{ (OUT_OF_RANGE_SENTINEL | default('DEADBEEF')) | upper }};
    localparam logic [31:0] DEFAULT_RW_TYPE       = 32'h11111111;  // Default RW mask for legacy overlays

    // =====================================================================
    // Memory Architecture
    // =====================================================================

    // Main configuration space BRAM ({{ CONFIG_SPACE_SIZE }} bytes = {{ CONFIG_SPACE_SIZE // 4 }} dwords)
    (* ram_style="block" *)
    // BRAM initialization via $readmemh in initial block (string parameters not supported in attributes)
    logic [31:0] config_space_ram[0:{{ (CONFIG_SPACE_SIZE // 4) - 1 }}];
    
    // Enhanced overlay RAM with bit type support ({{ OVERLAY_ENTRIES }} entries)
    generate
        if (ENABLE_BIT_TYPES) begin : gen_enhanced_overlay
            (* ram_style="block" *) logic [31:0] overlay_type_ram[0:OVERLAY_ENTRIES-1];
            (* ram_style="block" *) logic [31:0] overlay_mask_ram[0:OVERLAY_ENTRIES-1];
            (* ram_style="block" *) logic [31:0] overlay_data_ram[0:OVERLAY_ENTRIES-1];
        end else begin : gen_legacy_overlay
            (* ram_style="block" *) logic [31:0] overlay_data_ram[0:OVERLAY_ENTRIES-1];
        end
    endgenerate
    
    // =====================================================================
    // Sparse Mapping Architecture (Phase 1)
    // =====================================================================
    // Named generate block marker for sparse-map feature toggle (test hook)
    // Note: This block is a marker only; declarations remain at module scope
    // to preserve tool compatibility and simple name resolution.
    if (ENABLE_SPARSE_MAP) begin : gen_sparse_map
    end
    
    // Hash table entry for sparse mapping - using separate arrays for synthesis compatibility
    // SystemVerilog unpacked arrays of structs cannot have ram_style attributes applied directly
    (* ram_style="distributed" *) logic        hash_table_valid[0:HASH_TABLE_SIZE-1];
    (* ram_style="distributed" *) logic [9:0]  hash_table_reg_num[0:HASH_TABLE_SIZE-1];
    (* ram_style="distributed" *) logic [7:0]  hash_table_overlay_index[0:HASH_TABLE_SIZE-1];
    
    // Hash function for sparse mapping
    function automatic logic [7:0] hash_reg_num(input logic [9:0] reg_num);
        // Simple XOR folding hash function as specified in architecture
        return reg_num[7:0] ^ {reg_num[9:8], reg_num[7:2]};
    endfunction
    
    // Linear probing collision resolution
    function automatic logic [7:0] find_hash_entry(input logic [9:0] reg_num);
        if (ENABLE_SPARSE_MAP) begin
            logic [7:0] hash_idx = hash_reg_num(reg_num);
            logic [7:0] probe_count = 0;
            
            // Linear probe with wrap-around
            while (probe_count < HASH_TABLE_SIZE) begin
                if (hash_table_valid[hash_idx] && 
                    hash_table_reg_num[hash_idx] == reg_num) begin
                    return hash_table_overlay_index[hash_idx];
                end
                if (!hash_table_valid[hash_idx]) begin
                    return 8'hFF; // Not found
                end
                hash_idx = (hash_idx + 1) & (HASH_TABLE_SIZE-1);
                probe_count++;
            end
            return 8'hFF; // Table full or not found
        end else begin
            return 8'hFF; // Sparse mapping disabled
        end
    endfunction
    
    // =====================================================================
    // Legacy Direct Mapping Support (for backward compatibility)
    // =====================================================================
    
    // Auto-generated overlay constants
    {%- if OVERLAY_MAP is not defined or OVERLAY_MAP is none -%}
        // No overlays defined (OVERLAY_MAP is not defined or None)
    {%- elif OVERLAY_MAP is mapping -%}
        {%- for reg, entry in OVERLAY_MAP.items() -%}
            {%- if entry is sequence and entry|length >= 2 -%}
                {#- Enhanced/mapping format: [mask, type, ...] (support 2+ elements) -#}
                localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
                localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(entry[0]) }};
                localparam logic [31:0] OVR_TYPE_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(entry[1]) }};
            {%- elif entry is integer -%}
                {#- Legacy format - just mask -#}
                localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
                localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(entry) }};
                localparam logic [31:0] OVR_TYPE_{{ "%03X"|format(reg) }} = 32'h11111111; // Default RW for all bytes
            {%- endif -%}
        {%- else -%}
        // No overlays defined (empty map)
        {%- endfor -%}
    {%- elif OVERLAY_MAP is sequence -%}
        {%- for pair in OVERLAY_MAP -%}
            {%- if pair is sequence and pair|length >= 2 -%}
                {%- set reg = pair[0] -%}
                {%- if pair|length >= 3 -%}
                    {#- Enhanced format with bit types -#}
                    localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
                    localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(pair[1]) }};
                    localparam logic [31:0] OVR_TYPE_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(pair[2]) }};
                {%- else -%}
                    {#- Legacy format -#}
                    localparam int OVR_IDX_{{ "%03X"|format(reg) }} = {{ loop.index0 }};
                    localparam logic [31:0] OVR_MASK_{{ "%03X"|format(reg) }} = 32'h{{ "%08X"|format(pair[1]) }};
                    localparam logic [31:0] OVR_TYPE_{{ "%03X"|format(reg) }} = 32'h11111111; // Default RW
                {%- endif -%}
            {%- endif -%}
        {%- else -%}
        // No overlays defined (empty list)
        {%- endfor -%}
    {%- endif -%}
    
    // Legacy ROM-based lookup functions (used when sparse mapping is disabled)
    localparam bit [31:0] INVALID_INDEX = 32'hFFFFFFFF;
    
    function automatic logic [31:0] get_overlay_index(input logic [9:0] reg_num);
        if (ENABLE_SPARSE_MAP) begin
            // Use hash-based lookup
            logic [7:0] hash_idx = find_hash_entry(reg_num);
            return (hash_idx == 8'hFF) ? INVALID_INDEX : {24'h0, hash_idx};
        end else begin
            // Legacy direct lookup
            logic [31:0] result = INVALID_INDEX;
            {%- if OVERLAY_MAP is defined and OVERLAY_MAP is not none -%}
                {%- if OVERLAY_MAP is mapping -%}
                    {%- for reg, entry in OVERLAY_MAP.items() %}
            if (reg_num == {{ reg }}) result = {{ loop.index0 }};
                    {%- endfor -%}
                {%- elif OVERLAY_MAP is sequence -%}
                    {%- for pair in OVERLAY_MAP -%}
                        {%- if pair is sequence and pair|length >= 2 -%}
                            {%- set reg = pair[0] -%}
            if (reg_num == {{ reg }}) result = {{ loop.index0 }};
                        {%- endif -%}
                    {%- endfor -%}
                {%- endif -%}
            {%- endif -%}
            return result;
        end
    endfunction
    
    function automatic logic [31:0] get_overlay_mask(input logic [9:0] reg_num);
        logic [31:0] result = 32'h00000000;
        {%- if OVERLAY_MAP is defined and OVERLAY_MAP is not none -%}
            {%- if OVERLAY_MAP is mapping -%}
                {%- for reg, entry in OVERLAY_MAP.items() %}
        if (reg_num == {{ reg }}) result = OVR_MASK_{{ "%03X"|format(reg) }};
                {%- endfor -%}
            {%- elif OVERLAY_MAP is sequence -%}
                {%- for pair in OVERLAY_MAP -%}
                    {%- if pair is sequence and pair|length >= 2 -%}
                        {%- set reg = pair[0] -%}
        if (reg_num == {{ reg }}) result = OVR_MASK_{{ "%03X"|format(reg) }};
                    {%- endif -%}
                {%- endfor -%}
            {%- endif -%}
        {%- endif -%}
        return result;
    endfunction
    
    function automatic logic [31:0] get_overlay_type(input logic [9:0] reg_num);
        logic [31:0] result = DEFAULT_RW_TYPE; // Default all RW
        {%- if OVERLAY_MAP is defined and OVERLAY_MAP is not none -%}
            {%- if OVERLAY_MAP is mapping -%}
                {%- for reg, entry in OVERLAY_MAP.items() %}
        if (reg_num == {{ reg }}) result = OVR_TYPE_{{ "%03X"|format(reg) }};
                {%- endfor -%}
            {%- elif OVERLAY_MAP is sequence -%}
                {%- for pair in OVERLAY_MAP -%}
                    {%- if pair is sequence and pair|length >= 2 -%}
                        {%- set reg = pair[0] -%}
        if (reg_num == {{ reg }}) result = OVR_TYPE_{{ "%03X"|format(reg) }};
                    {%- endif -%}
                {%- endfor -%}
            {%- endif -%}
        {%- endif -%}
        return result;
    endfunction
    
    // Helper: check if overlay index is valid for a given register number
    function automatic logic has_valid_overlay(input logic [9:0] reg_num);
        logic [31:0] idx = get_overlay_index(reg_num);
        return (idx != INVALID_INDEX) && (idx < OVERLAY_ENTRIES);
    endfunction
    
    // Function to determine if shadow should take over from hardware
    function logic use_shadow_cfg(input logic [11:0] byte_addr);
        // Shadow takes over for extended configuration space based on pointer values
        if (byte_addr >= EXT_CFG_CAP_PTR) begin
            return 1'b1;  // Use shadow for all extended config space
        end else if (byte_addr >= EXT_CFG_XP_CAP_PTR && byte_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
    
    // =====================================================================
    // State Machine with Enhanced Bit Type Support
    // =====================================================================
    
    // PCIe configuration access state machine
    typedef enum logic [2:0] {
        CFG_IDLE,
        CFG_READ,
        CFG_READ_DATA,      // New state for pipelined read
        CFG_WRITE,
        CFG_COMPLETE
    } cfg_state_t;
    
    cfg_state_t cfg_state;
    logic [9:0] current_reg_num;
    logic [31:0] read_data;
    logic read_data_valid;
    logic shadow_handled_reg;
    
    // Registered version of cfgtlp_zero to avoid race condition
    logic cfgtlp_zero_reg;
    
    // Pipeline registers for BRAM reads
    logic [31:0] config_ram_readdata;
    logic [31:0] overlay_data_pipe;
    logic [31:0] overlay_mask_pipe;
    logic [9:0]  read_reg_num_pipe;
    logic        read_in_progress;
    
    // Configuration access state machine with bit type support
    always_ff @(posedge clk or negedge reset_n) begin
        // Declarations at top of block for tool compatibility
        logic [9:0]   effective_reg_num;
        logic [11:0]  byte_addr;
        logic [31:0]  ov_idx;
        logic [31:0]  overlay_idx;
        logic [31:0]  ov_data;
    logic [31:0]  ov_mask;
    logic [31:0]  new_data;
    logic [31:0]  current_mask;
    logic [31:0]  current_type_mask;
    logic [31:0]  current_data_field;
    logic [3:0]   byte_type;
    logic [7:0]   write_byte;
    logic [7:0]   current_byte;
    logic [7:0]   mask_byte;
        if (!reset_n) begin
            cfg_state <= CFG_IDLE;
            read_data_valid <= 1'b0;
            read_data <= 32'h0;
            current_reg_num <= 10'h0;
            read_in_progress <= 1'b0;
            config_ram_readdata <= 32'h0;
            overlay_data_pipe   <= 32'h0;
            overlay_mask_pipe   <= 32'h0;
            read_reg_num_pipe <= 10'h0;
            shadow_handled_reg <= 1'b0;
            cfgtlp_zero_reg <= 1'b0;
            
            // Initialize hash table if sparse mapping enabled
            if (ENABLE_SPARSE_MAP) begin
                for (int i = 0; i < HASH_TABLE_SIZE; i++) begin
                    hash_table_valid[i] <= 1'b0;
                end
            end
        end else begin
            case (cfg_state)
                CFG_IDLE: begin
                    read_data_valid <= 1'b0;
                    shadow_handled_reg <= 1'b0;
                    
                    // Register cfgtlp_zero here to avoid race condition
                    cfgtlp_zero_reg <= cfgtlp_zero;
                    
                    if (cfg_ext_read_received) begin
                        cfg_state <= CFG_READ;
                        current_reg_num <= cfg_ext_register_number;
                        read_in_progress <= 1'b1;
                    end else if (cfg_ext_write_received) begin
                        cfg_state <= CFG_WRITE;
                        current_reg_num <= cfg_ext_register_number;
                    end
                end
                
                CFG_READ: begin
                    // Calculate effective register number
                    
                    effective_reg_num = get_effective_reg_num(current_reg_num, cfg_a7);
                    byte_addr = {effective_reg_num, 2'b00};  // Convert to byte address
                    
                    // Check if we should use shadow configuration
                    if (use_shadow_cfg(byte_addr)) begin
                        shadow_handled_reg <= 1'b1;  // Shadow is handling this request
                        
                        // Use registered cfgtlp_zero to avoid race condition
                        if (cfgtlp_zero_reg) begin
                            read_data <= 32'h00000000;
                            read_data_valid <= 1'b1;
                            cfg_state <= CFG_COMPLETE;
                        end else if (read_in_progress) begin
                            // First cycle: initiate BRAM reads
                            // Bounds check before accessing BRAM
                            if (effective_reg_num < (CONFIG_SPACE_SIZE / 4)) begin
                                config_ram_readdata <= config_space_ram[effective_reg_num];
                            end else begin
                                config_ram_readdata <= OUT_OF_RANGE_SENTINEL;  // Invalid access marker
                            end
                            
                            overlay_data_pipe <= 32'h0;
                            overlay_mask_pipe <= 32'h0;

                            ov_idx = get_overlay_index(effective_reg_num);
                            if (has_valid_overlay(effective_reg_num)) begin
                                overlay_idx = ov_idx;

                                if (ENABLE_BIT_TYPES) begin
                                    // Read discrete enhanced entry fields
                                    overlay_data_pipe <= gen_enhanced_overlay.overlay_data_ram[overlay_idx];
                                    overlay_mask_pipe <= gen_enhanced_overlay.overlay_mask_ram[overlay_idx];
                                end else begin
                                    // Legacy 32-bit read with mask from static map
                                    overlay_data_pipe <= gen_legacy_overlay.overlay_data_ram[overlay_idx];
                                    overlay_mask_pipe <= get_overlay_mask(effective_reg_num);
                                end
                            end
                            
                            read_reg_num_pipe <= effective_reg_num;
                            read_in_progress <= 1'b0;
                            cfg_state <= CFG_READ_DATA;
                        end
                    end else begin
                        // For addresses below the pointer, let hardware handle it
                        shadow_handled_reg <= 1'b0;  // Shadow not handling this
                        read_data_valid <= 1'b0;  // Indicate shadow doesn't handle this
                        cfg_state <= CFG_COMPLETE;
                    end
                end
                
                CFG_READ_DATA: begin

                    // Second cycle: use pipelined BRAM data
                    if (has_valid_overlay(read_reg_num_pipe)) begin
                        ov_data = overlay_data_pipe;
                        ov_mask = overlay_mask_pipe;

                        // Apply overlay for writable bits using pipelined data
                        read_data <= (config_ram_readdata & ~ov_mask) | (ov_data & ov_mask);
                    end else begin
                        read_data <= config_ram_readdata;
                    end

                    read_data_valid <= 1'b1;
                    cfg_state <= CFG_COMPLETE;
                end
                
                CFG_WRITE: begin
                    // Use pre-declared block variables for tool compatibility

                    // Calculate effective register number
                    effective_reg_num = get_effective_reg_num(current_reg_num, cfg_a7);
                    byte_addr = {effective_reg_num, 2'b00};  // Convert to byte address

                    // Check if we should use shadow configuration and if PCIe writes are enabled
                    if (use_shadow_cfg(byte_addr) && cfgtlp_pcie_write_en) begin
                        shadow_handled_reg <= 1'b1;  // Shadow is handling this request

                        // Handle write to configuration space shadow
                            if (has_valid_overlay(effective_reg_num)) begin
                                // Get overlay info
                                ov_idx = get_overlay_index(effective_reg_num);

                                overlay_idx = ov_idx;

                                current_data_field = 32'h0;
                                current_mask       = 32'h0;
                                current_type_mask  = DEFAULT_RW_TYPE;

                                if (ENABLE_BIT_TYPES) begin
                                    current_data_field = gen_enhanced_overlay.overlay_data_ram[overlay_idx];
                                    current_mask       = gen_enhanced_overlay.overlay_mask_ram[overlay_idx];
                                    current_type_mask  = gen_enhanced_overlay.overlay_type_ram[overlay_idx];
                                end else begin
                                    current_data_field = gen_legacy_overlay.overlay_data_ram[overlay_idx];
                                    current_mask       = get_overlay_mask(effective_reg_num);
                                    current_type_mask  = get_overlay_type(effective_reg_num);
                                end

                                new_data = current_data_field;

                                // Process each byte based on its type
                                for (int byte_idx = 0; byte_idx < 4; byte_idx++) begin
                                    if (cfg_ext_write_byte_enable[byte_idx]) begin
                                        byte_type   = current_type_mask[byte_idx*4 +: 4];
                                        write_byte  = cfg_ext_write_data[byte_idx*8 +: 8];
                                        current_byte = current_data_field[byte_idx*8 +: 8];
                                        mask_byte    = current_mask[byte_idx*8 +: 8];

                                        case (byte_type)
                                            BIT_TYPE_RW: begin
                                                // Standard read-write
                                                new_data[byte_idx*8 +: 8] =
                                                    (write_byte & mask_byte) |
                                                    (current_byte & ~mask_byte);
                                            end

                                            BIT_TYPE_RW1C: begin
                                                // Write-1-to-clear
                                                new_data[byte_idx*8 +: 8] =
                                                    current_byte & ~(write_byte & mask_byte);
                                            end

                                            BIT_TYPE_RW1S: begin
                                                // Write-1-to-set
                                                new_data[byte_idx*8 +: 8] =
                                                    current_byte | (write_byte & mask_byte);
                                            end

                                            BIT_TYPE_RSVDP: begin
                                                // Reserved, preserve current value
                                                new_data[byte_idx*8 +: 8] = current_byte;
                                            end

                                            BIT_TYPE_HWINIT,
                                            BIT_TYPE_RO: begin
                                                // Hardware controlled or read-only, ignore writes
                                                new_data[byte_idx*8 +: 8] = current_byte;
                                            end

                                            default: begin
                                                // Undefined type - preserve value
                                                new_data[byte_idx*8 +: 8] = current_byte;
                                            end
                                        endcase
                                    end
                                end

                                // Update overlay entry
                                if (ENABLE_BIT_TYPES) begin
                                    gen_enhanced_overlay.overlay_data_ram[overlay_idx] <= new_data;
                                end else begin
                                    gen_legacy_overlay.overlay_data_ram[overlay_idx] <= new_data;
                                end
                            end
                        // If no valid overlay, ignore write (hardware handles it)
                    end else begin
                        // If not using shadow or PCIe writes disabled, hardware handles the write
                        shadow_handled_reg <= 1'b0;  // Shadow not handling this
                    end

                    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;
    assign shadow_handled = shadow_handled_reg;
    
    // Host access port (Port B) - synchronous read/write
    always_ff @(posedge clk) begin
        // Declarations at the top of the block
        logic [9:0] reg_num;
        logic [31:0] base_rd;
        logic [31:0] ov_mask;
        logic [31:0] ov_idx;
        logic [31:0] ov_data;
        logic [31:0] overlay_idx_cast;

        if (!reset_n) begin
            host_read_data <= 32'h0;
        end else if (host_access_en) begin

            reg_num = host_addr[11:2];  // Convert byte address to DWORD index
            
            // Bounds checking to prevent out-of-range access
            if (reg_num < (CONFIG_SPACE_SIZE / 4)) begin
                if (host_write_en) begin
                    // Host write to configuration space
                    config_space_ram[reg_num] <= host_write_data;
                end else begin
                    // Host read from configuration space
                    base_rd = config_space_ram[reg_num];
                    
                    // Get overlay info
                    ov_idx = get_overlay_index(reg_num);

                    if ((ov_idx != INVALID_INDEX) && (ov_idx < OVERLAY_ENTRIES)) begin
                        overlay_idx_cast = ov_idx;
                        if (ENABLE_BIT_TYPES) begin
                            ov_data = gen_enhanced_overlay.overlay_data_ram[overlay_idx_cast];
                            ov_mask = gen_enhanced_overlay.overlay_mask_ram[overlay_idx_cast];
                        end else begin
                            ov_data = gen_legacy_overlay.overlay_data_ram[overlay_idx_cast];
                            ov_mask = get_overlay_mask(reg_num);
                        end
                        host_read_data <= (base_rd & ~ov_mask) | (ov_data & ov_mask);
                    end else begin
                        host_read_data <= base_rd;
                    end
                end
            end else begin
                // Out of bounds access
                host_read_data <= OUT_OF_RANGE_SENTINEL;  // Return error marker for invalid access
            end
        end
    end
    
{% if DUAL_PORT %}
    // Additional dual-port access (Port C)
    always_ff @(posedge clkB) begin
        // Declarations at the top of the block
        logic [9:0] reg_num_B;
        logic [31:0] base_rd_B;
        logic [31:0] ov_mask_B;
        logic [31:0] ov_idx_B;
        logic [31:0] ov_data_B;
    logic [31:0] overlay_idx_B_cast;

        if (enB) begin
            reg_num_B = addrB[11:2];  // Convert byte address to DWORD index
            
            // Bounds checking
            if (reg_num_B < (CONFIG_SPACE_SIZE / 4)) begin
                if (weB) begin
                    // Port B write to configuration space
                    config_space_ram[reg_num_B] <= dinB;
                end else begin
                    // Port B read from configuration space
                    base_rd_B = config_space_ram[reg_num_B];
                    
                    // Get overlay info
                    ov_idx_B = get_overlay_index(reg_num_B);

                    if ((ov_idx_B != INVALID_INDEX) && (ov_idx_B < OVERLAY_ENTRIES)) begin
                        overlay_idx_B_cast = ov_idx_B;
                        if (ENABLE_BIT_TYPES) begin
                            ov_data_B = gen_enhanced_overlay.overlay_data_ram[overlay_idx_B_cast];
                            ov_mask_B = gen_enhanced_overlay.overlay_mask_ram[overlay_idx_B_cast];
                        end else begin
                            ov_data_B = gen_legacy_overlay.overlay_data_ram[overlay_idx_B_cast];
                            ov_mask_B = get_overlay_mask(reg_num_B);
                        end
                        doutB <= (base_rd_B & ~ov_mask_B) | (ov_data_B & ov_mask_B);
                    end else begin
                        doutB <= base_rd_B;
                    end
                end
            end else begin
                // Out of bounds access
                doutB <= OUT_OF_RANGE_SENTINEL;  // Return error marker
            end
        end
    end
    
{% endif %}
    // =====================================================================
    // Initialization and Hash Table Setup
    // =====================================================================
    
    // synthesis translate_off
    initial begin
        // Initialize overlay RAM (moved from reset block to enable BRAM synthesis)
        if (ENABLE_BIT_TYPES) begin
            for (int i = 0; i < OVERLAY_ENTRIES; i++) begin
                gen_enhanced_overlay.overlay_data_ram[i] = 32'h0;
                gen_enhanced_overlay.overlay_mask_ram[i] = 32'h0;
                gen_enhanced_overlay.overlay_type_ram[i] = DEFAULT_RW_TYPE;
            end
        end else begin
            for (int i = 0; i < OVERLAY_ENTRIES; i++) begin
                gen_legacy_overlay.overlay_data_ram[i] = 32'h0;
            end
        end
        
        // Initialize hash table for sparse mapping
        if (ENABLE_SPARSE_MAP) begin
            for (int i = 0; i < HASH_TABLE_SIZE; i++) begin
                hash_table_valid[i] = 1'b0;
                hash_table_reg_num[i] = 10'h0;
                hash_table_overlay_index[i] = 8'h0;
            end
            
            // Populate hash table with configured overlays
            {%- if OVERLAY_MAP is defined and OVERLAY_MAP is not none -%}
                {%- if OVERLAY_MAP is mapping -%}
                    {%- for reg, entry in OVERLAY_MAP.items() %}
            // Add register 0x{{ "%03X"|format(reg) }} to hash table
            begin
                logic [7:0] hash_idx = hash_reg_num({{ reg }});
                logic [7:0] probe_count = 0;
                // Find empty slot using linear probing
                while (hash_table_valid[hash_idx] && probe_count < HASH_TABLE_SIZE) begin
                    hash_idx = (hash_idx + 1) & (HASH_TABLE_SIZE-1);
                    probe_count++;
                end
                if (!hash_table_valid[hash_idx]) begin
                    hash_table_valid[hash_idx] = 1'b1;
                    hash_table_reg_num[hash_idx] = {{ reg }};
                    hash_table_overlay_index[hash_idx] = {{ loop.index0 }};
                end
            end
                    {%- endfor -%}
                {%- elif OVERLAY_MAP is sequence -%}
                    {%- for pair in OVERLAY_MAP -%}
                        {%- if pair is sequence and pair|length >= 2 -%}
                            {%- set reg = pair[0] -%}
            // Add register 0x{{ "%03X"|format(reg) }} to hash table
            begin
                logic [7:0] hash_idx = hash_reg_num({{ reg }});
                logic [7:0] probe_count = 0;
                // Find empty slot using linear probing
                while (hash_table_valid[hash_idx] && probe_count < HASH_TABLE_SIZE) begin
                    hash_idx = (hash_idx + 1) & (HASH_TABLE_SIZE-1);
                    probe_count++;
                end
                if (!hash_table_valid[hash_idx]) begin
                    hash_table_valid[hash_idx] = 1'b1;
                    hash_table_reg_num[hash_idx] = {{ reg }};
                    hash_table_overlay_index[hash_idx] = {{ loop.index0 }};
                end
            end
                        {%- endif -%}
                    {%- endfor -%}
                {%- endif -%}
            {%- endif %}
        end
        
        // Initialize configuration space BRAM if hex file provided
        if (CFG_INIT_HEX != "") begin
            $readmemh(CFG_INIT_HEX, config_space_ram);
        end
    end
    // synthesis translate_on

endmodule

`default_nettype wire