-- *************************************************************************
--
--  (c) Copyright 2010-2011 Xilinx, Inc. All rights reserved.
--
--  This file contains confidential and proprietary information
--  of Xilinx, Inc. and is protected under U.S. and
--  international copyright and other intellectual property
--  laws.
--
--  DISCLAIMER
--  This disclaimer is not a license and does not grant any
--  rights to the materials distributed herewith. Except as
--  otherwise provided in a valid license issued to you by
--  Xilinx, and to the maximum extent permitted by applicable
--  law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
--  WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
--  AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
--  BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
--  INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
--  (2) Xilinx shall not be liable (whether in contract or tort,
--  including negligence, or under any other theory of
--  liability) for any loss or damage of any kind or nature
--  related to, arising under or in connection with these
--  materials, including for any direct, or any indirect,
--  special, incidental, or consequential loss or damage
--  (including loss of data, profits, goodwill, or any type of
--  loss or damage suffered as a result of any action brought
--  by a third party) even if such damage or loss was
--  reasonably foreseeable or Xilinx had been advised of the
--  possibility of the same.
--
--  CRITICAL APPLICATIONS
--  Xilinx products are not designed or intended to be fail-
--  safe, or for use in any application requiring fail-safe
--  performance, such as life-support or safety devices or
--  systems, Class III medical devices, nuclear facilities,
--  applications related to the deployment of airbags, or any
--  other applications that could lead to death, personal
--  injury, or severe property or environmental damage
--  (individually and collectively, "Critical
--  Applications"). Customer assumes the sole risk and
--  liability of any use of Xilinx products in Critical
--  Applications, subject only to applicable laws and
--  regulations governing limitations on product liability.
--
--  THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
--  PART OF THIS FILE AT ALL TIMES. 
--
-- *************************************************************************
--
-------------------------------------------------------------------------------
-- Filename:    axi_sg_ftch_pntr.vhd
-- Description: This entity manages descriptor pointers and determine scatter
--              gather idle mode.
--
-- VHDL-Standard:   VHDL'93
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

library unisim;
use unisim.vcomponents.all;

library axi_sg_v4_1_3;
use axi_sg_v4_1_3.axi_sg_pkg.all;

-------------------------------------------------------------------------------
entity  axi_sg_ftch_pntr is
    generic (
        C_M_AXI_SG_ADDR_WIDTH       : integer range 32 to 64        := 32   ;
            -- Master AXI Memory Map Address Width for Scatter Gather R/W Port

        C_INCLUDE_CH1               : integer range 0 to 1          := 1    ;
            -- Include or Exclude channel 1 scatter gather engine
            -- 0 = Exclude Channel 1 SG Engine
            -- 1 = Include Channel 1 SG Engine

        C_INCLUDE_CH2               : integer range 0 to 1          := 1           
            -- Include or Exclude channel 2 scatter gather engine
            -- 0 = Exclude Channel 2 SG Engine
            -- 1 = Include Channel 2 SG Engine
    );
    port (
        -----------------------------------------------------------------------
        -- AXI Scatter Gather Interface
        -----------------------------------------------------------------------
        m_axi_sg_aclk               : in  std_logic                         ;                  --
        m_axi_sg_aresetn            : in  std_logic                         ;                  --
                                                                                               --
        nxtdesc                     : in  std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
                                                                                               --
        -------------------------------                                                        --
        -- CHANNEL 1                                                                           --
        -------------------------------                                                        --
        ch1_run_stop                : in  std_logic                         ;                  --
        ch1_desc_flush              : in  std_logic                         ; --CR568950       --
                                                                                               --
        -- CURDESC update to fetch pointer on run/stop assertion                               --
        ch1_curdesc                 : in  std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
                                                                                               --
        -- TAILDESC update on CPU write (from axi_dma_reg_module)                              --
        ch1_tailpntr_enabled        : in  std_logic                         ;                  --
        ch1_taildesc_wren           : in  std_logic                         ;                  --
        ch1_taildesc                : in  std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
                                                                                               --
        -- NXTDESC update on descriptor fetch (from axi_sg_ftchq_if)                           --
        ch1_nxtdesc_wren            : in  std_logic                         ;                  --
                                                                                               --
        -- Current address of descriptor to fetch                                              --
        ch1_fetch_address           : out std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
        ch1_sg_idle                 : out std_logic                         ;                  --
                                                                                               --
        -------------------------------                                                        --
        -- CHANNEL 2                                                                           --
        -------------------------------                                                        --
        ch2_run_stop                : in  std_logic                         ;                  --
        ch2_desc_flush              : in  std_logic                         ;--CR568950        --
        ch2_eof_detected            : in  std_logic                         ;                  --
                                                                                               --
        -- CURDESC update to fetch pointer on run/stop assertion                               --
        ch2_curdesc                 : in  std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
                                                                                               --
        -- TAILDESC update on CPU write (from axi_dma_reg_module)                              --
        ch2_tailpntr_enabled        : in  std_logic                         ;                  --
        ch2_taildesc_wren           : in  std_logic                         ;                  --
        ch2_taildesc                : in  std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
        tail_updt                   : in std_logic;
        tail_updt_latch             : out std_logic;
        ch2_updt_done               : in std_logic;
                                                                                               --
        -- NXTDESC update on descriptor fetch (from axi_sg_ftchq_if)                           --
        ch2_nxtdesc_wren            : in  std_logic                         ;                  --
                                                                                               --
        -- Current address of descriptor to fetch                                              --
        ch2_fetch_address           : out std_logic_vector                                     --
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)  ;                  --
        ch2_sg_idle                 : out std_logic                         ;                   --
        bd_eq                       : out std_logic
    );

