----------------------------------------------------------------------------------
-- Company: IJS 
-- Engineer: Gasper Kojek
-- 
-- Create Date:    12:15:46 03/04/2014
-- Last edit:      15:35    03/12/2014 
-- Design Name:    RX ENGINE
-- Module Name:    RX_ENGINE - Behavioral 
-- Project Name: 
-- Target Devices:  Xilinx Virtex 5
-- Tool versions:   Xilinx ISE 14.7
-- Description: 
--
-- Dependencies: 
--
-- Revision: 1.0
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_ARITH.ALL;

entity RX_ENGINE is
port (
    -- Common
    trn_clk      : in std_logic;
    trn_reset_n  : in std_logic;
    trn_lnk_up_n : in std_logic;

    -- Rx
    trn_rd          : in std_logic_vector((64 - 1) downto 0); --receive daa
    trn_rrem_n      : in std_logic_vector(7 downto 0); --receive data remainder(if all 0 , data valid 63 downto 0, if 0x0F, data valid only on 63 downto 32)
    trn_rsof_n      : in std_logic; --receive start of frame
    trn_reof_n      : in std_logic; --receive end of frame
    trn_rsrc_rdy_n  : in std_logic; --active low-indicates pcie is presenting valid data
    trn_rsrc_dsc_n  : in std_logic; --not supported, tied high
    trn_rdst_rdy_n  : out std_logic; --active low-receive destination(user application) ready
    trn_rerrfwd_n   : in std_logic;  --active low-receive error forward-marks the packet in progress as poisoned
    trn_rnp_ok_n    : out std_logic; --receive non posted OK

    trn_rbar_hit_n  : in std_logic_vector(6 downto 0);  --active low-indicates BAR(s) targeted by current receive transaction
    trn_rfc_nph_av  : in std_logic_vector(7 downto 0);  --flow control
    trn_rfc_npd_av  : in std_logic_vector(11 downto 0); --flow control
    trn_rfc_ph_av   : in std_logic_vector(7 downto 0);  --flow control
    trn_rfc_pd_av   : in std_logic_vector(11 downto 0); --flow control
    trn_rcpl_streaming_n : out std_logic; --active low-receive completion streaming
    
    -- Register Access (write)
    data_w             : out std_logic_vector (63 downto 0);
    addr_w             : out std_logic_vector (4 downto 0);
    we_w               : out std_logic;
    addr_error_w       : in std_logic;
    busy_w             : in std_logic;
    -- Register Access (read)    
    data_r             : in std_logic_vector (63 downto 0);
    addr_r             : out std_logic_vector (4 downto 0);
    data_r_valid       : in std_logic;

    -- TX ENGINE communication
    req_compl_o             : out std_logic;
    req_compl_addr_err_o    : out std_logic; -- request completion with data: address error
    req_compl_with_data_o   : out std_logic; -- asserted indicates to generate a completion WITH data    
                                             -- otherwise a completion WITHOUT data will be generated
    compl_done_i            : in std_logic;  -- requested completion done
    
    -- from received TLP header:
    req_tc_o          : out std_logic_vector(2 downto 0);  -- Memory Read TC(traffic class field) 
    req_td_o          : out std_logic; -- Memory Read TD(TLP digest bit-ECRC)  - vedno 0?
    req_ep_o          : out std_logic; -- Memory Read EP(poisoned bit)         - not yet implemented
    req_attr_o        : out std_logic_vector(1 downto 0);  -- Memory Read Attribute
    req_len_o         : out std_logic_vector(9 downto 0);  -- Memory Read Length (1DW)
    req_rid_o         : out std_logic_vector(15 downto 0); -- Memory Read Requestor ID
    req_tag_o         : out std_logic_vector(7 downto 0);  -- Memory Read Tag
    req_be_o          : out std_logic_vector(7 downto 0);  -- Memory Read Byte Enables
    req_addr_o        : out std_logic_vector(12 downto 0)  -- Memory Read Address 
    );

end RX_ENGINE;

architecture rtl of RX_ENGINE is
alias clk   : std_logic is trn_clk;
alias rst_n : std_logic is trn_reset_n;

