-------------------------------------------------------------------------------
--
-- (c) Copyright 2009-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.
--
-------------------------------------------------------------------------------
-- Project    : Virtex-6 Integrated Block for PCI Express
-- File       : axi_basic_tx_thrtl_ctl.vhd
-- Version    : 2.3
--
-- Description:
--    TX throttle controller. Anticipates back-pressure from PCIe block and
--      preemptively back-pressures user design (packet boundary throttling).
--
--      Notes:
--      Optional notes section.
--
--      Hierarchical:
--        axi_basic_top
--          axi_basic_tx
--            axi_basic_tx_thrtl_ctl
--------------------------------------------------------------------------------
-- Library Declarations
--------------------------------------------------------------------------------

LIBRARY ieee;
   USE ieee.std_logic_1164.all;
   USE ieee.std_logic_unsigned.all;


ENTITY axi_basic_tx_thrtl_ctl IS
   GENERIC (
      C_DATA_WIDTH              : INTEGER := 128;      -- RX/TX interface data width
      C_FAMILY                  : STRING := "X7";      -- Targeted FPGA family
      C_ROOT_PORT               : BOOLEAN := FALSE;   -- PCIe block is in root port mode
      TCQ                       : INTEGER := 1         -- Clock to Q time
   );
   PORT (
     -- AXI TX
     -------------
      S_AXIS_TX_TDATA           : IN STD_LOGIC_VECTOR(C_DATA_WIDTH - 1 DOWNTO 0)  := (OTHERS=>'0'); -- TX data from user
      S_AXIS_TX_TVALID          : IN STD_LOGIC                     := '0';    -- TX data is valid
      S_AXIS_TX_TUSER           : IN STD_LOGIC_VECTOR(3 DOWNTO 0)  := "0000"; -- TX user signals
      S_AXIS_TX_TLAST           : IN STD_LOGIC                     := '0';    -- TX data is last

     -- User Misc.
     -------------
      USER_TURNOFF_OK           : IN STD_LOGIC                     := '0';    -- Turnoff OK from user
      USER_TCFG_GNT             : IN STD_LOGIC                     := '0';    -- Send cfg OK from user

     -- TRN TX
     -------------
      TRN_TBUF_AV               : IN STD_LOGIC_VECTOR(5 DOWNTO 0)  := "000000"; -- TX buffers available
      TRN_TDST_RDY              : IN STD_LOGIC                     := '0';     -- TX destination ready

     -- TRN Misc.
     -------------
      TRN_TCFG_REQ              : IN STD_LOGIC                     := '0';     -- TX config request
      TRN_TCFG_GNT              : OUT STD_LOGIC                    ;--:= '0';     -- RX config grant
      TRN_LNK_UP                : IN STD_LOGIC                     := '0';     -- PCIe link up

     -- 7 Series/Virtex6 PM
     -------------
      CFG_PCIE_LINK_STATE       : IN STD_LOGIC_VECTOR(2 DOWNTO 0)  := "000";    -- Encoded PCIe link state

     -- Virtex6 PM
     -------------
      CFG_PM_SEND_PME_TO        : IN STD_LOGIC                     := '0';              -- PM send PME turnoff msg
      CFG_PMCSR_POWERSTATE      : IN STD_LOGIC_VECTOR(1 DOWNTO 0)  := "00";             -- PMCSR power state
      TRN_RDLLP_DATA            : IN STD_LOGIC_VECTOR(31 DOWNTO 0) := x"00000000";      -- RX DLLP data
      TRN_RDLLP_SRC_RDY         : IN STD_LOGIC                     := '0';              -- RX DLLP source ready

     -- Virtex6/Spartan6 PM
     -------------
      CFG_TO_TURNOFF            : IN STD_LOGIC                     := '0';    -- Turnoff request
      CFG_TURNOFF_OK            : OUT STD_LOGIC                    ;--:= '0';    -- Turnoff grant

     -- System
     -------------
      TREADY_THRTL              : OUT STD_LOGIC                    ;--:= '0';   -- TREADY to pipeline
      USER_CLK                  : IN STD_LOGIC                     := '0';   -- user clock from block
      USER_RST                  : IN STD_LOGIC                     := '0'    -- user reset from block
   );
END axi_basic_tx_thrtl_ctl;

