//==============================================================================
// Data Buffer Region Handler
// Generated by PCILeech Firmware Generator
//
// This module handles the data buffer region (0x0200-0x03FF):
// - High-speed data buffer access for PCILeech operations
// - DMA data staging and buffering
// - Burst transfer support and optimization
// - Buffer management and overflow protection
// - Dual-port memory for concurrent access patterns
//==============================================================================

`include "../tlp_pkg.svh"
`include "../bar_layout_pkg.svh"

module region_data_buffer
    import tlp_pkg::*;
    import bar_layout_pkg::*;
#(
    parameter bit ENABLE_DUAL_PORT = 1'b1,
    parameter bit ENABLE_BURST_OPTIMIZATION = 1'b1,
    parameter int BURST_SIZE = 16  // Number of 32-bit words per burst
) (
    // Clock and Reset
    input  logic                clk,
    input  logic                reset_n,
    
    // Request Interface from BAR Dispatcher
    input  logic                req_valid,
    input  logic [31:0]         req_addr,
    input  logic [31:0]         req_data,
    input  logic [3:0]          req_be,
    input  logic                req_write,
    input  tlp_header_t         req_header,
    output logic                req_ready,
    
    // Response Interface to BAR Dispatcher
    output logic                resp_valid,
    output logic [31:0]         resp_data,
    output logic                resp_error,
    input  logic                resp_ready,
    
    // DMA Interface (Port B for concurrent access)
    input  logic                dma_buffer_req,
    input  logic [31:0]         dma_buffer_addr,
    input  logic [31:0]         dma_buffer_wdata,
    input  logic [3:0]          dma_buffer_be,
    input  logic                dma_buffer_write,
    output logic [31:0]         dma_buffer_rdata,
    output logic                dma_buffer_ready,
    output logic                dma_buffer_valid,
    
    // Buffer Status and Control
    input  logic                buffer_flush,
    input  logic                buffer_clear,
    output logic [31:0]         buffer_fill_level,
    output logic                buffer_full,
    output logic                buffer_empty,
    output logic                buffer_overflow,
    output logic                buffer_underflow,
    
    // Performance and Debug
    output logic [31:0]         buffer_read_count,
    output logic [31:0]         buffer_write_count,
    output logic [31:0]         buffer_burst_count,
    output logic [7:0]          buffer_state
);

    // ========================================================================
    // Local Parameters and Types
    // ========================================================================
    
    // Buffer memory parameters
    localparam int BUFFER_WORDS = DATA_BUFFER_SIZE / 4;  // 128 words (512 bytes)
    localparam int BUFFER_ADDR_WIDTH = $clog2(BUFFER_WORDS);
    
    // State machine for buffer management
    typedef enum logic [2:0] {
        BUFFER_IDLE,
        BUFFER_READ,
        BUFFER_WRITE,
        BUFFER_BURST_READ,
        BUFFER_BURST_WRITE,
        BUFFER_RESPOND,
        BUFFER_ERROR
    } buffer_state_t;
    
    // Burst control
    typedef struct packed {
        logic                   active;
        logic [4:0]             count;
        logic [BUFFER_ADDR_WIDTH-1:0] start_addr;
        logic                   is_write;
    } burst_control_t;

    // ========================================================================
    // Internal Signals and Storage
    // ========================================================================
    
    // State machine
    buffer_state_t          current_state, next_state;
    
    // Dual-port block RAM
    (* ram_style = "block" *)
    logic [31:0]            buffer_memory [0:BUFFER_WORDS-1];
    
    // Port A (TLP interface)
    logic [BUFFER_ADDR_WIDTH-1:0] porta_addr;
    logic [31:0]            porta_wdata;
    logic [3:0]             porta_be;
    logic                   porta_write;
    logic [31:0]            porta_rdata;
    
    // Port B (DMA interface)
    logic [BUFFER_ADDR_WIDTH-1:0] portb_addr;
    logic [31:0]            portb_wdata;
    logic [3:0]             portb_be;
    logic                   portb_write;
    logic [31:0]            portb_rdata;
    
    // Buffer management
    logic [BUFFER_ADDR_WIDTH:0] write_pointer;  // Extra bit for full/empty detection
    logic [BUFFER_ADDR_WIDTH:0] read_pointer;
    logic [BUFFER_ADDR_WIDTH:0] fill_level;
    logic                   buffer_full_int;
    logic                   buffer_empty_int;
    logic                   buffer_overflow_int;
    logic                   buffer_underflow_int;
    
    // Request processing
    logic [31:0]            response_data;
    logic                   response_error;
    logic [BUFFER_ADDR_WIDTH-1:0] request_offset;
    logic                   valid_buffer_access;
    
    // Burst control
    burst_control_t         burst_ctrl;
    
    // Performance counters
    logic [31:0]            read_count_reg;
    logic [31:0]            write_count_reg;
    logic [31:0]            burst_count_reg;

    // ========================================================================
    // Address Decoding
    // ========================================================================
    
    always_comb begin
        request_offset = req_addr[BUFFER_ADDR_WIDTH+1:2];  // Word-aligned
        valid_buffer_access = (req_addr[31:BUFFER_ADDR_WIDTH+2] == '0) && 
                             (request_offset < BUFFER_WORDS);
    end

    // ========================================================================
    // State Machine
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= BUFFER_IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    always_comb begin
        next_state = current_state;
        req_ready = 1'b0;
        
        unique case (current_state)
            BUFFER_IDLE: begin
                req_ready = 1'b1;
                if (req_valid) begin
                    if (!valid_buffer_access) begin
                        next_state = BUFFER_ERROR;
                    end else if (ENABLE_BURST_OPTIMIZATION && 
                                req_header.length >= BURST_SIZE && 
                                (request_offset + req_header.length) <= BUFFER_WORDS) begin
                        // Burst operation
                        next_state = req_write ? BUFFER_BURST_WRITE : BUFFER_BURST_READ;
                    end else begin
                        // Single operation
                        next_state = req_write ? BUFFER_WRITE : BUFFER_READ;
                    end
                end
            end
            
            BUFFER_READ: begin
                next_state = BUFFER_RESPOND;
            end
            
            BUFFER_WRITE: begin
                next_state = BUFFER_RESPOND;
            end
            
            BUFFER_BURST_READ: begin
                if (burst_ctrl.count >= req_header.length - 1) begin
                    next_state = BUFFER_RESPOND;
                end
                // Stay in burst state for multiple cycles
            end
            
            BUFFER_BURST_WRITE: begin
                if (burst_ctrl.count >= req_header.length - 1) begin
                    next_state = BUFFER_RESPOND;
                end
                // Stay in burst state for multiple cycles
            end
            
            BUFFER_RESPOND: begin
                if (resp_ready) begin
                    next_state = BUFFER_IDLE;
                end
            end
            
            BUFFER_ERROR: begin
                if (resp_ready) begin
                    next_state = BUFFER_IDLE;
                end
            end
        endcase
    end

    // ========================================================================
    // Dual-Port Block RAM Implementation
    // ========================================================================
    
    // Port A (TLP interface) - with byte enables
    always_ff @(posedge clk) begin
        if (porta_write) begin
            if (porta_be[0]) buffer_memory[porta_addr][7:0] <= porta_wdata[7:0];
            if (porta_be[1]) buffer_memory[porta_addr][15:8] <= porta_wdata[15:8];
            if (porta_be[2]) buffer_memory[porta_addr][23:16] <= porta_wdata[23:16];
            if (porta_be[3]) buffer_memory[porta_addr][31:24] <= porta_wdata[31:24];
        end
        porta_rdata <= buffer_memory[porta_addr];
    end
    
    // Port B (DMA interface) - with byte enables
    generate
        if (ENABLE_DUAL_PORT) begin : gen_dual_port
            always_ff @(posedge clk) begin
                if (portb_write) begin
                    if (portb_be[0]) buffer_memory[portb_addr][7:0] <= portb_wdata[7:0];
                    if (portb_be[1]) buffer_memory[portb_addr][15:8] <= portb_wdata[15:8];
                    if (portb_be[2]) buffer_memory[portb_addr][23:16] <= portb_wdata[23:16];
                    if (portb_be[3]) buffer_memory[portb_addr][31:24] <= portb_wdata[31:24];
                end
                portb_rdata <= buffer_memory[portb_addr];
            end
        end else begin : gen_single_port
            // Single port mode - DMA access has lower priority
            assign portb_rdata = '0;
        end
    endgenerate

    // ========================================================================
    // Port A Control (TLP Interface)
    // ========================================================================
    
    always_comb begin
        porta_addr = request_offset;
        porta_wdata = req_data;
        porta_be = req_be;
        porta_write = 1'b0;
        
        response_data = porta_rdata;
        response_error = !valid_buffer_access;
        
        // Control port A based on state
        unique case (current_state)
            BUFFER_WRITE: begin
                porta_write = 1'b1;
                response_data = 32'h00000000;  // No read data for writes
            end
            BUFFER_BURST_WRITE: begin
                porta_addr = burst_ctrl.start_addr + burst_ctrl.count;
                porta_write = 1'b1;
                response_data = 32'h00000000;
            end
            BUFFER_READ: begin
                // Read data available in porta_rdata
            end
            BUFFER_BURST_READ: begin
                porta_addr = burst_ctrl.start_addr + burst_ctrl.count;
                // Burst read data handling would need additional logic for streaming
            end
            BUFFER_ERROR: begin
                response_error = 1'b1;
                response_data = 32'hDEADBEEF;
            end
            default: begin
                // Default values already set
            end
        endcase
    end

    // ========================================================================
    // Port B Control (DMA Interface)
    // ========================================================================
    
    generate
        if (ENABLE_DUAL_PORT) begin : gen_portb_control
            always_comb begin
                portb_addr = dma_buffer_addr[BUFFER_ADDR_WIDTH+1:2];
                portb_wdata = dma_buffer_wdata;
                portb_be = dma_buffer_be;
                portb_write = dma_buffer_req && dma_buffer_write;
                
                dma_buffer_rdata = portb_rdata;
                dma_buffer_ready = 1'b1;  // Always ready in this implementation
                dma_buffer_valid = dma_buffer_req && !dma_buffer_write;
            end
        end else begin : gen_portb_disabled
            assign dma_buffer_rdata = '0;
            assign dma_buffer_ready = 1'b0;
            assign dma_buffer_valid = 1'b0;
        end
    endgenerate

    // ========================================================================
    // Burst Control Logic
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            burst_ctrl <= '0;
        end else begin
            unique case (current_state)
                BUFFER_IDLE: begin
                    if (req_valid && ENABLE_BURST_OPTIMIZATION && 
                        req_header.length >= BURST_SIZE) begin
                        burst_ctrl.active <= 1'b1;
                        burst_ctrl.count <= '0;
                        burst_ctrl.start_addr <= request_offset;
                        burst_ctrl.is_write <= req_write;
                    end else begin
                        burst_ctrl.active <= 1'b0;
                    end
                end
                BUFFER_BURST_READ, BUFFER_BURST_WRITE: begin
                    if (burst_ctrl.count < req_header.length - 1) begin
                        burst_ctrl.count <= burst_ctrl.count + 1;
                    end else begin
                        burst_ctrl.active <= 1'b0;
                    end
                end
                default: begin
                    burst_ctrl.active <= 1'b0;
                end
            endcase
        end
    end

    // ========================================================================
    // Buffer Management and Status
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            write_pointer <= '0;
            read_pointer <= '0;
            buffer_overflow_int <= 1'b0;
            buffer_underflow_int <= 1'b0;
        end else if (buffer_clear) begin
            write_pointer <= '0;
            read_pointer <= '0;
            buffer_overflow_int <= 1'b0;
            buffer_underflow_int <= 1'b0;
        end else begin
            // Update pointers based on access patterns
            if (current_state == BUFFER_WRITE && !buffer_full_int) begin
                write_pointer <= write_pointer + 1;
            end else if (current_state == BUFFER_WRITE && buffer_full_int) begin
                buffer_overflow_int <= 1'b1;
            end
            
            if (current_state == BUFFER_READ && !buffer_empty_int) begin
                read_pointer <= read_pointer + 1;
            end else if (current_state == BUFFER_READ && buffer_empty_int) begin
                buffer_underflow_int <= 1'b1;
            end
            
            // Handle burst operations
            if (current_state == BUFFER_BURST_WRITE) begin
                if (!buffer_full_int) begin
                    write_pointer <= write_pointer + 1;
                end else begin
                    buffer_overflow_int <= 1'b1;
                end
            end
            
            if (current_state == BUFFER_BURST_READ) begin
                if (!buffer_empty_int) begin
                    read_pointer <= read_pointer + 1;
                end else begin
                    buffer_underflow_int <= 1'b1;
                end
            end
        end
    end
    
    // Buffer status calculations
    always_comb begin
        fill_level = write_pointer - read_pointer;
        buffer_full_int = (fill_level == BUFFER_WORDS);
        buffer_empty_int = (fill_level == 0);
    end

    // ========================================================================
    // Performance Counters
    // ========================================================================
    
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_count_reg <= '0;
            write_count_reg <= '0;
            burst_count_reg <= '0;
        end else begin
            if (current_state == BUFFER_READ) begin
                read_count_reg <= read_count_reg + 1;
            end
            
            if (current_state == BUFFER_WRITE) begin
                write_count_reg <= write_count_reg + 1;
            end
            
            if ((current_state == BUFFER_BURST_READ || current_state == BUFFER_BURST_WRITE) &&
                burst_ctrl.count == 0) begin
                burst_count_reg <= burst_count_reg + 1;
            end
        end
    end

    // ========================================================================
    // Memory Initialization for Simulation
    // ========================================================================
    
`ifdef SIMULATION
    initial begin
        for (int i = 0; i < BUFFER_WORDS; i++) begin
            buffer_memory[i] = 32'h00000000;
        end
    end
`endif

    // ========================================================================
    // Output Assignments
    // ========================================================================
    
    assign resp_valid = (current_state == BUFFER_RESPOND) || (current_state == BUFFER_ERROR);
    assign resp_data = response_data;
    assign resp_error = response_error;
    
    assign buffer_fill_level = {16'h0000, fill_level};
    assign buffer_full = buffer_full_int;
    assign buffer_empty = buffer_empty_int;
    assign buffer_overflow = buffer_overflow_int;
    assign buffer_underflow = buffer_underflow_int;
    
    assign buffer_read_count = read_count_reg;
    assign buffer_write_count = write_count_reg;
    assign buffer_burst_count = burst_count_reg;
    assign buffer_state = {5'b00000, current_state};

    // ========================================================================
    // Simulation Support
    // ========================================================================
    
`ifdef SIMULATION
    // Buffer access logging
    always @(posedge clk) begin
        if (current_state == BUFFER_WRITE) begin
            $display("[Data Buffer] WRITE to addr 0x%03X, data=0x%08X at time %0t",
                     porta_addr, porta_wdata, $time);
        end
        if (current_state == BUFFER_READ) begin
            $display("[Data Buffer] READ from addr 0x%03X, data=0x%08X at time %0t",
                     porta_addr, porta_rdata, $time);
        end
        if (current_state == BUFFER_BURST_WRITE && burst_ctrl.count == 0) begin
            $display("[Data Buffer] BURST WRITE started, length=%0d at time %0t",
                     req_header.length, $time);
        end
        if (current_state == BUFFER_BURST_READ && burst_ctrl.count == 0) begin
            $display("[Data Buffer] BURST READ started, length=%0d at time %0t",
                     req_header.length, $time);
        end
    end
    
    // Buffer status monitoring
    always @(posedge clk) begin
        if (buffer_overflow_int) begin
            $display("[Data Buffer] OVERFLOW detected at time %0t", $time);
        end
        if (buffer_underflow_int) begin
            $display("[Data Buffer] UNDERFLOW detected at time %0t", $time);
        end
    end
`endif

    // ========================================================================
    // Assertions for Design Verification
    // ========================================================================
    
`ifdef SIMULATION
    // Check buffer bounds
    property buffer_bounds_check;
        @(posedge clk) disable iff (!reset_n)
        req_valid |-> (request_offset < BUFFER_WORDS);
    endproperty
    assert property (buffer_bounds_check) else 
        $error("Data Buffer: Access beyond buffer bounds");
    
    // Check dual-port conflict
    generate
        if (ENABLE_DUAL_PORT) begin : gen_conflict_check
            property no_dual_port_conflict;
                @(posedge clk) disable iff (!reset_n)
                (porta_write && portb_write) |-> (porta_addr != portb_addr);
            endproperty
            assert property (no_dual_port_conflict) else 
                $error("Data Buffer: Dual-port write conflict detected");
        end
    endgenerate
    
    // Check response timing
    property response_timing;
        @(posedge clk) disable iff (!reset_n)
        (current_state == BUFFER_RESPOND || current_state == BUFFER_ERROR) |-> resp_valid;
    endproperty
    assert property (response_timing) else 
        $error("Data Buffer: Response not valid when expected");
`endif

endmodule