end axi_sg_ftch_pntr;

-------------------------------------------------------------------------------
-- Architecture
-------------------------------------------------------------------------------
architecture implementation of axi_sg_ftch_pntr is
  attribute DowngradeIPIdentifiedWarnings: string;
  attribute DowngradeIPIdentifiedWarnings of implementation : architecture is "yes";



-------------------------------------------------------------------------------
-- Functions
-------------------------------------------------------------------------------

-- No Functions Declared

-------------------------------------------------------------------------------
-- Constants Declarations
-------------------------------------------------------------------------------

-- No Constants Declared

-------------------------------------------------------------------------------
-- Signal / Type Declarations
-------------------------------------------------------------------------------
signal ch1_run_stop_d1              : std_logic := '0';
signal ch1_run_stop_re              : std_logic := '0';
signal ch1_use_crntdesc             : std_logic := '0';
signal ch1_fetch_address_i          : std_logic_vector
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)
                                        := (others => '0');

signal ch2_run_stop_d1              : std_logic := '0';
signal ch2_run_stop_re              : std_logic := '0';
signal ch2_use_crntdesc             : std_logic := '0';
signal ch2_fetch_address_i          : std_logic_vector
                                        (C_M_AXI_SG_ADDR_WIDTH-1 downto 0)
                                        := (others => '0');
signal first : std_logic;
signal eof_latch : std_logic;

signal ch2_sg_idle_int : std_logic;
-------------------------------------------------------------------------------
-- Begin architecture logic
-------------------------------------------------------------------------------
begin

