----------------------------------------------------------------------------------
-- Company: IJS 
-- Engineer: Gasper Kojek
-- 
-- Create Date:    12:15:31 03/04/2014 
-- Last edit:      15:35    03/12/2014 
-- Design Name:    TX ENGINE
-- Module Name:    TX_ENGINE - rtl 
-- 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 TX_ENGINE is
port (
    -- Common
    clk                      : in std_logic;
    rst_n                    : in std_logic;

    -- Tx
    trn_td                   : out std_logic_vector( 63 downto 0); --transmit data
    trn_trem_n               : out std_logic_vector(7 downto 0); --valid data, 0x00-valid all, 0x0F-valid only on (63:32)
    trn_tsof_n               : out std_logic;   --active low, transmit start of frame
    trn_teof_n               : out std_logic;   --active low, transmit end of frame
    trn_tsrc_rdy_n           : out std_logic;   --active low, transmit source ready (TX ENGINE)-asserted when valid data on trn_td
    trn_tsrc_dsc_n           : out std_logic;   --active low, transmit source discontinue
    trn_tdst_rdy_n           : in std_logic;    --active low, transmit destination ready (PCIe endpoint block plus)
    trn_tdst_dsc_n           : in std_logic;    --not supported, always = '1'

    -- RX ENGINE communication
    req_compl_i              : in std_logic;
    req_compl_addr_err_i     : in std_logic; -- requested completion with data: address error
    req_compl_with_data_i    : in std_logic;  -- asserted indicates to generate a completion WITH data
                                              -- otherwise a completion WITHOUT data will be generated
    compl_done_o             : out std_logic;

    -- from received TLP header:
    req_tc_i                 : in std_logic_vector(2 downto 0);  -- Memory Read TC(traffic class field)
    req_td_i                 : in std_logic;                     -- Memory Read TD(TLP digest bit-ECRC)
    req_ep_i                 : in std_logic;                     -- Memory Read EP(poisoned bit)
    req_attr_i               : in std_logic_vector(1 downto 0);  -- Memory Read Attribute
    req_len_i                : in std_logic_vector(9 downto 0);  -- Memory Read Length (1DW)
    req_rid_i                : in std_logic_vector(15 downto 0); -- Memory Read Requestor ID
    req_tag_i                : in std_logic_vector(7 downto 0);  -- Memory Read Tag
    req_be_i                 : in std_logic_vector(7 downto 0);  -- Memory Read Byte Enables
    req_addr_i               : in std_logic_vector(12 downto 0); -- Memory Read Address 
    
    -- Settings
    completer_id_i           : in std_logic_vector(15 downto 0); --from cfg interface
    cfg_bus_mstr_enable_i    : in std_logic;

    -- Register Access
    data_r                   : in  std_logic_vector (63 downto 0);
    addr_r                   : out std_logic_vector (4 downto 0);
    busy_tx                  : in  std_logic;
    addr_error_r             : in  std_logic;
    re                       : out std_logic;
    addr_r_take_o            : out std_logic

);
end TX_ENGINE;

architecture rtl of TX_ENGINE is

constant TX_CPLD_FMT_TYPE     : std_logic_vector(6 downto 0) := "1001010"; --completion with data
constant TX_CPL_FMT_TYPE      : std_logic_vector(6 downto 0) := "0001010"; --completion without data

type state_type is (TX_RST_STATE, TX_CPLD_QW1, TX_CPLD_QW2, TX_CPL_QW1);

signal state                 : state_type;
signal byte_count            : std_logic_vector(11 downto 0); -- this field is the number of bytes left for transmission, including those in the current packet.
signal lower_addr            : std_logic_vector(6 downto 0);
signal rd_be_int           : std_logic_vector(7 downto 0);
signal req_compl_q           : std_logic;
signal req_compl_with_data_q : std_logic;
signal addr_r_int            : std_logic_vector (12 downto 0);
signal data_r_next           : std_logic_vector (31 downto 0);
-- signal read_count_len        : std_logic_vector (9 downto 0);   --in DW, same as len
signal read_count_len        : integer;
signal addr_r_take_int       : std_logic;