ARCHITECTURE trans OF axi_basic_tx_thrtl_ctl IS

   function tbuf_av_min_fn (
     constant wdt   : integer)
     return integer is
      variable buf_min : integer := 1;
   begin  -- tbuf_av_min_fn
 
     if (wdt = 128) then
       buf_min := 5;
     elsif (wdt = 64) then
       buf_min := 1;
     else
       buf_min := 0;
     end if;
     return buf_min;
   end tbuf_av_min_fn;

   function tbuf_gap_time_fn (
     constant wdt   : integer)
     return integer is
      variable gap_time : integer := 1;
   begin  -- tbuf_gap_time_fn
 
     if (wdt = 128) then
       gap_time := 4;
     else
       gap_time := 1;
     end if;
     return gap_time;
   end tbuf_gap_time_fn;

   -- Thrtl user when TBUF hits this val
   CONSTANT TBUF_AV_MIN               : INTEGER :=  tbuf_av_min_fn(C_DATA_WIDTH);

   -- Pause user when TBUF hits this val
   CONSTANT TBUF_AV_GAP               : INTEGER :=  TBUF_AV_MIN + 1;

   -- GAP pause time - the latency from the time a packet is accepted on the TRN
   -- interface to the time trn_tbuf_av from the Block will decrement.
   CONSTANT TBUF_GAP_TIME             : INTEGER :=  tbuf_gap_time_fn(C_DATA_WIDTH);

   -- Latency time from when tcfg_gnt is asserted to when PCIe block will throttle
   CONSTANT TCFG_LATENCY_TIME         : INTEGER :=  2;

   CONSTANT LINKSTATE_L0              : INTEGER := 0;
   CONSTANT LINKSTATE_PPM_L1          : INTEGER := 1;
   CONSTANT LINKSTATE_PPM_L1_TRANS    : INTEGER := 5;
   CONSTANT LINKSTATE_PPM_L23R_TRANS  : INTEGER := 6;
   CONSTANT PM_ENTER_L1               : INTEGER := 32;
   CONSTANT POWERSTATE_D0             : INTEGER := 0;

   SIGNAL lnk_up_thrtl           : STD_LOGIC;
   SIGNAL lnk_up_trig            : STD_LOGIC;
   SIGNAL lnk_up_exit            : STD_LOGIC;
   SIGNAL tbuf_av_min_thrtl      : STD_LOGIC;
   SIGNAL tbuf_av_min_trig       : STD_LOGIC;
   SIGNAL tbuf_av_gap_thrtl      : STD_LOGIC;
   SIGNAL tbuf_gap_cnt           : STD_LOGIC_VECTOR(2 DOWNTO 0);
   SIGNAL tbuf_gap_cnt_t         : STD_LOGIC_VECTOR(2 DOWNTO 0);
   SIGNAL tbuf_av_gap_trig       : STD_LOGIC;
   SIGNAL tbuf_av_gap_exit       : STD_LOGIC;
   SIGNAL gap_trig_tlast         : STD_LOGIC;
   SIGNAL gap_trig_tlast_1       : STD_LOGIC;
   SIGNAL gap_trig_decr          : STD_LOGIC;
   SIGNAL gap_trig_decr_1        : STD_LOGIC;
   SIGNAL gap_trig_decr_2        : STD_LOGIC;
   SIGNAL tbuf_av_d              : STD_LOGIC_VECTOR(5 DOWNTO 0);
   SIGNAL tcfg_req_thrtl         : STD_LOGIC;
   SIGNAL tcfg_req_cnt           : STD_LOGIC_VECTOR(1 DOWNTO 0);
   SIGNAL trn_tdst_rdy_d         : STD_LOGIC;
   SIGNAL tcfg_req_trig          : STD_LOGIC;
   SIGNAL tcfg_req_exit          : STD_LOGIC;
   SIGNAL pre_throttle           : STD_LOGIC;
   SIGNAL reg_throttle           : STD_LOGIC;
   SIGNAL exit_crit              : STD_LOGIC;
   SIGNAL reg_tcfg_gnt           : STD_LOGIC;
   SIGNAL trn_tcfg_req_d         : STD_LOGIC;
   SIGNAL tcfg_gnt_pending       : STD_LOGIC;
   SIGNAL wire_to_turnoff        : STD_LOGIC;
   SIGNAL reg_turnoff_ok         : STD_LOGIC;
   SIGNAL tready_thrtl_mux       : STD_LOGIC;
   SIGNAL ppm_L1_thrtl           : STD_LOGIC;
   SIGNAL ppm_L1_trig            : STD_LOGIC;
   SIGNAL ppm_L1_exit            : STD_LOGIC;
   SIGNAL cfg_pcie_link_state_d  : STD_LOGIC_VECTOR(2 DOWNTO 0);
   SIGNAL trn_rdllp_src_rdy_d    : STD_LOGIC;
   SIGNAL ppm_L23_thrtl          : STD_LOGIC;
   SIGNAL ppm_L23_trig           : STD_LOGIC;
   SIGNAL cfg_turnoff_ok_pending : STD_LOGIC;
   SIGNAL reg_tlast              : STD_LOGIC;
   SIGNAL cur_state              : STD_LOGIC;
   SIGNAL next_state             : STD_LOGIC;

   SIGNAL reg_axi_in_pkt         : STD_LOGIC;
   SIGNAL axi_in_pkt             : STD_LOGIC;
   SIGNAL axi_pkt_ending         : STD_LOGIC;
   SIGNAL axi_throttled          : STD_LOGIC;
   SIGNAL axi_thrtl_ok           : STD_LOGIC;
   SIGNAL tx_ecrc_pause          : STD_LOGIC;

   SIGNAL gap_trig_tcfg          : STD_LOGIC;
   SIGNAL reg_to_turnoff         : STD_LOGIC;
   SIGNAL reg_tx_ecrc_pkt        : STD_LOGIC;

   SIGNAL tx_ecrc_pkt            : STD_LOGIC;
   SIGNAL packet_fmt             : STD_LOGIC_VECTOR(1 DOWNTO 0);
   SIGNAL packet_td              : STD_LOGIC;
   SIGNAL header_len             : STD_LOGIC_VECTOR(2 DOWNTO 0);
   SIGNAL payload_len            : STD_LOGIC_VECTOR(9 DOWNTO 0);
   SIGNAL packet_len             : STD_LOGIC_VECTOR(13 DOWNTO 0);
   SIGNAL pause_needed           : STD_LOGIC;

   -- Declare intermediate signals for referenced outputs
   SIGNAL trn_tcfg_gnt_xhdl2     : STD_LOGIC;
   SIGNAL cfg_turnoff_ok_xhdl0   : STD_LOGIC;
   SIGNAL tready_thrtl_xhdl1     : STD_LOGIC;