-- Channel 1 is included therefore generate pointer logic
GEN_PNTR_FOR_CH1 : if C_INCLUDE_CH1 = 1 generate
begin


    GEN_RUNSTOP_RE : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0')then
                    ch1_run_stop_d1 <= '0';
                else
                    ch1_run_stop_d1 <= ch1_run_stop;
                end if;
            end if;
        end process GEN_RUNSTOP_RE;

    ch1_run_stop_re <= ch1_run_stop and not ch1_run_stop_d1;


    ---------------------------------------------------------------------------
    -- At setting of run/stop need to use current descriptor pointer therefor
    -- flag for use
    ---------------------------------------------------------------------------
    GEN_INIT_PNTR : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0' or ch1_nxtdesc_wren = '1')then
                    ch1_use_crntdesc <= '0';
                elsif(ch1_run_stop_re = '1')then
                    ch1_use_crntdesc <= '1';
                end if;
            end if;
        end process GEN_INIT_PNTR;

    ---------------------------------------------------------------------------
    -- Register Current Fetch Address.  During start (run/stop asserts) reg
    -- curdesc pointer from register module.  Once running use nxtdesc pointer.
    ---------------------------------------------------------------------------
    REG_FETCH_ADDRESS : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0')then
                    ch1_fetch_address_i <= (others => '0');
                -- On initial tail pointer write use current desc pointer
                elsif(ch1_use_crntdesc = '1' and ch1_nxtdesc_wren = '0')then
                    ch1_fetch_address_i <= ch1_curdesc;
                -- On desriptor fetch capture next pointer
                elsif(ch1_nxtdesc_wren = '1')then
                    ch1_fetch_address_i <= nxtdesc;
                end if;
            end if;
        end process REG_FETCH_ADDRESS;

    -- Pass address out of module
    -- Addresses are always 16 word 32-bit aligned
    ch1_fetch_address <= ch1_fetch_address_i (C_M_AXI_SG_ADDR_WIDTH-1 downto 6) & "000000";

    ---------------------------------------------------------------------------
    -- Compair tail descriptor pointer to scatter gather engine current
    -- descriptor pointer.  Set idle if matched.  Only check if DMA engine
    -- is running and current descriptor is in process of being fetched.  This
    -- forces at least 1 descriptor fetch before checking for IDLE condition.
    ---------------------------------------------------------------------------
    COMPARE_ADDRESS : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                -- SG is IDLE on reset and on stop.
                --CR568950 - reset idlag on descriptor flush
                --if(m_axi_sg_aresetn = '0' or ch1_run_stop = '0')then
                if(m_axi_sg_aresetn = '0' or ch1_run_stop = '0' or ch1_desc_flush = '1')then
                    ch1_sg_idle <= '1';

                -- taildesc_wren must be in this 'if' to force a minimum
                -- of 1 clock of sg_idle = '0'.
                elsif(ch1_taildesc_wren = '1' or ch1_tailpntr_enabled = '0')then
                    ch1_sg_idle <= '0';

                -- Descriptor at fetch_address is being fetched (wren=1)
                -- therefore safe to check if tail matches the fetch address
                elsif(ch1_nxtdesc_wren = '1'
                and ch1_taildesc = ch1_fetch_address_i)then
                    ch1_sg_idle <= '1';
                end if;
            end if;
        end process COMPARE_ADDRESS;

end generate GEN_PNTR_FOR_CH1;


-- Channel 1 is NOT included therefore tie off pointer logic
GEN_NO_PNTR_FOR_CH1 : if C_INCLUDE_CH1 = 0 generate
begin
    ch1_fetch_address   <= (others =>'0');
    ch1_sg_idle         <= '0';
end generate GEN_NO_PNTR_FOR_CH1;