begin

    trn_tsrc_dsc_n   <= '1';
    addr_r <= addr_r_int(5 downto 1); 
    addr_r_take_o <= '1' when ((trn_tdst_rdy_n = '0') and (req_compl_q = '1') and (trn_tdst_dsc_n = '1')) 
                                or state/=TX_RST_STATE else '0';
    
    -- Present address and byte enable to memory module
    -- rd_addr_o <= req_addr_i(12 downto 2);
    -- so rd_be_int (internal) is upper hex is 1st DW BE, lower hex is last DW BE
    rd_be_int(7 downto 4)  <=  req_be_i(3 downto 0);
    rd_be_int(3 downto 0)  <=  req_be_i(7 downto 4);

-- Calculate byte count based on 1st byte enable
process(req_be_i, rd_be_int, req_len_i)
begin
    if rd_be_int(3 downto 0)=x"0" then -- last DW BE = 0x0
        case rd_be_int(7 downto 4) is
            when X"9" => byte_count <= X"004";
            when X"B" => byte_count <= X"004";
            when X"D" => byte_count <= X"004";
            when X"F" => byte_count <= X"004";
            when X"5" => byte_count <= X"003";
            when X"7" => byte_count <= X"003";
            when X"A" => byte_count <= X"003";
            when X"E" => byte_count <= X"003";
            when X"3" => byte_count <= X"002";
            when X"6" => byte_count <= X"002";
            when X"C" => byte_count <= X"002";
            when X"1" => byte_count <= X"001";
            when X"2" => byte_count <= X"001";
            when X"4" => byte_count <= X"001";
            when X"8" => byte_count <= X"001";
            when X"0" => byte_count <= X"001";
            when others => byte_count <= X"001";
        end case;
    elsif rd_be_int(4 downto 3)="11" then
        byte_count<="00"&(req_len_i+req_len_i+req_len_i+req_len_i);        --req_len_i*4
    elsif rd_be_int(4 downto 2)="101" or rd_be_int(5 downto 3)="101" then
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-1);    --req_len_i*4-1
    elsif rd_be_int(4 downto 1)="1001" or rd_be_int(5 downto 2)="1001" or rd_be_int(6 downto 3)="1001" then        
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-2);    --req_len_i*4-2
    elsif rd_be_int(4 downto 0)="10001" or rd_be_int(5 downto 1)="10001" or rd_be_int(6 downto 2)="10001" or rd_be_int(7 downto 3)="10001" then
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-3);    --req_len_i*4-3
    elsif rd_be_int(5 downto 0)="100001" or rd_be_int(6 downto 1)="100001" or rd_be_int(7 downto 2)="100001" then        
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-4);    --req_len_i*4-4
    elsif rd_be_int(6 downto 0)="1000001" or rd_be_int(7 downto 1)="1000001" then
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-5);    --req_len_i*4-5
    elsif rd_be_int=x"81" then
        byte_count<="00"&((req_len_i+req_len_i+req_len_i+req_len_i)-6);    --req_len_i*4-6
    end if;
end process;

-- Calculate lower address based on 1st DW byte enable
process(req_be_i, req_addr_i) -- FIXME --a bi mogl narest addressing po registrih po bajtih mogoče?
begin
    if (req_be_i(0) = '1') then
        -- when "---1"
        lower_addr <= req_addr_i(7 downto 3) & "00";
    elsif (req_be_i(1) = '1') then
        -- when "--10"
        lower_addr <= req_addr_i(7 downto 3) & "01";
    elsif (req_be_i(2) = '1') then
        -- when "-100"
        lower_addr <= req_addr_i(7 downto 3) & "10";
    elsif (req_be_i(3) = '1') then
        -- when "1000"
        lower_addr <= req_addr_i(7 downto 3) & "11";
    else
        -- when "0000"
        lower_addr <= req_addr_i(7 downto 3) & "00";
    end if;
