{#- Advanced SystemVerilog Module Template -#}
{% from "_helpers.j2" import get_vendor_id, get_device_id %}
{{ header | safe }}

`default_nettype none

{#- Handle device_type: can be string, dict, or object with .value -#}
{%- if device_config.device_type is string -%}
    {%- set device_type = device_config.device_type -%}
{%- elif device_config.device_type is mapping -%}
    {%- set device_type = device_config.device_type.get('value', device_config.device_type.get('name', 'generic')) -%}
{%- elif device_config.device_type is defined and device_config.device_type.value is defined -%}
    {%- set device_type = device_config.device_type.value -%}
{%- else -%}
    {%- set device_type = 'generic' -%}
{%- endif -%}

{#- Handle device_class: can be string, dict, or object with .value -#}
{%- if device_config.device_class is string -%}
    {%- set device_class = device_config.device_class -%}
{%- elif device_config.device_class is mapping -%}
    {%- set device_class = device_config.device_class.get('value', device_config.device_class.get('name', 'consumer')) -%}
{%- elif device_config.device_class is defined and device_config.device_class.value is defined -%}
    {%- set device_class = device_config.device_class.value -%}
{%- else -%}
    {%- set device_class = 'consumer' -%}
{%- endif -%}

module pcileech_advanced_{{ device_type }}_{{ device_class }} #(
    parameter DEVICE_TYPE = "{{ device_type }}",
    parameter DEVICE_CLASS = "{{ device_class }}",
    parameter MAX_PAYLOAD_SIZE = {{ device_config.max_payload_size }},
    parameter MSI_VECTORS = {{ device_config.msi_vectors }},
    {%- set perf_counter_width = 32 -%}
    {%- if performance_counters is defined -%}
        {%- if performance_counters.counter_width is defined -%}
            {%- set perf_counter_width = performance_counters.counter_width -%}
        {%- endif -%}
    {%- endif -%}
    {%- set enable_perf_outputs = performance_counters.enable_perf_outputs if (performance_counters is defined and performance_counters.enable_perf_outputs is defined) else false -%}
    parameter COUNTER_WIDTH = {{ perf_counter_width }}
) (
    // Clock and reset
    input  wire         clk,
    input  wire         reset_n,
    
    // Memory clock domain
    input  wire         mem_clk,
    input  wire         mem_reset_n,
    
    // Auxiliary clock domain  
    input  wire         aux_clk,
    input  wire         aux_reset_n,

    // BAR interface
    input  wire  [31:0] bar_addr,
    input  wire  [31:0] bar_wr_data,
    input  wire         bar_wr_en,
    input  wire         bar_rd_en,
    output logic [31:0] bar_rd_data,
    output logic        bar_rd_valid,

    // Configuration space interface
    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 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  wire         msix_interrupt_ack,

{%- if device_type == 'network' %}
    // Network-specific ports
    output logic [63:0] tx_data,
    output logic        tx_valid,
    input  wire         tx_ready,
    input  wire  [63:0] rx_data,
    input  wire         rx_valid,
    output logic        rx_ready,
{%- elif device_type == 'storage' %}
    // Storage-specific ports
    output logic [31:0] storage_addr,
    output logic [31:0] storage_wr_data,
    output logic        storage_wr_en,
    output logic        storage_rd_en,
    input  wire  [31:0] storage_rd_data,
    input  wire         storage_ready,
{%- elif device_type == 'graphics' %}
    // Graphics-specific ports
    output logic [23:0] pixel_data,
    output logic        pixel_valid,
    output logic        hsync,
    output logic        vsync,
    output logic        display_enable,
{%- endif %}

    // Power management interface
    // Module issues requests; external PM acknowledges readiness
    output logic        power_down_request,
    output logic        power_up_request,
    input  wire         power_ready,
    output logic [1:0]  current_power_state,

    // Debug and status
    output logic [31:0] debug_status,
    output logic        device_ready
);

{%- include 'sv/components/register_declarations.sv.j2' %}

{%- if power_management %}
{%- include 'sv/power_management.sv.j2' %}
{%- endif %}

{%- if error_handling %}
{%- include 'sv/error_handling/error_handling_complete.sv.j2' %}
{%- else %}
    // Safe fallbacks when error handling block not included
    logic [31:0] error_status = 32'h0;
    logic [1:0]  error_state = 2'b00;
    logic        error_recovery_active = 1'b0;
{%- endif %}

{%- if performance_counters is defined and performance_counters %}
{%- include 'sv/performance_counters.sv.j2' %}
{%- else %}
    // Safe fallbacks when performance counters not included
    logic [31:0] perf_counter_data = 32'h0;
    logic [3:0]  perf_counter_select = 4'h0;
    logic        performance_overflow = 1'b0;
{%- endif %}

    // Interrupt control signals (locally owned by this module)
    logic        interrupt_pending;
    logic [3:0]  interrupt_priority;

    // Register access logic
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            bar_rd_data <= 32'h0;
            bar_rd_valid <= 1'b0;
            cfg_ext_read_data <= 32'h0;
            cfg_ext_read_data_valid <= 1'b0;
            // Power management defaults
            power_down_request <= 1'b0;
            power_up_request   <= 1'b0;
            current_power_state <= 2'b00; // D0 by default
            // Default perf selector
            perf_counter_select <= 4'h0;
            // Default interrupt priority
            interrupt_priority <= 4'h0;
{%- for reg in registers %}
            {{ reg.name }}_access_pending <= 1'b0;
            {{ reg.name }}_timing_counter <= 8'h0;
{%- endfor %}
        end else begin
            // Default assignments
            bar_rd_valid <= 1'b0;
            cfg_ext_read_data_valid <= 1'b0;
            
            // BAR register access
            if (bar_rd_en) begin
                bar_rd_valid <= 1'b1;
                case (bar_addr[15:2])  // Word-aligned access
{%- for reg in registers %}
{%- set reg_addr = (reg.offset | int(base=16)) // 4 %}
                    14'h{{ "%04X" | format(reg_addr) }}: begin
                        bar_rd_data <= {{ reg.name }}_reg;
                        {{ reg.name }}_access_pending <= 1'b1;
                    end
{%- endfor %}
{%- if performance_counters is defined and performance_counters %}
                    14'h1000: bar_rd_data <= perf_counter_data;  // Performance counter data
                    14'h1001: bar_rd_data <= {28'h0, perf_counter_select};  // Performance counter select
{%- endif %}
                    14'h1FFE: bar_rd_data <= error_status;  // Error status
                    14'h1FFF: bar_rd_data <= debug_status;  // Debug status
                    default: bar_rd_data <= 32'h00000000;
                endcase
            end
            
            if (bar_wr_en) begin
                case (bar_addr[15:2])  // Word-aligned access
{%- for reg in registers %}
{%- set reg_addr = (reg.offset | int(base=16)) // 4 %}
                    14'h{{ "%04X" | format(reg_addr) }}: begin
                        {{ reg.name }}_reg <= bar_wr_data;
                        {{ reg.name }}_access_pending <= 1'b1;
                    end
{%- endfor %}
{%- if performance_counters is defined and performance_counters %}
                    14'h1001: perf_counter_select <= bar_wr_data[3:0];  // Performance counter select
{%- endif %}
                    default: ; // Ignore writes to undefined registers
                endcase
            end
            
            // Configuration space access
            if (cfg_ext_read_received) begin
                cfg_ext_read_data_valid <= 1'b1;
                case (cfg_ext_register_number)
                    10'h000: cfg_ext_read_data <= {16'h{{ get_device_id(device_config, device, config_space, device_id_int) }}, 16'h{{ get_vendor_id(device_config, device, config_space, vendor_id_int) }}};
                    10'h001: cfg_ext_read_data <= {16'h0000, 16'h0010};  // Status and Command
                    10'h002: cfg_ext_read_data <= {8'h{{ device_config.class_code[2:4] }}, 24'h{{ device_config.class_code[4:] }}};  // Class code and revision
{%- if power_management %}
                    10'h010: cfg_ext_read_data <= {30'h0, current_power_state};  // Power management
{%- endif %}
{%- if performance_counters is defined and performance_counters %}
                    10'h020: cfg_ext_read_data <= perf_counter_data;  // Performance data
{%- endif %}
                    10'h030: cfg_ext_read_data <= error_status;  // Error status
                    default: cfg_ext_read_data <= 32'h00000000;
                endcase
            end
            
            if (cfg_ext_write_received) begin
                case (cfg_ext_register_number)
                    10'h001: begin  // Command register
                        // Handle command register writes
                    end
{%- if power_management %}
                    10'h010: begin  // Power management
                        if (cfg_ext_write_data[1:0] != current_power_state) begin
                            if (cfg_ext_write_data[1:0] == 2'b01) power_down_request <= 1'b1;
                            else if (cfg_ext_write_data[1:0] == 2'b00) power_up_request <= 1'b1;
                        end
                    end
{%- endif %}
{%- if performance_counters is defined and performance_counters %}
                    10'h020: perf_counter_select <= cfg_ext_write_data[3:0];  // Performance counter select
{%- endif %}
                    default: ; // Ignore writes to read-only registers
                endcase
            end
            
            // Auto-clear power requests on acknowledge and update state
            if (power_ready) begin
                if (power_down_request) begin
                    power_down_request <= 1'b0;
                    current_power_state <= 2'b01; // D1/D3 simplified
                end else if (power_up_request) begin
                    power_up_request <= 1'b0;
                    current_power_state <= 2'b00; // D0
                end
            end

            // Update timing counters for register access simulation
{%- for reg in registers %}
            if ({{ reg.name }}_access_pending) begin
                {{ reg.name }}_timing_counter <= {{ reg.name }}_timing_counter + 1;
                if ({{ reg.name }}_timing_counter >= 8'h10) begin  // 16 cycle access time
                    {{ reg.name }}_access_pending <= 1'b0;
                    {{ reg.name }}_timing_counter <= 8'h0;
                end
            end
{%- endfor %}
        end
    end

    // Device ready and debug status
    assign device_ready = power_ready && !error_recovery_active;
    assign debug_status = {
        8'h{{ '%02x' % ((get_vendor_id(device_config, device, config_space, vendor_id_int) | safe_int(0) // 256)) }},  // Vendor ID high byte (0 if unknown)
        8'h{{ '%02x' % ((get_device_id(device_config, device, config_space, device_id_int) | safe_int(0) // 256)) }},  // Device ID high byte (0 if unknown)
        4'h0,
        current_power_state,
        error_state[1:0],
        6'h0,
        device_ready,
        power_ready
    };

    // MSI-X interrupt generation
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            msix_interrupt <= 1'b0;
            msix_vector <= 11'h0;
            interrupt_pending <= 1'b0;
        end else begin
            if (interrupt_pending && !msix_interrupt) begin
                msix_interrupt <= 1'b1;
                msix_vector <= {7'h0, interrupt_priority};
            end else if (msix_interrupt_ack) begin
                msix_interrupt <= 1'b0;
                interrupt_pending <= 1'b0;
            end
            
            // Generate interrupts based on various conditions
            if (error_recovery_active || performance_overflow) begin
                interrupt_pending <= 1'b1;
                interrupt_priority <= error_recovery_active ? 4'hF : 4'h8;
            end
        end
    end

endmodule

`default_nettype wire