-- Channel 2 is included therefore generate pointer logic
GEN_PNTR_FOR_CH2 : if C_INCLUDE_CH2 = 1 generate
begin

    ---------------------------------------------------------------------------
    -- Create clock delay of run_stop in order to generate a rising edge pulse
    ---------------------------------------------------------------------------
    GEN_RUNSTOP_RE : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0')then
                    ch2_run_stop_d1 <= '0';
                else
                    ch2_run_stop_d1 <= ch2_run_stop;
                end if;
            end if;
        end process GEN_RUNSTOP_RE;

    ch2_run_stop_re <= ch2_run_stop and not ch2_run_stop_d1;

    ---------------------------------------------------------------------------
    -- At setting of run/stop need to use current descriptor pointer therefor
    -- flag for use
    ---------------------------------------------------------------------------
    GEN_INIT_PNTR : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0' or ch2_nxtdesc_wren = '1')then
                    ch2_use_crntdesc <= '0';
                elsif(ch2_run_stop_re = '1')then
                    ch2_use_crntdesc <= '1';
                end if;
            end if;
        end process GEN_INIT_PNTR;

    ---------------------------------------------------------------------------
    -- Register Current Fetch Address.  During start (run/stop asserts) reg
    -- curdesc pointer from register module.  Once running use nxtdesc pointer.
    ---------------------------------------------------------------------------
    REG_FETCH_ADDRESS : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0')then
                    ch2_fetch_address_i <= (others => '0');
                -- On initial tail pointer write use current desc pointer
                elsif((ch2_use_crntdesc = '1' and ch2_nxtdesc_wren = '0'))then
                    ch2_fetch_address_i <= ch2_curdesc;
                -- On descirptor fetch capture next pointer
                elsif(ch2_nxtdesc_wren = '1')then
                    ch2_fetch_address_i <= nxtdesc;
                end if;
            end if;
        end process REG_FETCH_ADDRESS;

    -- Pass address out of module
    -- Addresses are always 16 word 32-bit aligned
    ch2_fetch_address <= ch2_fetch_address_i (C_M_AXI_SG_ADDR_WIDTH-1 downto 6) & "000000";

    ---------------------------------------------------------------------------
    -- Compair tail descriptor pointer to scatter gather engine current
    -- descriptor pointer.  Set idle if matched.  Only check if DMA engine
    -- is running and current descriptor is in process of being fetched.  This
    -- forces at least 1 descriptor fetch before checking for IDLE condition.
    ---------------------------------------------------------------------------
    COMPARE_ADDRESS : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                -- SG is IDLE on reset and on stop.
                --CR568950 - reset idlag on descriptor flush
                --if(m_axi_sg_aresetn = '0' or ch2_run_stop = '0')then
                if(m_axi_sg_aresetn = '0' or ch2_run_stop = '0' or ch2_desc_flush = '1' or ch2_eof_detected = '1')then
                    ch2_sg_idle <= '1';
                    ch2_sg_idle_int <= '1';

                -- taildesc_wren must be in this 'if' to force a minimum
                -- of 1 clock of sg_idle = '0'.
                elsif(ch2_taildesc_wren = '1' or ch2_tailpntr_enabled = '0')then
                    ch2_sg_idle <= '0';
                    ch2_sg_idle_int <= '0';

                -- Descriptor at fetch_address is being fetched (wren=1)
                -- therefore safe to check if tail matches the fetch address
                elsif(ch2_nxtdesc_wren = '1'
                and ch2_taildesc = ch2_fetch_address_i)then
                    ch2_sg_idle <= '1';
                    ch2_sg_idle_int <= '1';
                end if;
            end if;
        end process COMPARE_ADDRESS;

-- Needed for multi channel

    EOF_LATCH_PROC : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0' or ch2_taildesc_wren = '1' or eof_latch = '1')then  -- nned to have some reset condition here
                    eof_latch <= '0';
                elsif (ch2_sg_idle_int = '1' and ch2_updt_done = '1') then
                    eof_latch <= '1';
                end if;
            end if;
        end process EOF_LATCH_PROC;



    TAILUPDT_LATCH : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0' or eof_latch = '1')then  -- nned to have some reset condition here
                    tail_updt_latch <= '0';
                    first <= '0';
                elsif (tail_updt = '1') then
                    tail_updt_latch <= '0';
                elsif(ch2_taildesc_wren = '1' and first = '0')then
                    first <= '1';
                elsif(ch2_taildesc_wren = '1' and first = '1')then
                    tail_updt_latch <= '1';
                end if;
            end if;
        end process TAILUPDT_LATCH;

    EQUAL_BD : process(m_axi_sg_aclk)
        begin
            if(m_axi_sg_aclk'EVENT and m_axi_sg_aclk = '1')then
                if(m_axi_sg_aresetn = '0' or ch2_run_stop = '0' or ch2_desc_flush = '1')then
                    bd_eq <= '0';

                elsif(ch2_taildesc_wren = '1' or ch2_tailpntr_enabled = '0')then
                    bd_eq <= '0';

                elsif(ch2_nxtdesc_wren = '1'
                and ch2_taildesc = ch2_fetch_address_i)then
                    bd_eq <= '1';
                end if;
            end if;
        end process EQUAL_BD;


end generate GEN_PNTR_FOR_CH2;


-- Channel 2 is NOT included therefore tie off pointer logic
GEN_NO_PNTR_FOR_CH2 : if C_INCLUDE_CH2 = 0 generate
begin
    ch2_fetch_address   <= (others =>'0');
    ch2_sg_idle         <= '0';
    tail_updt_latch <= '0';
    bd_eq <= '0';
end generate GEN_NO_PNTR_FOR_CH2;

end implementation;