end process;


process (rst_n, clk)
begin  
if (rst_n = '0') then
    req_compl_q <= '0';
    req_compl_with_data_q <= '1';
elsif (rising_edge(clk)) then
    req_compl_q <= req_compl_i;
    req_compl_with_data_q <= req_compl_with_data_i;
end if;
end process;


--  State Machine to generate Completion with 1 DW Payload or Completion without Data
process (rst_n, clk)
begin
if (rst_n = '0' ) then
    trn_tsof_n        <= '1';
    trn_teof_n        <= '1';
    trn_tsrc_rdy_n    <= '1';
    trn_td            <= (others => '0'); -- 64-bits
    trn_trem_n    <= (others => '0'); -- 8-bits
    compl_done_o      <= '0';
    state             <= TX_RST_STATE;
    re<='0';
elsif (rising_edge(clk)) then
    compl_done_o      <= '0';
    -- if req_compl_i='1' then addr_r_int<=req_addr_i; end if;
    
    if read_count_len>1 then
        read_count_len<=read_count_len-2;
    elsif read_count_len=1 then
        read_count_len<=read_count_len-1;
    end if;

    case ( state ) is

        when TX_RST_STATE =>
            if  ((trn_tdst_rdy_n = '0') and (req_compl_q = '1') and     --completion with data
                (req_compl_with_data_q = '1') and (trn_tdst_dsc_n = '1')) then
                trn_tsof_n       <= '0';
                trn_teof_n       <= '1';
                trn_tsrc_rdy_n   <= '0';
                trn_td           <= '0' & TX_CPLD_FMT_TYPE & '0' & req_tc_i & "0000" & req_td_i & req_ep_i &
                                    req_attr_i & "00" & req_len_i & completer_id_i & "000" & '0' & byte_count;
                trn_trem_n       <= (others => '0'); --valid data, 0x00-valid all, 0x0F-valid only on (63:32)
                state            <= TX_CPLD_QW1;
                addr_r_int<=addr_r_int+2;
                
                addr_r_take_int<='1';
                if req_len_i<3 then compl_done_o <= '1'; end if;             
                
                if req_addr_i<x"34" then --registers
                    -- addr_r<=req_addr_i(4 downto 0);
                    re<='1';
                -- else --FIXME : DDR2
                end if;
            elsif   ((trn_tdst_rdy_n = '0') and (req_compl_q = '1') and --completion without data
                    (req_compl_with_data_q = '0') and (trn_tdst_dsc_n = '1')) then
                trn_tsof_n       <= '0';
                trn_teof_n       <= '1';
                trn_tsrc_rdy_n   <= '0';
                compl_done_o     <= '1';
                trn_td           <= '0' & TX_CPL_FMT_TYPE & '0' & req_tc_i & "0000" & req_td_i & req_ep_i &
                                    req_attr_i & "00" & req_len_i & completer_id_i & "000" & '0' & byte_count;
                trn_trem_n       <= (others => '0'); -- 8-bit
                state            <= TX_CPL_QW1;
            else    --no TX request
                trn_tsof_n       <= '1';
                trn_teof_n       <= '1';
                trn_tsrc_rdy_n   <= '1';
                trn_td           <= (others => '0'); -- 64-bit
                trn_trem_n       <= (others => '0'); -- 8-bit
                compl_done_o     <= '0';
                addr_r_int<=req_addr_i;
                state            <= TX_RST_STATE;
            end if;

        when TX_CPLD_QW1 => --completion with data - QW1
            if ((busy_tx = '0') and (trn_tdst_dsc_n = '1') and (trn_tdst_rdy_n = '0')) then
                trn_tsof_n       <= '1';
                trn_tsrc_rdy_n   <= '0';
                trn_trem_n       <= (others => '0');
                read_count_len <=conv_integer( unsigned(req_len_i));
                -- addr_r_int<=addr_r_int+2;
                if req_addr_i<x"34" then --registers
                    if req_addr_i(0)='0' then
                        trn_td <= req_rid_i & req_tag_i & '0' & lower_addr & data_r(63 downto 32);
                        data_r_next <= data_r(31 downto 0);
                    else 
                        trn_td <= req_rid_i & req_tag_i & '0' & lower_addr & data_r(31 downto 0);
                        data_r_next <= data_r(63 downto 32); 
                        re<='1';
                    end if;
                -- else --FIXME for DDR2
                end if;
                if req_len_i>2 and req_addr_i<x"34" then --more than 1 register to read
                    re <= '1'; 
                    addr_r_int<=addr_r_int+2;                    
                    state <= TX_CPLD_QW2;
                elsif req_len_i="0000000001" then --last data
                    trn_teof_n       <= '0';
                    -- compl_done_o     <= '1';  
                    if req_addr_i=x"32" then
                        re<='1';
                    else
                        re<='0';
                    end if;
                    addr_r_int<=req_addr_i;
                    state            <= TX_RST_STATE;
                else 
                    re <= '0'; 
                    addr_r_int<=addr_r_int+2;
                    state <= TX_CPLD_QW2;
                end if;
            else 
                trn_tsrc_rdy_n   <= '1';
            end if;
        when TX_CPLD_QW2 => --completion with data -QW2 and after
            if ((busy_tx = '0') and (trn_tdst_dsc_n = '1') and (trn_tdst_rdy_n = '0') ) then
                trn_tsrc_rdy_n   <= '0';
                -- addr_r_int<=addr_r_int+2;
                if req_addr_i<x"34" then --registers
                    if req_addr_i(0)='0' then
                        trn_td <= data_r_next & data_r(63 downto 32);                
                        data_r_next <= data_r(31 downto 0);
                    else 
                       trn_td <= data_r_next & data_r(31 downto 0);
                        data_r_next <= data_r(63 downto 32);  
                    end if;                    
                    if read_count_len>2 then re<='1'; end if;                  
                -- else --FIXME for DDR2
                end if;
                if read_count_len<5 and read_count_len>2 then compl_done_o <= '1'; else compl_done_o <= '0'; end if;
                if read_count_len<3 and  req_len_i(0)='0' then --last data (even)
                    trn_teof_n       <= '0';
                    trn_trem_n       <= "00001111";
                    re<='0';
                    addr_r_int<=req_addr_i;
                    -- compl_done_o     <= '0';  
                    state            <= TX_RST_STATE;
                elsif read_count_len<3 and req_len_i(0)='1' then --last data (odd)
                    trn_teof_n       <= '0';
                    trn_trem_n       <= "00000000";
                    re<='0';
                    addr_r_int<=req_addr_i;
                    -- compl_done_o     <= '0';  
                    state            <= TX_RST_STATE;
                else 
                    state<=TX_CPLD_QW2;
                    addr_r_int<=addr_r_int+2;
                    re<='1';
                    trn_trem_n       <= (others => '0');
                end if;
            elsif (trn_tdst_dsc_n = '0') then
                re<='0';
                addr_r_int<=req_addr_i;
                state            <= TX_RST_STATE;
            else 
                trn_tsrc_rdy_n   <= '1';
            end if;   

        when TX_CPL_QW1 =>  --completion without data
            if ((trn_tdst_rdy_n = '0') and (trn_tdst_dsc_n = '1')) then
                trn_tsof_n       <= '1';
                trn_teof_n       <= '0';
                trn_tsrc_rdy_n   <= '0';
                trn_td           <= req_rid_i & req_tag_i & '0' & lower_addr & X"00000000";
                trn_trem_n       <= "00001111";
                -- compl_done_o     <= '1';  
                
                state            <= TX_RST_STATE;
            elsif (trn_tdst_dsc_n = '0') then
                state            <= TX_RST_STATE;
            end if;

        when others => NULL;
    end case;
end if;
end process;

end rtl;