--   TYPE T_STATE is  (IDLE_A,THROTTLE);
--   SIGNAL CUR_STATE_A, NEXT_STATE_A : T_STATE;
   SIGNAL CUR_STATE_A, NEXT_STATE_A : STD_LOGIC;

   constant IDLE : std_logic := '0';
   constant THROTTLE : std_logic := '1';

BEGIN
   -- Drive referenced outputs
   TRN_TCFG_GNT     <= trn_tcfg_gnt_xhdl2;
   CFG_TURNOFF_OK   <= cfg_turnoff_ok_xhdl0;
   TREADY_THRTL     <= tready_thrtl_xhdl1;
    --------------------------------------------------------------------------------
    -- THROTTLE REASON: PCIe link is down                                         --
    --   - When to throttle: trn_lnk_up deasserted                                --
    --   - When to stop: trn_tdst_rdy assesrted                                   --
    --------------------------------------------------------------------------------
   lnk_up_trig      <= NOT(TRN_LNK_UP);
   lnk_up_exit      <= TRN_TDST_RDY;

   PROCESS (USER_CLK)
   BEGIN
           IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                   IF (USER_RST = '1') THEN
                           lnk_up_thrtl <= '1'  AFTER (TCQ)*1 ps;
                   ELSE
                           IF (lnk_up_trig = '1') THEN
                                   lnk_up_thrtl <= '1'   AFTER (TCQ)*1 ps;
                           ELSIF (lnk_up_exit = '1') THEN
                                   lnk_up_thrtl <= '0'   AFTER (TCQ)*1 ps;
                           END IF;
                   END IF;
           END IF;
   END PROCESS;

  --------------------------------------------------------------------------------
  -- THROTTLE REASON: Transmit buffers depleted                                 --
  --   - When to throttle: trn_tbuf_av falls to 0                               --
  --   - When to stop: trn_tbuf_av rises above 0 again                          --
  --------------------------------------------------------------------------------
   tbuf_av_min_trig <= '1' WHEN (TRN_TBUF_AV <= TBUF_AV_MIN) ELSE '0';

   PROCESS (USER_CLK)
   BEGIN
      IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
              IF (USER_RST = '1') THEN
                      tbuf_av_min_thrtl <= '0'   AFTER (TCQ)*1 ps;
              ELSE
                      IF (tbuf_av_min_trig = '1') THEN
                              tbuf_av_min_thrtl <= '1'  AFTER (TCQ)*1 ps;
                      -- The exit condition for tbuf_av_min_thrtl is !tbuf_av_min_trig
                      ELSE
                              tbuf_av_min_thrtl <= '0'  AFTER (TCQ)*1 ps;
                      END IF;
              END IF;
      END IF;
   END PROCESS;

   ------------------------------------------------------------------------------//
   -- THROTTLE REASON: Transmit buffers getting low                              //
   --   - When to throttle: trn_tbuf_av falls below "gap" threshold TBUF_AV_GAP  //
   --   - When to stop: after TBUF_GAP_TIME cycles elapse                        //
   --                                                                            //
   -- If we're about to run out of transmit buffers, throttle the user for a     //
   -- few clock cycles to give the PCIe block time to catch up. This is          //
   -- needed to compensate for latency in decrementing trn_tbuf_av in the PCIe   //
   -- Block transmit path.                                                       //
   ------------------------------------------------------------------------------//

   -- Detect two different scenarios for buffers getting low:
   -- 1) If we see a TLAST. a new packet has been inserted into the buffer, and
   --    we need to pause and let that packet "soak in"
   gap_trig_tlast_1 <=  '1' WHEN (TRN_TBUF_AV <= TBUF_AV_GAP) ELSE '0';
   gap_trig_tlast   <=  (gap_trig_tlast_1 AND S_AXIS_TX_TVALID AND tready_thrtl_xhdl1 AND S_AXIS_TX_TLAST );

   -- 2) Any time tbug_avail decrements to the TBUF_AV_GAP threshold, we need to
   --    pause and make sure no other packets are about to soak in and cause the
   --    buffer availability to drop further.
   gap_trig_decr_1    <=  '1' WHEN ( TRN_TBUF_AV = TBUF_AV_GAP) ELSE '0' ;
   gap_trig_decr_2  <=  '1' WHEN (tbuf_av_d = TBUF_AV_GAP + 1) ELSE '0' ;

   gap_trig_decr    <= ( gap_trig_decr_1 AND gap_trig_decr_2);

   gap_trig_tcfg    <= ((tcfg_req_thrtl AND tcfg_req_exit));
   tbuf_av_gap_trig <= (gap_trig_tlast OR gap_trig_decr OR gap_trig_tcfg) ;
   tbuf_av_gap_exit <= '1' WHEN (tbuf_gap_cnt = "000") ELSE '0' ;
   tbuf_gap_cnt_t   <= "100" WHEN (C_DATA_WIDTH = 128) ELSE "001";
   PROCESS (USER_CLK)
   BEGIN
           IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                   IF (USER_RST = '1') THEN
                           tbuf_av_gap_thrtl <= '0'       AFTER (TCQ)*1 ps;
                           tbuf_gap_cnt      <= "000"     AFTER (TCQ)*1 ps;
                           tbuf_av_d         <= "000000"  AFTER (TCQ)*1 ps;
                   ELSE
                           IF (tbuf_av_gap_trig = '1') THEN
                                   tbuf_av_gap_thrtl <= '1'   AFTER (TCQ)*1 ps;
                           ELSIF (tbuf_av_gap_exit = '1') THEN
                                   tbuf_av_gap_thrtl <= '0'   AFTER (TCQ)*1 ps;
                           END IF;
                           -- tbuf gap counter:
                           -- This logic controls the length of the throttle condition when tbufs are
                           -- getting low.
                           IF (tbuf_av_gap_thrtl = '1' AND (CUR_STATE_A = THROTTLE)) THEN
                                   IF (tbuf_gap_cnt > "000") THEN
                                           tbuf_gap_cnt <= tbuf_gap_cnt - "001"  AFTER (TCQ)*1 ps;
                                   END IF;
                           ELSE
                                   tbuf_gap_cnt <= tbuf_gap_cnt_t AFTER (TCQ)*1 ps;
                           END IF;
                           tbuf_av_d <= TRN_TBUF_AV  AFTER (TCQ)*1 ps;
                   END IF;
           END IF;
   END PROCESS;

   ------------------------------------------------------------------------------
   -- THROTTLE REASON: Block needs to send a CFG response
   --   - When to throttle: trn_tcfg_req and user_tcfg_gnt asserted
   --   - When to stop: after trn_tdst_rdy transitions to unasserted
   --
   -- If the block needs to send a response to a CFG packet, this will cause
   -- the subsequent deassertion of trn_tdst_rdy. When the user design permits,
   -- grant permission to the block to service request and throttle the user.
   ------------------------------------------------------------------------------

   tcfg_req_trig <= (TRN_TCFG_REQ AND reg_tcfg_gnt);
   tcfg_req_exit <= '1' WHEN (tcfg_req_cnt = "00" AND trn_tdst_rdy_d = '0' AND  TRN_TDST_RDY ='1') ELSE '0';

   PROCESS (USER_CLK)
   BEGIN
      IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
              IF (USER_RST = '1') THEN
                      tcfg_req_thrtl   <= '0'   AFTER (TCQ)*1 ps;
                      trn_tcfg_req_d   <= '0'   AFTER (TCQ)*1 ps;
                      trn_tdst_rdy_d   <= '1'   AFTER (TCQ)*1 ps;
                      reg_tcfg_gnt     <= '0'   AFTER (TCQ)*1 ps;
                      tcfg_req_cnt     <= "00"  AFTER (TCQ)*1 ps;
                      tcfg_gnt_pending <= '0'   AFTER (TCQ)*1 ps;
              ELSE
                      IF (tcfg_req_trig = '1') THEN
                              tcfg_req_thrtl <= '1'  AFTER (TCQ)*1 ps;
                      ELSIF (tcfg_req_exit = '1') THEN
                              tcfg_req_thrtl <= '0'  AFTER (TCQ)*1 ps;
                      END IF;
                      -- We need to wait the appropriate amount of time for the tcfg_gnt to
                      -- "sink in" to the PCIe block. After that, we know that the PCIe block will
                      -- not reassert trn_tdst_rdy until the CFG request has been serviced. If a
                      -- new request is being service (trn_tcfg_gnt == 1), then reset the timer.
                      IF ((NOT(trn_tcfg_req_d = '1' ) AND TRN_TCFG_REQ = '1' ) OR tcfg_gnt_pending = '1') THEN
                              -- As TCFG_LATENCY_TIME value is 2
                              tcfg_req_cnt <= "10"  AFTER (TCQ)*1 ps;
                      ELSE
                              IF (tcfg_req_cnt > "00") THEN
                                      tcfg_req_cnt <= tcfg_req_cnt - "01"  AFTER (TCQ)*1 ps;
                              END IF;
                      END IF;
                      -- Make sure trn_tcfg_gnt pulses once for one clock cycle for every
                      -- cfg packet request.
                      IF (TRN_TCFG_REQ = '1' AND NOT(trn_tcfg_req_d = '1' )) THEN
                              tcfg_gnt_pending   <= '1' AFTER (TCQ)*1 ps;
                      ELSIF (trn_tcfg_gnt_xhdl2 = '1') THEN
                              tcfg_gnt_pending   <= '0' AFTER (TCQ)*1 ps;
                      END IF;

                      trn_tcfg_req_d   <= TRN_TCFG_REQ   AFTER (TCQ)*1 ps;
                      trn_tdst_rdy_d   <= TRN_TDST_RDY   AFTER (TCQ)*1 ps;
                      reg_tcfg_gnt     <= USER_TCFG_GNT  AFTER (TCQ)*1 ps;
              END IF;
      END IF;
   END PROCESS;

   ------------------------------------------------------------------------------
   -- THROTTLE REASON: Block needs to transition to low power state PPM L1
   --   - When to throttle: appropriate low power state signal asserted
   --     (architecture dependent)
   --   - When to stop: cfg_pcie_link_state goes to proper value (C_ROOT_PORT
   --     dependent)
   --
   -- If the block needs to transition to PM state PPM L1, we need to finish
   -- up what we're doing and throttle immediately.
   ------------------------------------------------------------------------------
   xhdl3 : IF ((C_FAMILY = "X7") AND (C_ROOT_PORT)) GENERATE
           -- PPM L1 signals for 7 Series in RC mode
           ppm_L1_trig <=  '1' WHEN ((cfg_pcie_link_state_d = "000") AND (CFG_PCIE_LINK_STATE = "101")) ELSE '0';
           ppm_L1_exit <= '1' WHEN (CFG_PCIE_LINK_STATE = "001") ELSE '0';
   END GENERATE;

  -- PPM L1 signals for 7 Series in EP mode
   xhdl4 : IF (NOT((C_FAMILY = ("X7")) AND (C_ROOT_PORT))) GENERATE
           xhdl5 : IF ((C_FAMILY = ("X7")) AND (NOT(C_ROOT_PORT))) GENERATE
                   ppm_L1_trig <= '1' WHEN ((cfg_pcie_link_state_d = "000") AND (CFG_PCIE_LINK_STATE = "101")) ELSE '0';
                   ppm_L1_exit <= '1' WHEN (CFG_PCIE_LINK_STATE = "000") ELSE '0';
           END GENERATE;
      -- PPM L1 signals for V6 in RC mode
           xhdl6 : IF (NOT((C_FAMILY = ("X7")) AND (NOT(C_ROOT_PORT)))) GENERATE
                   xhdl7 : IF ((C_FAMILY = ("V6")) AND (C_ROOT_PORT)) GENERATE
                           ppm_L1_trig <= '1' WHEN ((TRN_RDLLP_DATA(31 DOWNTO 24) = x"20") AND TRN_RDLLP_SRC_RDY = '1' AND trn_rdllp_src_rdy_d = '0') ELSE '0';
                           ppm_L1_exit <= '1' WHEN (CFG_PCIE_LINK_STATE = "001") ELSE '0';
                   END GENERATE;
         -- PPM L1 signals for V6 in EP mode
                   xhdl8 : IF (NOT((C_FAMILY = ("V6")) AND (C_ROOT_PORT))) GENERATE
                           xhdl9 : IF ((C_FAMILY = ("V6")) AND (NOT(C_ROOT_PORT))) GENERATE
                                   ppm_L1_trig <= '1' WHEN (CFG_PMCSR_POWERSTATE /= "000") ELSE '0';
                                   ppm_L1_exit <= '1' WHEN (CFG_PCIE_LINK_STATE = "000") ELSE '0';
                           END GENERATE;
            -- PPM L1 detection not supported for S6
                           xhdl10 : IF (NOT((C_FAMILY = ("V6")) AND (NOT(C_ROOT_PORT)))) GENERATE
                                   ppm_L1_trig <= '0';
                                   ppm_L1_exit <= '1';
                           END GENERATE;
                   END GENERATE;
           END GENERATE;
   END GENERATE;

   PROCESS (USER_CLK)
   BEGIN
      IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
              IF (USER_RST = '1') THEN
                      ppm_L1_thrtl          <= '0'   AFTER (TCQ)*1 ps;
                      cfg_pcie_link_state_d <= "000" AFTER (TCQ)*1 ps;
                      trn_rdllp_src_rdy_d   <= '0'   AFTER (TCQ)*1 ps;
              ELSE
                      IF (ppm_L1_trig = '1') THEN
                              ppm_L1_thrtl  <= '1'   AFTER (TCQ)*1 ps;
                      ELSIF (ppm_L1_exit = '1') THEN
                              ppm_L1_thrtl  <= '0'   AFTER (TCQ)*1 ps;
                      END IF;
                      cfg_pcie_link_state_d <= CFG_PCIE_LINK_STATE  AFTER (TCQ)*1 ps;
                      trn_rdllp_src_rdy_d <= TRN_RDLLP_SRC_RDY      AFTER (TCQ)*1 ps;
              END IF;
      END IF;
   END PROCESS;

    ------------------------------------------------------------------------------
    -- THROTTLE REASON: Block needs to transition to low power state PPM L2/3
    --   - When to throttle: appropriate PM signal indicates a transition to
    --     L2/3 is pending or in progress (family and role dependent)
    --   - When to stop: never (the only path out of L2/3 is a full reset)
    --
    -- If the block needs to transition to PM state PPM L2/3, we need to finish
    -- up what we're doing and throttle when the user gives permission.
    ------------------------------------------------------------------------------
    -- PPM L2/3 signals for 7 Series in RC mode
   xhdl11 : IF ((C_FAMILY = ("X7")) AND (C_ROOT_PORT)) GENERATE
           ppm_L23_trig <= '1' WHEN (cfg_pcie_link_state_d = "110") ELSE '0';
           wire_to_turnoff <= '0';
   END GENERATE;

   -- PPM L2/3 signals for V6 in RC mode
   xhdl12 : IF (NOT((C_FAMILY = ("X7")) AND (C_ROOT_PORT))) GENERATE
           xhdl13 : IF ((C_FAMILY = ("V6")) AND (C_ROOT_PORT)) GENERATE
                   ppm_L23_trig <= CFG_PM_SEND_PME_TO;
                   wire_to_turnoff <= '0';
           END GENERATE;
            -- PPM L2/3 signals in EP mode
           xhdl14 : IF (NOT((C_FAMILY = ("V6")) AND (C_ROOT_PORT))) GENERATE
                   ppm_L23_trig <= (wire_to_turnoff AND reg_turnoff_ok);
                    -- PPM L2/3 signals for 7 Series in EP mode
                    -- For 7 Series, cfg_to_turnoff pulses once when a turnoff request is
                    -- outstanding, so we need a "sticky" register that grabs the request.
                   xhdl15 : IF (C_FAMILY = ("X7")) GENERATE
                           PROCESS (USER_CLK)
                           BEGIN
                                   IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                                           IF (USER_RST = '1') THEN
                                                   reg_to_turnoff <= '0' AFTER (TCQ)*1 ps;
                                           ELSE
                                                   IF (CFG_TO_TURNOFF = '1') THEN
                                                           reg_to_turnoff <= '1' AFTER (TCQ)*1 ps;
                                                   END IF;
                                           END IF;
                                   END IF;
                           END PROCESS;
                           wire_to_turnoff <= reg_to_turnoff;
                   END GENERATE;
                    -- PPM L2/3 signals for V6/S6 in EP mode
                    -- In V6 and S6, the to_turnoff signal asserts and remains asserted until
                    -- turnoff_ok is asserted, so a sticky reg is not necessary.
                   xhdl16 : IF (NOT(C_FAMILY = ("X7"))) GENERATE
                           wire_to_turnoff <= CFG_TO_TURNOFF;
                   END GENERATE;
                   PROCESS (USER_CLK)
                   BEGIN
                           IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                                   IF (USER_RST = '1') THEN
                                           reg_turnoff_ok <= '0' AFTER (TCQ)*1 ps;
                                   ELSE
                                           reg_turnoff_ok <= USER_TURNOFF_OK AFTER (TCQ)*1 ps;
                                   END IF;
                           END IF;
                   END PROCESS;
           END GENERATE;
   END GENERATE;

   PROCESS (USER_CLK)
   BEGIN
           IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                   IF (USER_RST = '1') THEN
                           ppm_L23_thrtl <= '0' AFTER (TCQ)*1 ps;
                           cfg_turnoff_ok_pending <= '0' AFTER (TCQ)*1 ps;
                   ELSE
                   -- Make sure cfg_turnoff_ok pulses once for one clock cycle for every
                   -- turnoff request.
                           IF (ppm_L23_trig = '1') THEN
                                   ppm_L23_thrtl <= '1' AFTER (TCQ)*1 ps;
                           END IF;
                           IF (ppm_L23_trig = '1' AND (ppm_L23_thrtl = '0')) THEN
                                   cfg_turnoff_ok_pending <= '1' AFTER (TCQ)*1 ps;
                           ELSIF (cfg_turnoff_ok_xhdl0 = '1') THEN
                                   cfg_turnoff_ok_pending <= '0' AFTER (TCQ)*1 ps;
                           END IF;
                   END IF;
           END IF;
   END PROCESS;

   --------------------------------------------------------------------------------
   -- Create axi_thrtl_ok. This signal determines if it's OK to throttle the     --
   -- user design on the AXI interface. Since TREADY is registered, this signal  --
   -- needs to assert on the cycle ~before~ we actually intend to throttle.      --
   -- The only time it's OK to throttle when TVALID is asserted is on the first  --
   -- beat of a new packet. Therefore, assert axi_thrtl_ok if one of the         --
   -- is true:                                                                   --
   --    1) The user is not in a packet and is not starting one                  --
   --    2) The user is just finishing a packet                                  --
   --    3) We're already throttled, so it's OK to continue throttling           --
   --------------------------------------------------------------------------------

   PROCESS (USER_CLK)
   BEGIN
      IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
              IF (USER_RST = '1') THEN
                      reg_axi_in_pkt <= '0' AFTER (TCQ)*1 ps;
              ELSE
                      IF (S_AXIS_TX_TVALID = '1' AND S_AXIS_TX_TLAST  = '1') THEN
                              reg_axi_in_pkt <= '0' AFTER (TCQ)*1 ps;
                      ELSIF (tready_thrtl_xhdl1  = '1'  AND S_AXIS_TX_TVALID  = '1') THEN
                              reg_axi_in_pkt <= '1' AFTER (TCQ)*1 ps;
                      END IF;
              END IF;
      END IF;
   END PROCESS;

   axi_in_pkt     <= (S_AXIS_TX_TVALID OR reg_axi_in_pkt);
   axi_pkt_ending <= (S_AXIS_TX_TVALID AND S_AXIS_TX_TLAST);
   axi_throttled  <= NOT(tready_thrtl_xhdl1);
   axi_thrtl_ok   <= (NOT(axi_in_pkt) OR axi_pkt_ending OR axi_throttled);
   --------------------------------------------------------------------------------
   -- Throttle CTL State Machine:                                                --
   -- Throttle user design when a throttle trigger (or triggers) occur.          --
   -- Keep user throttled until all exit criteria have been met.                 --
   --------------------------------------------------------------------------------

   -- Immediate throttle signal. Used to "pounce" on a throttle opportunity when
   -- we're seeking one
   pre_throttle <= (tbuf_av_min_trig OR tbuf_av_gap_trig OR lnk_up_trig OR tcfg_req_trig OR ppm_L1_trig OR ppm_L23_trig);

   -- Registered throttle signals. Used to control throttle state machine
   reg_throttle <= (tbuf_av_min_thrtl OR tbuf_av_gap_thrtl OR lnk_up_thrtl OR tcfg_req_thrtl OR ppm_L1_thrtl OR ppm_L23_thrtl);
   exit_crit <= (NOT(tbuf_av_min_thrtl) AND NOT(tbuf_av_gap_thrtl) AND (NOT(lnk_up_thrtl)) AND (NOT(tcfg_req_thrtl)) AND (NOT(ppm_L1_thrtl)) AND (NOT(ppm_L23_thrtl)));

   PROCESS (CUR_STATE_A,reg_throttle, axi_thrtl_ok, tcfg_req_thrtl, tcfg_gnt_pending, cfg_turnoff_ok_pending, pre_throttle, exit_crit)
   BEGIN
      CASE CUR_STATE_A IS
         -- IDLE: in this state we're waiting for a trigger event to occur. As
         -- soon as an event occurs and the user isn't transmitting a packet, we
         -- throttle the PCIe block and the user and next state is THROTTLE.
              WHEN (IDLE) =>
                      IF(reg_throttle = '1' AND axi_thrtl_ok = '1') THEN
                              tready_thrtl_mux <= '0';
                              NEXT_STATE_A   <= (THROTTLE);
                              -- Assert appropriate grant signal depending on the throttle type.
                              IF (tcfg_req_thrtl  = '1') THEN
                                      trn_tcfg_gnt_xhdl2    <= '1';   -- For cfg request, grant the request
                                      cfg_turnoff_ok_xhdl0  <= '0'; --
                              ELSIF (ppm_L23_thrtl = '1') THEN
                                      trn_tcfg_gnt_xhdl2    <= '0';    --
                                      cfg_turnoff_ok_xhdl0  <= '1';  -- For PM request, permit transition
                              ELSE
                                      trn_tcfg_gnt_xhdl2    <= '0';  -- Otherwise do nothing
                                      cfg_turnoff_ok_xhdl0  <= '0';--
                              END IF;
                      ELSE
                             -- If there's not throttle event, do nothing
                             -- Throttle user as soon as possible
                              tready_thrtl_mux <= (NOT((axi_thrtl_ok AND pre_throttle)));
                              NEXT_STATE_A <= IDLE;
                              trn_tcfg_gnt_xhdl2 <= '0';
                              cfg_turnoff_ok_xhdl0 <= '0';
                      END IF;
              -- THROTTLE: in this state the user is throttle and we're waiting for
              -- exit criteria, which tells us that the throttle event is over. When
              -- the exit criteria is satisfied, de-throttle the user and next state
              WHEN (THROTTLE) =>
                      IF (exit_crit = '1') THEN
                              -- Dethrottle user
                              tready_thrtl_mux <= (NOT(pre_throttle));
                              NEXT_STATE_A   <= IDLE;
                      ELSE
                              -- Throttle user
                              tready_thrtl_mux <= '0';
                              NEXT_STATE_A   <= (THROTTLE);
                      END IF;
                       -- Assert appropriate grant signal depending on the throttle type.
                      IF (tcfg_req_thrtl = '1' AND tcfg_gnt_pending = '1') THEN
                              trn_tcfg_gnt_xhdl2     <= '1';   -- For cfg request, grant the request
                              cfg_turnoff_ok_xhdl0   <= '0'; --
                      ELSIF (cfg_turnoff_ok_pending = '1') THEN
                              trn_tcfg_gnt_xhdl2     <= '0';   --
                              cfg_turnoff_ok_xhdl0   <= '1'; -- For PM request, permit transition
                      ELSE
                              trn_tcfg_gnt_xhdl2     <= '0';   -- Otherwise do nothing
                              cfg_turnoff_ok_xhdl0   <= '0'; --
                      END IF;
              WHEN OTHERS =>
                      tready_thrtl_mux     <= '0';
                      NEXT_STATE_A         <= (IDLE);
                      trn_tcfg_gnt_xhdl2   <= '0' ;
                      cfg_turnoff_ok_xhdl0 <= '0';
      END CASE;
   END PROCESS;