--BAR hits:
constant registers      : std_logic_vector(6 downto 0) := "1111110";
constant ddr2           : std_logic_vector(6 downto 0) := "1111101"; --FIXME

--FMT and TYPE:
constant RX_MEM_RD32    : std_logic_vector(6 downto 0) := "0000000"; 
constant RX_IO_RD32     : std_logic_vector(6 downto 0) := "0000010"; 
constant RX_MEM_RD64    : std_logic_vector(6 downto 0) := "0100000"; 

constant RX_MEM_WR32    : std_logic_vector(6 downto 0) := "1000000"; 
constant RX_IO_WR32     : std_logic_vector(6 downto 0) := "1000010"; 
constant RX_MEM_WR64    : std_logic_vector(6 downto 0) := "1100000";

type state_type is (RX_RST_STATE,
                    RX_MEM_RD32_QW2,
                    RX_MEM_RD64_QW2,
                    RX_MEM_WR32_QW2,
                    RX_MEM_WR32_QW3, --and all after (if any)
                    RX_MEM_WR64_QW2,
                    RX_MEM_WR64_QW3, --and all after (if any)
                    RX_IO_WR_1DW,
                    RX_IO_WR_1DW_WAIT_1,
                    RX_WR32_WAIT_STATE,
                    RX_WR64_WAIT_STATE,
                    RX_WAIT_STATE);

signal state              : state_type;
signal tlp_type           : std_logic_vector (6 downto 0);
signal trn_rdst_rdy_n_int : std_logic;
signal addr_w_int         : std_Logic_vector (4 downto 0); --FIXME -- za ddr2
signal NULL_QW            : std_logic_vector (63 downto 0);
signal tlp_write_len, new_tlp_write_len      : std_logic_vector (9 downto 0);
signal data_w_next        : std_logic_vector (31 downto 0);
signal addr_odd           : std_logic;


begin
--core Tie-offs:
  trn_rnp_ok_n              <= '0';
  trn_rcpl_streaming_n      <= '1'; 
  

trn_rdst_rdy_n <= trn_rdst_rdy_n_int;
new_tlp_write_len<=tlp_write_len-2 when tlp_write_len>1 else tlp_write_len-1;


