{#- PCILeech FIFO Module Template -#}
{#- Generates PCILeech FIFO compatible with pcileech-fpga project -#}

//
// PCILeech FPGA.
//
// FIFO network / control.
//
// Generated by PCILeech Firmware Generator
// Device: {{ vendor_id | default('0x0000') }}:{{ device_id | default('0x0000') }}
//

`timescale 1ns / 1ps
`include "pcileech_header.svh"

`define ENABLE_STARTUPE2

module pcileech_fifo #(
    parameter               PARAM_DEVICE_ID = {{ device_id | default('0') }},
    parameter               PARAM_VERSION_NUMBER_MAJOR = {{ version_major | default('4') }},
    parameter               PARAM_VERSION_NUMBER_MINOR = {{ version_minor | default('14') }},
    parameter               PARAM_CUSTOM_VALUE = {{ custom_value | default('32\'hffffffff') }}
) (
    input                   clk,
    input                   rst,
    input                   rst_cfg_reload,
    
    input                   pcie_present,
    input                   pcie_perst_n,
    
    IfComToFifo.mp_fifo     dcom,
    
    IfPCIeFifoCfg.mp_fifo   dcfg,
    IfPCIeFifoTlp.mp_fifo   dtlp,
    IfPCIeFifoCore.mp_fifo  dpcie,
    IfShadow2Fifo.fifo      dshadow2fifo
    );

    // ----------------------------------------------------
    // TickCount64
    // ----------------------------------------------------
    
    time tickcount64 = 0;
    always @ ( posedge clk )
        tickcount64 <= tickcount64 + 1;
   
    // ----------------------------------------------------------------------------
    // RX FROM USB/FT601/FT245 BELOW:
    // ----------------------------------------------------------------------------
    // Incoming data received from FT601 is converted from 32-bit to 64-bit.
    // This always happen regardless whether receiving FIFOs are full or not.
    // The actual data contains a MAGIC which tells which receiving FIFO should
    // accept the data. Receiving TYPEs are: PCIe TLP, PCIe CFG, Loopback, Command.
    //
    //         /--> PCIe TLP (32-bit)
    //         |
    // FT601 --+--> PCIe CFG (64-bit)
    //         |
    //         +--> LOOPBACK (32-bit)
    //         |
    //         \--> COMMAND  (64-bit)
    //
            
    // ----------------------------------------------------------
    // Route 64-bit incoming FT601 data to correct receiver below:
    // ----------------------------------------------------------
    `define CHECK_MAGIC     (dcom.com_dout[7:0] == 8'h77)
    `define CHECK_TYPE_TLP  (dcom.com_dout[9:8] == 2'b00)
    `define CHECK_TYPE_CFG  (dcom.com_dout[9:8] == 2'b01)
    `define CHECK_TYPE_LOOP (dcom.com_dout[9:8] == 2'b10)
    `define CHECK_TYPE_CMD  (dcom.com_dout[9:8] == 2'b11)
    
    assign   dtlp.tx_valid = dcom.com_dout_valid & `CHECK_MAGIC & `CHECK_TYPE_TLP;
    assign   dcfg.tx_valid = dcom.com_dout_valid & `CHECK_MAGIC & `CHECK_TYPE_CFG;
    wire     _loop_rx_wren = dcom.com_dout_valid & `CHECK_MAGIC & `CHECK_TYPE_LOOP;
    wire     _cmd_rx_wren  = dcom.com_dout_valid & `CHECK_MAGIC & `CHECK_TYPE_CMD;
    
    // Incoming TLPs are forwarded to PCIe core logic.
    assign dtlp.tx_data = dcom.com_dout[63:32];
    assign dtlp.tx_last = dcom.com_dout[10];
    // Incoming CFGs are forwarded to PCIe core logic.
    assign dcfg.tx_data = dcom.com_dout;
    
    // ----------------------------------------------------------------------------
    // TX TO USB/FT601/FT245 BELOW:
    // ----------------------------------------------------------------------------
    //
    //                                         MULTIPLEXER
    //                                         ===========
    //                                         1st priority
    // PCIe TLP ->(32-bit) --------------------->--\
    //                                         2nd |    /-----------------------------------------\
    // PCIe CFG ->(32-bit)---------------------->--+--> | 256-> BUFFER FIFO (NATIVE OR DRAM) ->32 | -> FT601
    //                                             |    \-----------------------------------------/
    //            /--------------------------\ 3rd |
    // FT601   -> | 34->  Loopback FIFO ->34 | ->--/
    //            \--------------------------/     |
    //                                             |
    //            /--------------------------\ 4th |
    // COMMAND -> | 34->  Command FIFO  ->34 | ->--/
    //            \--------------------------/
    //
   
    // ----------------------------------------------------------
    // LOOPBACK FIFO:
    // ----------------------------------------------------------
    wire [33:0]       _loop_dout;
    wire              _loop_valid;
    wire              _loop_rd_en;
    fifo_34_34 i_fifo_loop_tx(
        .clk            ( clk                       ),
        .srst           ( rst                       ),
        .rd_en          ( _loop_rd_en               ),
        .dout           ( _loop_dout                ),
        .din            ( {dcom.com_dout[11:10], dcom.com_dout[63:32]} ),
        .wr_en          ( _loop_rx_wren             ),
        .full           (                           ),
        .almost_full    (                           ),
        .empty          (                           ),
        .valid          ( _loop_valid               )
    );
   
    // ----------------------------------------------------------
    // COMMAND FIFO:
    // ----------------------------------------------------------
    wire [33:0]       _cmd_tx_dout;
    wire              _cmd_tx_valid;
    wire              _cmd_tx_rd_en;
    wire              _cmd_tx_almost_full;
    reg               _cmd_tx_wr_en;
    reg [33:0]        _cmd_tx_din;
    fifo_34_34 i_fifo_cmd_tx(
        .clk            ( clk                       ),
        .srst           ( rst                       ),
        .rd_en          ( _cmd_tx_rd_en             ),
        .dout           ( _cmd_tx_dout              ),
        .din            ( _cmd_tx_din               ),
        .wr_en          ( _cmd_tx_wr_en             ),
        .full           (                           ),
        .almost_full    ( _cmd_tx_almost_full       ),
        .empty          (                           ),
        .valid          ( _cmd_tx_valid             )
    );

    // ----------------------------------------------------------
    // MULTIPLEXER
    // ----------------------------------------------------------
    pcileech_mux i_pcileech_mux(
        .clk            ( clk                           ),
        .rst            ( rst                           ),
        // output
        .dout           ( dcom.com_din                  ),
        .dout_valid     ( dcom.com_din_wr_en            ),
        // input 0 - TLP
        .din0_rden      ( dtlp.rx_rd_en                ),
        .din0_data      ( {dtlp.rx_data[0], dtlp.rx_data[1], dtlp.rx_data[2], dtlp.rx_data[3]} ),
        .din0_first     ( {dtlp.rx_first[0], dtlp.rx_first[1], dtlp.rx_first[2], dtlp.rx_first[3]} ),
        .din0_last      ( {dtlp.rx_last[0], dtlp.rx_last[1], dtlp.rx_last[2], dtlp.rx_last[3]} ),
        .din0_valid     ( {dtlp.rx_valid[0], dtlp.rx_valid[1], dtlp.rx_valid[2], dtlp.rx_valid[3]} ),
        // input 1 - CFG
        .din1_rden      ( dcfg.rx_rd_en                ),
        .din1_data      ( dcfg.rx_data                 ),
        .din1_valid     ( dcfg.rx_valid                ),
        // input 2 - LOOPBACK
        .din2_rden      ( _loop_rd_en                  ),
        .din2_data      ( _loop_dout[31:0]             ),
        .din2_last      ( _loop_dout[32]               ),
        .din2_valid     ( _loop_valid                  ),
        // input 3 - COMMAND
        .din3_rden      ( _cmd_tx_rd_en                ),
        .din3_data      ( _cmd_tx_dout[31:0]           ),
        .din3_last      ( _cmd_tx_dout[32]             ),
        .din3_valid     ( _cmd_tx_valid                )
    );
    
    // ----------------------------------------------------------------------------
    // COMMAND PROCESSING
    // ----------------------------------------------------------------------------
    
    // Configuration registers
    logic [255:0] rw;
    
    // Initialize configuration
    initial begin
        // Set all registers to default values
        rw = 256'h0;
        
        {% if enable_custom_config %}
        // CRITICAL: Enable custom configuration space
        // Change from 1'b1 to 1'b0 to enable custom configuration space
        rw[203] = 1'b0;  // CFGTLP ZERO DATA (0 = CUSTOM CONFIGURATION SPACE ENABLED)
        {% else %}
        // Use standard configuration space
        rw[203] = 1'b1;  // CFGTLP ZERO DATA (1 = STANDARD CONFIGURATION SPACE)
        {% endif %}
        
        // Device-specific configuration
        {% if device_specific_config %}
        {% for config_bit, value in device_specific_config.items() %}
        rw[{{ config_bit }}] = 1'b{{ value }};  // {{ config_bit }} configuration
        {% endfor %}
        {% endif %}
        
        // PCILeech specific settings
        rw[0] = 1'b1;    // Enable PCILeech functionality
        rw[1] = 1'b1;    // Enable DMA operations
        rw[2] = 1'b{{ '1' if enable_scatter_gather else '0' }};    // Scatter-gather support
        rw[3] = 1'b{{ '1' if enable_interrupt else '0' }};    // Interrupt support
    end
    
    // Command processing
    wire [63:0]       _cmd_rx_data = dcom.com_dout;
    reg  [63:0]       _cmd_tx_data;
    reg               _cmd_tx_valid;
    
    always @ ( posedge clk ) begin
        _cmd_tx_wr_en <= 0;
        _cmd_tx_valid <= 0;
        
        if ( rst ) begin
            rw <= 256'h0;
            {% if enable_custom_config %}
            rw[203] <= 1'b0;  // CFGTLP ZERO DATA (0 = CUSTOM CONFIGURATION SPACE ENABLED)
            {% else %}
            rw[203] <= 1'b1;  // CFGTLP ZERO DATA (1 = STANDARD CONFIGURATION SPACE)
            {% endif %}
        end else if ( _cmd_rx_wren ) begin
            _cmd_tx_valid <= 1;
            case ( _cmd_rx_data[63:56] )
                8'h01: begin // read status
                    _cmd_tx_data[63:48] <= 16'h0143;  // PCILeech signature
                    _cmd_tx_data[47:32] <= {8'h00, 4'h0, PARAM_VERSION_NUMBER_MAJOR};
                    _cmd_tx_data[31:16] <= {12'h000, PARAM_VERSION_NUMBER_MINOR};
                    _cmd_tx_data[15:0]  <= PARAM_DEVICE_ID;
                end
                8'h02: begin // read PCIe config
                    _cmd_tx_data <= dpcie.drp_rdy ? {16'h0, dpcie.drp_do, 32'h0} : 64'hffffffff_ffffffff;
                end
                8'h03: begin // write PCIe config
                    dpcie.drp_en <= 1;
                    dpcie.drp_we <= _cmd_rx_data[44];
                    dpcie.drp_addr <= _cmd_rx_data[40:32];
                    dpcie.drp_di <= _cmd_rx_data[15:0];
                end
                8'h04: begin // write rw
                    case ( _cmd_rx_data[55:52] )
                        4'h0: rw[31:0]   <= _cmd_rx_data[31:0];
                        4'h1: rw[63:32]  <= _cmd_rx_data[31:0];
                        4'h2: rw[95:64]  <= _cmd_rx_data[31:0];
                        4'h3: rw[127:96] <= _cmd_rx_data[31:0];
                        4'h4: rw[159:128] <= _cmd_rx_data[31:0];
                        4'h5: rw[191:160] <= _cmd_rx_data[31:0];
                        4'h6: rw[223:192] <= _cmd_rx_data[31:0];
                        4'h7: rw[255:224] <= _cmd_rx_data[31:0];
                    endcase
                end
                8'h05: begin // read rw
                    case ( _cmd_rx_data[55:52] )
                        4'h0: _cmd_tx_data <= { 32'h0, rw[31:0]   };
                        4'h1: _cmd_tx_data <= { 32'h0, rw[63:32]  };
                        4'h2: _cmd_tx_data <= { 32'h0, rw[95:64]  };
                        4'h3: _cmd_tx_data <= { 32'h0, rw[127:96] };
                        4'h4: _cmd_tx_data <= { 32'h0, rw[159:128] };
                        4'h5: _cmd_tx_data <= { 32'h0, rw[191:160] };
                        4'h6: _cmd_tx_data <= { 32'h0, rw[223:192] };
                        4'h7: _cmd_tx_data <= { 32'h0, rw[255:224] };
                        4'h8: _cmd_tx_data <= { 32'h0, PARAM_CUSTOM_VALUE };
                        4'h9: _cmd_tx_data <= { 48'h0, pcie_perst_n, 15'h0 };
                        4'ha: _cmd_tx_data <= tickcount64;
                    endcase
                end
                8'h06: begin // PCIe reset
                    dpcie.pcie_rst_core   <= _cmd_rx_data[0];
                    dpcie.pcie_rst_subsys <= _cmd_rx_data[1];
                end
            endcase
        end
        
        if ( _cmd_tx_valid && !_cmd_tx_almost_full ) begin
            _cmd_tx_wr_en <= 1;
            _cmd_tx_din   <= { 2'b11, _cmd_tx_data[31:0] };
        end
    end
    
    // Shadow configuration space control
    assign dshadow2fifo.cfgtlp_wren   = rw[192];
    assign dshadow2fifo.cfgtlp_zero   = rw[203];
    assign dshadow2fifo.cfgtlp_en     = rw[204];
    assign dshadow2fifo.cfgtlp_filter = rw[205];
    assign dshadow2fifo.alltlp_filter = rw[207];  // Changed from rw[206] to rw[207]
    assign dshadow2fifo.cfgtlp_pcie_write_en = rw[206];  // CFGTLP PCIE WRITE ENABLE
    assign dshadow2fifo.cfg_a7        = {rw[21], rw[20]};  // Configuration address bit 7
    assign dshadow2fifo.bar_en        = rw[223:208];
    assign dshadow2fifo.rx_rden       = 0;
    assign dshadow2fifo.rx_wren       = 0;
    assign dshadow2fifo.rx_be         = 0;
    assign dshadow2fifo.rx_data       = 0;
    assign dshadow2fifo.rx_addr       = 0;
    assign dshadow2fifo.rx_addr_lo    = 0;

endmodule