-- Synchronous logic
   PROCESS (USER_CLK)
   BEGIN
      IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
              IF (USER_RST = '1') THEN
                      -- Throttle user by default until link comes up
                      CUR_STATE_A        <= (THROTTLE) AFTER (TCQ)*1 ps;
                      reg_tlast          <= '0' AFTER (TCQ)*1 ps;
                      tready_thrtl_xhdl1 <= '0' AFTER (TCQ)*1 ps;
              ELSE
                      CUR_STATE_A        <= NEXT_STATE_A AFTER (TCQ)*1 ps;
                      tready_thrtl_xhdl1 <= (tready_thrtl_mux AND NOT(tx_ecrc_pause)) AFTER (TCQ)*1 ps;
                      reg_tlast          <= S_AXIS_TX_TLAST AFTER (TCQ)*1 ps;
              END IF;
      END IF;
   END PROCESS;


   -- For X7, the PCIe block will generate the ECRC for a packet if trn_tecrc_gen
   -- is asserted at SOF. In this case, the Block needs an extra data beat to
   -- calculate the ECRC, but only if the following conditions are met:
   --  1) there is no empty DWORDS at the end of the packet
   --     (i.e. packet length % C_DATA_WIDTH == 0)
   --
   --  2) There isn't a ECRC in the TLP already, as indicated by the TD bit in the
   --     TLP header
   --
   -- If both conditions are met, the Block will stall the TRN interface for one
   -- data beat after EOF. We need to predict this stall and preemptively stall the
   -- User for one beat.
   xhdl17 : IF (C_FAMILY = ("X7")) GENERATE

      -- Grab necessary packet fields
      packet_fmt <= S_AXIS_TX_TDATA(30 DOWNTO 29);
      packet_td  <= S_AXIS_TX_TDATA(15);

      -- Calculate total packet length
      header_len <= "100" WHEN packet_fmt(0) = '1' ELSE "011";
      payload_len <= S_AXIS_TX_TDATA(9 DOWNTO 0) WHEN packet_fmt(1) = '1' ELSE "0000000000";
      packet_len  <= ("0000000000" & header_len) + ("0000" & payload_len);

      -- Determine if an ECRC pause is needed
      PACKET_LEN_CHECK_128 : IF (C_DATA_WIDTH = 128) GENERATE
              pause_needed <= '1' WHEN (packet_len(1 DOWNTO 0) = "00" AND packet_td = '0') ELSE '0';
      END GENERATE;

      PACKET_LEN_CHECK_64 : IF (C_DATA_WIDTH /= 128) GENERATE
              pause_needed <= '1' WHEN (packet_len(0) = '0' AND packet_td = '0') ELSE '0';
      END GENERATE;

      -- Create flag to alert TX pipeline to insert a stall
      tx_ecrc_pkt <= S_AXIS_TX_TUSER(0) AND pause_needed AND tready_thrtl_xhdl1 AND S_AXIS_TX_TVALID AND not(reg_axi_in_pkt);

      PROCESS (USER_CLK)
      BEGIN
              IF (USER_CLK'EVENT AND USER_CLK = '1') THEN
                      IF (USER_RST = '1') THEN
                              reg_tx_ecrc_pkt <= '0' AFTER (TCQ)*1 ps;
                      ELSE
                              IF (tx_ecrc_pkt  = '1' AND S_AXIS_TX_TLAST = '0') THEN
                                      reg_tx_ecrc_pkt <= '1' AFTER (TCQ)*1 ps;
                              ELSIF (tready_thrtl_xhdl1 = '1' AND S_AXIS_TX_TVALID = '1' AND S_AXIS_TX_TLAST = '1') THEN
                                      reg_tx_ecrc_pkt <= '0' AFTER (TCQ)*1 ps;
                              END IF;
                      END IF;
              END IF;
      END PROCESS;

--      tx_ecrc_pkt <= (S_AXIS_TX_TUSER(0) AND tready_thrtl_xhdl1 AND S_AXIS_TX_TVALID AND (NOT(reg_axi_in_pkt)));
      tx_ecrc_pause <= (((tx_ecrc_pkt OR reg_tx_ecrc_pkt) AND S_AXIS_TX_TLAST AND S_AXIS_TX_TVALID AND tready_thrtl_xhdl1));
   END GENERATE;

   xhdl18 : IF (NOT(C_FAMILY = ("X7"))) GENERATE
           tx_ecrc_pause <= '0';
   END GENERATE;
END trans;
