//==============================================================================
// TLP Frontend - TLP Parsing and Header Extraction
// Generated by PCILeech Firmware Generator
//
// This module handles the front-end processing of incoming TLP packets:
// - TLP header parsing and validation
// - Address extraction (32-bit and 64-bit)
// - Payload data extraction
// - Error detection and reporting
// - Interface to downstream processing modules
//==============================================================================

`include "../tlp_pkg.svh"

module tlp_frontend 
    import tlp_pkg::*;
#(
    parameter int COMPLETER_ID = 16'h0000,
    parameter bit ENABLE_ERROR_CHECKING = 1'b1,
    parameter int TIMEOUT_CYCLES = DEFAULT_TIMEOUT_CYCLES
) (
    // Clock and Reset
    input  logic                clk,
    input  logic                reset_n,
    
    // TLP Input Interface (from PCIe core)
    input  logic                tlp_rx_valid,
    input  logic [127:0]        tlp_rx_data,
    input  logic                tlp_rx_sop,
    input  logic                tlp_rx_eop,
    input  logic [3:0]          tlp_rx_empty,
    input  logic                tlp_rx_err,
    output logic                tlp_rx_ready,
    
    // Parsed TLP Output Interface
    output logic                parsed_tlp_valid,
    output tlp_header_t         parsed_tlp_header,
    output logic [63:0]         parsed_tlp_address,
    output logic [127:0]        parsed_tlp_data,
    output logic                parsed_tlp_has_data,
    output logic [11:0]         parsed_tlp_byte_count,
    input  logic                parsed_tlp_ready,
    
    // Error and Status Outputs
    output logic                tlp_parse_error,
    output logic [7:0]          tlp_error_code,
    output logic [31:0]         tlp_parse_count,
    output logic [31:0]         tlp_error_count
);

    // ========================================================================
    // Local Parameters and Types
    // ========================================================================
    
    typedef enum logic [2:0] {
        PARSE_IDLE,
        PARSE_HEADER,
        PARSE_DATA,
        PARSE_COMPLETE,
        PARSE_ERROR
    } parse_state_t;

    // Error codes
    localparam logic [7:0] ERR_NONE           = 8'h00;
    localparam logic [7:0] ERR_MALFORMED_TLP  = 8'h01;
    localparam logic [7:0] ERR_UNSUPPORTED    = 8'h02;
    localparam logic [7:0] ERR_LENGTH_MISMATCH = 8'h03;
    localparam logic [7:0] ERR_TIMEOUT        = 8'h04;
    localparam logic [7:0] ERR_PARITY         = 8'h05;

    // ========================================================================
    // Internal Signals
    // ========================================================================
    
    // State machine
    parse_state_t           current_state, next_state;
    
    // TLP parsing registers
    tlp_header_t            tlp_header_reg;
    logic [127:0]           tlp_data_reg;
    logic [63:0]            tlp_address_reg;
    logic                   tlp_has_data_reg;
    logic [11:0]            tlp_byte_count_reg;
    
    // Control signals
    logic                   header_valid;
    logic                   data_valid;
    logic                   parse_complete;
    logic                   parse_error_int;
    logic [7:0]             error_code_reg;
    
    // Counters
    logic [31:0]            parse_count_reg;
    logic [31:0]            error_count_reg;
    logic [15:0]            timeout_counter;
    
    // Header parsing signals
    logic [4:0]             extracted_fmt_type;
    logic                   is_64bit_addr;
    logic                   has_payload;
    logic [9:0]             payload_length;
    
    // ========================================================================
    // TLP Header Parsing Logic
    // ========================================================================
    
    always_comb begin
        // Extract format and type from first DW
        extracted_fmt_type = tlp_rx_data[127:123];
        
        // Determine addressing mode and payload presence
        is_64bit_addr = is_64bit_address(tlp_type_t'(extracted_fmt_type));
        has_payload = is_write_request(tlp_type_t'(extracted_fmt_type));
        payload_length = tlp_rx_data[105:96];
        
        // Validate TLP format
        header_valid = tlp_rx_valid && tlp_rx_sop && 
                      (extracted_fmt_type != TLP_INVALID) &&
                      (payload_length != 0 || !has_payload);
    end

    // ========================================================================
    // State Machine
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= PARSE_IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    always_comb begin
        next_state = current_state;
        tlp_rx_ready = 1'b0;
        
        unique case (current_state)
            PARSE_IDLE: begin
                tlp_rx_ready = 1'b1;
                if (tlp_rx_valid && tlp_rx_sop) begin
                    if (header_valid) begin
                        next_state = PARSE_HEADER;
                    end else begin
                        next_state = PARSE_ERROR;
                    end
                end
            end
            
            PARSE_HEADER: begin
                tlp_rx_ready = 1'b1;
                if (has_payload && !tlp_rx_eop) begin
                    next_state = PARSE_DATA;
                end else begin
                    next_state = PARSE_COMPLETE;
                end
            end
            
            PARSE_DATA: begin
                tlp_rx_ready = 1'b1;
                if (tlp_rx_valid && tlp_rx_eop) begin
                    next_state = PARSE_COMPLETE;
                end
            end
            
            PARSE_COMPLETE: begin
                if (parsed_tlp_ready) begin
                    next_state = PARSE_IDLE;
                end
            end
            
            PARSE_ERROR: begin
                if (parsed_tlp_ready) begin
                    next_state = PARSE_IDLE;
                end
            end
        endcase
    end

    // ========================================================================
    // TLP Header Register Update
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            tlp_header_reg <= '0;
            tlp_address_reg <= '0;
            tlp_has_data_reg <= 1'b0;
            tlp_byte_count_reg <= '0;
        end else if (current_state == PARSE_HEADER && tlp_rx_valid) begin
            // Parse TLP header fields
            tlp_header_reg.fmt_type <= tlp_rx_data[127:123];
            tlp_header_reg.tc <= tlp_rx_data[122:120];
            tlp_header_reg.td <= tlp_rx_data[119];
            tlp_header_reg.ep <= tlp_rx_data[118];
            tlp_header_reg.attr <= tlp_rx_data[117:116];
            tlp_header_reg.th <= tlp_rx_data[115];
            tlp_header_reg.length <= tlp_rx_data[105:96];
            tlp_header_reg.requester_id <= tlp_rx_data[95:80];
            tlp_header_reg.tag <= tlp_rx_data[79:72];
            tlp_header_reg.first_be <= tlp_rx_data[71:68];
            tlp_header_reg.last_be <= tlp_rx_data[67:64];
            
            // Extract address based on TLP type
            if (is_64bit_addr) begin
                tlp_header_reg.address_low <= tlp_rx_data[63:32];
                tlp_header_reg.address_high <= tlp_rx_data[31:0];
                tlp_address_reg <= {tlp_rx_data[31:0], tlp_rx_data[63:34], 2'b00};
            end else begin
                tlp_header_reg.address_low <= tlp_rx_data[63:32];
                tlp_header_reg.address_high <= 32'h00000000;
                tlp_address_reg <= {32'h00000000, tlp_rx_data[63:34], 2'b00};
            end
            
            // Calculate byte count
            tlp_byte_count_reg <= calculate_byte_count(
                tlp_rx_data[105:96],
                tlp_rx_data[71:68],
                tlp_rx_data[67:64]
            );
            
            // Set data presence flag
            tlp_has_data_reg <= has_payload;
        end
    end

    // ========================================================================
    // TLP Data Register Update
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            tlp_data_reg <= '0;
        end else if (current_state == PARSE_DATA && tlp_rx_valid) begin
            tlp_data_reg <= tlp_rx_data;
        end
    end

    // ========================================================================
    // Error Detection and Handling
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            parse_error_int <= 1'b0;
            error_code_reg <= ERR_NONE;
            timeout_counter <= '0;
        end else begin
            // Clear error on new packet
            if (current_state == PARSE_IDLE && tlp_rx_valid && tlp_rx_sop) begin
                parse_error_int <= 1'b0;
                error_code_reg <= ERR_NONE;
                timeout_counter <= '0;
            end
            
            // Detect various error conditions
            if (ENABLE_ERROR_CHECKING) begin
                // Malformed TLP detection
                if (current_state == PARSE_HEADER && tlp_rx_valid) begin
                    if (extracted_fmt_type == TLP_INVALID) begin
                        parse_error_int <= 1'b1;
                        error_code_reg <= ERR_MALFORMED_TLP;
                    end else if (has_payload && payload_length == 0) begin
                        parse_error_int <= 1'b1;
                        error_code_reg <= ERR_LENGTH_MISMATCH;
                    end
                end
                
                // Input error propagation
                if (tlp_rx_err) begin
                    parse_error_int <= 1'b1;
                    error_code_reg <= ERR_PARITY;
                end
                
                // Timeout detection
                if (current_state != PARSE_IDLE && current_state != PARSE_COMPLETE) begin
                    if (timeout_counter < TIMEOUT_CYCLES-1) begin
                        timeout_counter <= timeout_counter + 1;
                    end else begin
                        parse_error_int <= 1'b1;
                        error_code_reg <= ERR_TIMEOUT;
                    end
                end
            end
            
            // Error state handling
            if (current_state == PARSE_ERROR) begin
                parse_error_int <= 1'b1;
                if (error_code_reg == ERR_NONE) begin
                    error_code_reg <= ERR_MALFORMED_TLP;
                end
            end
        end
    end

    // ========================================================================
    // Performance Counters
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            parse_count_reg <= '0;
            error_count_reg <= '0;
        end else begin
            // Count successful parses
            if (current_state == PARSE_COMPLETE && parsed_tlp_ready && !parse_error_int) begin
                parse_count_reg <= parse_count_reg + 1;
            end
            
            // Count errors
            if (current_state == PARSE_ERROR && parsed_tlp_ready) begin
                error_count_reg <= error_count_reg + 1;
            end
        end
    end

    // ========================================================================
    // Output Assignments
    // ========================================================================
    
    assign parsed_tlp_valid = (current_state == PARSE_COMPLETE) || (current_state == PARSE_ERROR);
    assign parsed_tlp_header = tlp_header_reg;
    assign parsed_tlp_address = tlp_address_reg;
    assign parsed_tlp_data = tlp_data_reg;
    assign parsed_tlp_has_data = tlp_has_data_reg && !parse_error_int;
    assign parsed_tlp_byte_count = tlp_byte_count_reg;
    
    assign tlp_parse_error = parse_error_int;
    assign tlp_error_code = error_code_reg;
    assign tlp_parse_count = parse_count_reg;
    assign tlp_error_count = error_count_reg;

    // ========================================================================
    // Assertions for Design Verification
    // ========================================================================
    
`ifdef SIMULATION
    // Check that SOP and EOP are properly aligned
    property sop_eop_alignment;
        @(posedge clk) disable iff (!reset_n)
        tlp_rx_valid && tlp_rx_sop |-> !tlp_rx_eop || tlp_rx_data[105:96] == 1;
    endproperty
    assert property (sop_eop_alignment) else 
        $error("TLP Frontend: SOP/EOP alignment violation");
    
    // Check that ready/valid handshake is maintained
    property ready_valid_handshake;
        @(posedge clk) disable iff (!reset_n)
        parsed_tlp_valid && !parsed_tlp_ready |=> parsed_tlp_valid;
    endproperty
    assert property (ready_valid_handshake) else 
        $error("TLP Frontend: Ready/Valid handshake violation");
    
    // Check that error conditions are properly flagged
    property error_detection;
        @(posedge clk) disable iff (!reset_n)
        tlp_rx_err |-> ##[1:3] tlp_parse_error;
    endproperty
    assert property (error_detection) else 
        $error("TLP Frontend: Error detection failure");
`endif

endmodule