process (clk, rst_n)
begin
  if (rst_n = '0' ) then
    trn_rdst_rdy_n_int <= '0';
    req_compl_o     <= '0';
    req_compl_addr_err_o<='0';    
    req_compl_with_data_o  <= '1';
    req_tc_o        <= (others => '0');
    req_td_o        <= '0';
    req_ep_o        <= '0';
    req_attr_o      <= (others => '0');
    req_len_o       <= (others => '0');
    req_rid_o       <= (others => '0');
    req_tag_o       <= (others => '0');
    req_be_o        <= (others => '0');
    req_addr_o      <= (others => '0');
    addr_w          <= (others => '0');
    data_w          <= (others => '0');
    we_w            <= '0';
    state           <= RX_RST_STATE;
    tlp_type        <= (others => '0');
    tlp_write_len   <= (others => '0');
  else
    if (rising_edge(clk)) then
     
      addr_odd  <='0';
      we_w        <= '0';
      req_compl_o    <= '0';
      req_compl_with_data_o  <= '1';
      -- if (tlp_type = RX_MEM_WR32) or (tlp_type = RX_IO_WR32) then --if 32 bit write
        -- data_w(63 downto 32) <= data_w_next; --for 32 bit write - shift
        -- data_w(31 downto 0) <= data_w_next; --for 32 bit write - shift --FIXME
      -- end if;
      addr_w_int<=addr_w_int+1;
      -- if (tlp_write_len>1) then
        -- new_tlp_write_len<=tlp_write_len-2;
        -- addr_w_int<=addr_w_int+1;
      -- else 
        -- new_tlp_write_len<=tlp_write_len-1;
      -- end if;

      case (state) is

        -- Wait in reset state until Memory or IO TLP with 1 DW payload is received
        when RX_RST_STATE =>
          
          trn_rdst_rdy_n_int <= '0';

            -- if start of rx trn transaction
            if ((trn_rsof_n = '0') and (trn_rsrc_rdy_n = '0') and (trn_rdst_rdy_n_int = '0')) then

                -- check tlp fmt and type fields
                case (trn_rd(62 downto 56)) is

                  -- tlp is 32 bit read request
                    when RX_MEM_RD32 =>               
                        tlp_type           <= trn_rd(62 downto 56);
                        req_len_o          <= trn_rd(41 downto 32);
                        req_tc_o     <= trn_rd(54 downto 52);
                        req_td_o     <= trn_rd(47);
                        req_ep_o     <= trn_rd(46);
                        req_attr_o   <= trn_rd(45 downto 44);
                        req_len_o    <= trn_rd(41 downto 32); --length in DW (1 DW = 32 bit / 4 bytes)
                        req_rid_o    <= trn_rd(31 downto 16); --requester ID
                        req_tag_o    <= trn_rd(15 downto 8);
                        req_be_o     <= trn_rd(7 downto 0);
                        
                        state        <= RX_MEM_RD32_QW2;

                      -- tlp is mem64 read request 
                    when RX_MEM_RD64 =>
                        tlp_type     <= trn_rd(62 downto 56);
                        req_len_o    <= trn_rd(41 downto 32);
                        req_tc_o     <= trn_rd(54 downto 52);
                        req_td_o     <= trn_rd(47);
                        req_ep_o     <= trn_rd(46);
                        req_attr_o   <= trn_rd(45 downto 44);
                        req_len_o    <= trn_rd(41 downto 32);
                        req_rid_o    <= trn_rd(31 downto 16);
                        req_tag_o    <= trn_rd(15 downto 08);
                        req_be_o     <= trn_rd(07 downto 00);
                        
                        state        <= RX_MEM_RD64_QW2;

                      -- tlp is 32 bit write request 
                    when RX_MEM_WR32 =>
                        tlp_type     <= trn_rd(62 downto 56); --shranimo tip
                        -- req_len_o    <= trn_rd(41 downto 32); --nevem še zakaj se rabi to na TX strani
                        tlp_write_len    <= trn_rd(41 downto 32); --števec kako dolg je paket

                        state        <= RX_MEM_WR32_QW2;

                      -- tlp is mem64 write request
                    when RX_MEM_WR64 =>
                        tlp_type     <= trn_rd(62 downto 56); --shranimo tip
                        -- req_len_o    <= trn_rd(41 downto 32); --nevem še zakaj se rabi to na TX strani
                        tlp_write_len    <= trn_rd(41 downto 32); --števec kako dolg je paket

                        state        <= RX_MEM_WR64_QW2;
                    -- tlp is io read request
                    when RX_IO_RD32 =>
                        tlp_type     <= trn_rd(62 downto 56);
                        req_len_o    <= trn_rd(41 downto 32);
                        trn_rdst_rdy_n_int <= '1'; 
                        -- if tlp payload length is 1 DW then process
                        if (trn_rd(41 downto 32) = "0000000001") then
                            req_tc_o     <= trn_rd(54 downto 52);
                            req_td_o     <= trn_rd(47);
                            req_ep_o     <= trn_rd(46);
                            req_attr_o   <= trn_rd(45 downto 44);
                            req_len_o    <= trn_rd(41 downto 32);
                            req_rid_o     <= trn_rd(31 downto 16);
                            req_tag_o    <= trn_rd(15 downto 8);
                            req_be_o     <= trn_rd(7 downto 0);
                            state        <= RX_MEM_RD32_QW2;        --FIXME       
                        end if;

                    -- tlp is io write request
                    when RX_IO_WR32 =>
                        tlp_type     <= trn_rd(62 downto 56);
                        req_len_o    <= trn_rd(41 downto 32);
                        -- if tlp payload length is 1 DW then process 
                        if (trn_rd(41 downto 32) = "0000000001") then
                            req_tc_o     <= trn_rd(54 downto 52);
                            req_td_o     <= trn_rd(47);
                            req_ep_o     <= trn_rd(46);
                            req_attr_o   <= trn_rd(45 downto 44);
                            req_len_o    <= trn_rd(41 downto 32);
                            req_rid_o     <= trn_rd(31 downto 16);
                            req_tag_o    <= trn_rd(15 downto 8);
                            req_be_o     <= trn_rd(7 downto 0);
                            state        <= RX_IO_WR_1DW;
                        end if;                        

                    when others => -- other TLPs not supported

                        state <= RX_RST_STATE;

                end case;
            end if;

        -- first and second dwords of mem32 read tlp
        when RX_MEM_RD32_QW2 =>
            if (trn_rsrc_rdy_n = '0') then
                trn_rdst_rdy_n_int <= '1';
                req_compl_o  <= '1';
                req_compl_with_data_o  <= '1';
                req_addr_o<=trn_rd(46 downto 34); -- FIXME ?
                state <= RX_WAIT_STATE;
            end if;
              
        -- first and second dwords of mem64 read tlp
        when RX_MEM_RD64_QW2 =>
            if (trn_rsrc_rdy_n = '0') then
                req_compl_o  <= '1';
                req_compl_with_data_o  <= '1';
                trn_rdst_rdy_n_int <= '1';
                req_addr_o<=trn_rd(14 downto 2);-- FIXME ?
                state <= RX_WAIT_STATE;
            end if;       

        -- second QW of mem32 write tlp --address and higher DW of data
        when RX_MEM_WR32_QW2 =>     --FIXME
            if ((trn_rsrc_rdy_n = '0') and (trn_rdst_rdy_n_int='0')) then
                -- if tlp_type=RX_IO_WR32 then
                    -- req_compl_o  <= '1';
                    -- req_compl_with_data_o  <= '0';
                    -- if trn_rbar_hit_n = registers then
                        
                        -- data_w_next <= trn_rd(31 downto 0);
                        -- trn_rdst_rdy_n_int <= '1';
                        -- addr_w  <= trn_rd(39 downto 35);
                        -- addr_w_int  <= trn_rd(39 downto 35);
                        -- state   <= RX_MEM_WR32_QW3;                        
                    -- end if;
                -- else
                    if trn_rbar_hit_n = registers then --if registers
                        data_w_next <= trn_rd(31 downto 0);
                        addr_w  <= trn_rd(39 downto 35);
                        addr_w_int  <= trn_rd(39 downto 35);
                        if busy_w='1' then
                            state   <= RX_WR32_WAIT_STATE; --there's more tlp's
                            trn_rdst_rdy_n_int <= '1';
                        else 
                            state   <= RX_MEM_WR32_QW3; 
                        end if;                        
                    end if; 
                -- end if;
            else 
                if busy_w='0' then
                    state <= RX_MEM_WR32_QW2;
                    trn_rdst_rdy_n_int <= '0';
                end if;
            end if;
            
        -- third and after qword of mem32 write tlp contains 2 DW(1 QW) write data --low DW of current data and high DW of next data    
        when RX_MEM_WR32_QW3 =>     --FIXME
            if ((trn_rsrc_rdy_n = '0') and (trn_rdst_rdy_n_int='0')) then
                if trn_rbar_hit_n = registers then --if registers
                    data_w (31 downto 0) <= trn_rd (63 downto 32);
                    data_w_next <= trn_rd (31 downto 0);
                    data_w(63 downto 32) <= data_w_next; --FIXME ??
                    we_w<='1';
                    addr_w<=addr_w_int;
                    if (new_tlp_write_len>0) then
                        tlp_write_len<=new_tlp_write_len;
                        if busy_w='1' then
                            state   <= RX_WR32_WAIT_STATE; --there's more tlp's
                            trn_rdst_rdy_n_int <= '1';
                        end if;
                    else
                    state   <= RX_WAIT_STATE; --last tlp 
                    end if;
                end if;
            else state   <= RX_WR32_WAIT_STATE;
            end if;

        when RX_WR32_WAIT_STATE => 
            if trn_rbar_hit_n = registers then --if registers
                we_w    <= '1';
                if busy_w='0' then
                    state <= RX_MEM_WR32_QW3;
                    trn_rdst_rdy_n_int <= '0';
                end if;
            end if;
 
        -- first and second dwords of mem64 write tlp - 64 bit address
        when RX_MEM_WR64_QW2 =>
            if ((trn_rsrc_rdy_n = '0') and (trn_rdst_rdy_n_int='0')) then
                if trn_rbar_hit_n = registers then --if registers
                    trn_rdst_rdy_n_int <= '0';
                    addr_w  <= trn_rd (7 downto 3); 
                    addr_w_int  <= trn_rd (7 downto 3); 
                    -- tlp write data is not available until next clock
                end if;
            state <= RX_MEM_WR64_QW3;
            end if;

        -- third and after qword of mem64 write tlp contains 2 DW(1 QW) write data
        when RX_MEM_WR64_QW3 =>
            if ((trn_rsrc_rdy_n = '0') and (trn_rdst_rdy_n_int='0')) then
                if trn_rbar_hit_n = registers then --if registers
                    data_w  <= trn_rd(63 downto 0); --FIXME
                    --al je tko:
                    -- data_w (31 downto 0)  <= trn_rd (63 downto 32);
                    -- data_w (63 downto 32)  <= trn_rd (31 downto 0);
                    we_w    <= '1';
                    addr_w<=addr_w_int;
                    if (new_tlp_write_len>0) then 
                        tlp_write_len<=new_tlp_write_len;
                        if busy_w='1' then
                            state   <= RX_WR64_WAIT_STATE; --there's more tlp's
                            trn_rdst_rdy_n_int <= '1';
                        end if;
                    else 
                        state   <= RX_WAIT_STATE; --last tlp 
                    end if;
                end if;
            end if;
        when RX_WR64_WAIT_STATE=>
            if trn_rbar_hit_n = registers then --if registers
                we_w<='1';
                if busy_w='0' then
                    state <= RX_MEM_WR64_QW3;
                    trn_rdst_rdy_n_int <= '0';
                end if;
            end if;
            
        -- first and second dwords of io write tlp
        when RX_IO_WR_1DW =>
            if (trn_rsrc_rdy_n = '0') and  (trn_rdst_rdy_n_int='0') then
                if trn_rd(34)='1' then --lih naslov
                    addr_odd<='1';
                    data_w(63 downto 32)  <= trn_rd(31 downto 0);
                else 
                    data_w(31 downto 0)  <= trn_rd(31 downto 0);
                end if;
                addr_w  <= trn_rd(39 downto 35);--FIXME
                addr_r  <= trn_rd(39 downto 35);--FIXME
                req_compl_o  <= '1';
                req_compl_with_data_o  <= '0';
                trn_rdst_rdy_n_int <= '1';
                we_w<='1';
                state  <= RX_IO_WR_1DW_WAIT_1;
            end if;   

        when RX_IO_WR_1DW_WAIT_1 => --waiting for read data
            if addr_odd='1' then --lih naslov
                data_w(31 downto 0)  <= data_r(31 downto 0);
            else 
                data_w(63 downto 32)  <= data_r(31 downto 0);
            end if;
            state  <= RX_WAIT_STATE;   
            -- state   <= RX_IO_WR_1DW_WAIT_1;  
            
        -- when RX_IO_WR_1DW_WAIT_2 =>
            -- we_w    <= '1';
            -- state   <= RX_WAIT_STATE;   

        -- Stay in wait state for
        --  1. Target writes until the write has been completed and
        --     written into BRAM.
        --  2. Target reads until the completion has been generated
        --     and has been successfully transmitted via the PIO's
        --     TX interface.
        -- 3. IO Write and Extended CFG write until the completion 
        --     has been generated and has been successfully transmitted
        --     via the PIOs TX interface

        when RX_WAIT_STATE =>
              req_compl_o  <= '0';
              if ((tlp_type = RX_MEM_WR32) and (busy_w = '0')) then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;

              elsif ((tlp_type = RX_IO_WR32) and (busy_w = '0'))  then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;

              elsif ((tlp_type = RX_MEM_WR64) and (busy_w = '0')) then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;

              elsif ((tlp_type = RX_MEM_RD32) and (compl_done_i = '1')) then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;

              elsif ((tlp_type = RX_IO_RD32) and (compl_done_i = '1')) then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;

              elsif ((tlp_type = RX_MEM_RD64) and (compl_done_i = '1')) then
                trn_rdst_rdy_n_int <= '0';
                we_w      <= '0';
                state        <= RX_RST_STATE;
              end if;

        when others =>
              state <= RX_RST_STATE; 
              we_w      <= '0';

      end case;
    end if;
  end if;
end process;


end rtl;

