-------------------------------------------------------------------------------
-- Definition of Ports
-- DIN : Input data bus for the fifo.
-- DOUT : Output data bus for the fifo.
-- AINIT : Asynchronous Reset for the fifo.
-- WR_EN : Write enable signal.
-- WR_CLK : Write Clock.
-- FULL : Full signal.
-- ALMOST_FULL : One space left.
-- WR_ACK : Last write acknowledged.
-- WR_ERR : Last write rejected.
-- WR_COUNT : Number of data words in fifo(synchronous to WR_CLK)
-- Rd_EN : Read enable signal.
-- RD_CLK : Read Clock.
-- EMPTY : Empty signal.
-- ALMOST_EMPTY: One sapce left
-- VALID : Last read acknowledged.
-- RD_ERR : Last read rejected.
-- RD_COUNT : Number of data words in fifo(synchronous to RD_CLK)
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- Architecture Heading
-------------------------------------------------------------------------------
ARCHITECTURE behavioral OF fifo_generator_v2_1_bhv_as IS



-------------------------------------------------------------------------------
-- FUNCTION actual_fifo_depth
--
-- Returns the actual depth of the FIFO (may differ from what the user specified)
--
-- The FIFO depth is always represented as 2^n (16,32,64,128,256)
-- However, the ACTUAL fifo depth may be 2^n or (2^n - 1) depending on certain
-- options. This function returns the ACTUAL depth of the fifo, as seen by
-- the user.
--
-- The FIFO depth remains as 2^n when any of the following conditions are true:
-- *C_PRELOAD_REGS=1 AND C_PRELOAD_LATENCY=1 (preload output register adds 1
-- word of depth to the FIFO)
-------------------------------------------------------------------------------
  FUNCTION actual_fifo_depth(c_fifo_depth : integer; C_PRELOAD_REGS : integer; C_PRELOAD_LATENCY : integer) RETURN integer IS
  BEGIN
    IF (C_PRELOAD_REGS = 1 AND C_PRELOAD_LATENCY = 1) THEN
      RETURN c_fifo_depth;
    ELSIF (C_PRELOAD_REGS = 1 AND C_PRELOAD_LATENCY = 0) THEN
      RETURN c_fifo_depth-1;
    ELSE
      RETURN c_fifo_depth-1;
    END IF;
  END actual_fifo_depth;

-------------------------------------------------------------------------------
-- Derived Constants
-------------------------------------------------------------------------------
  CONSTANT C_FULL_RESET_VAL        : std_logic := '1';
  CONSTANT C_ALMOST_FULL_RESET_VAL : std_logic := '1';
  CONSTANT C_PROG_FULL_RESET_VAL   : std_logic := '1';

  CONSTANT C_FIFO_WR_DEPTH : integer := actual_fifo_depth(C_WR_DEPTH, C_PRELOAD_REGS, C_PRELOAD_LATENCY);
  CONSTANT C_FIFO_RD_DEPTH : integer := actual_fifo_depth(C_RD_DEPTH, C_PRELOAD_REGS, C_PRELOAD_LATENCY);

  CONSTANT C_SMALLER_PNTR_WIDTH : integer := get_lesser(C_WR_PNTR_WIDTH, C_RD_PNTR_WIDTH);
  CONSTANT C_SMALLER_DEPTH      : integer := get_lesser(C_FIFO_WR_DEPTH, C_FIFO_RD_DEPTH);
  CONSTANT C_SMALLER_DATA_WIDTH : integer := get_lesser(C_DIN_WIDTH, C_DOUT_WIDTH);
  CONSTANT C_LARGER_PNTR_WIDTH  : integer := get_greater(C_WR_PNTR_WIDTH, C_RD_PNTR_WIDTH);
  CONSTANT C_LARGER_DEPTH       : integer := get_greater(C_FIFO_WR_DEPTH, C_FIFO_RD_DEPTH);
  CONSTANT C_LARGER_DATA_WIDTH  : integer := get_greater(C_DIN_WIDTH, C_DOUT_WIDTH);

  --The write depth to read depth ratio is   C_RATIO_W : C_RATIO_R
  CONSTANT C_RATIO_W : integer := if_then_else( (C_WR_DEPTH > C_RD_DEPTH), (C_WR_DEPTH/C_RD_DEPTH), 1);
  CONSTANT C_RATIO_R : integer := if_then_else( (C_RD_DEPTH > C_WR_DEPTH), (C_RD_DEPTH/C_WR_DEPTH), 1);

  CONSTANT C_PROG_FULL_REG  : integer := 0;
  CONSTANT C_PROG_EMPTY_REG : integer := 0;

  CONSTANT counter_depth_wr : integer := C_FIFO_WR_DEPTH + 1;
  CONSTANT counter_depth_rd : integer := C_FIFO_RD_DEPTH + 1;

  
-------------------------------------------------------------------------------
-- Internal Signals
-------------------------------------------------------------------------------
  SIGNAL wr_point : integer   := 0;
  SIGNAL rd_point : integer   := 0;
  SIGNAL rd_reg0  : integer   := 0;
  SIGNAL rd_reg1  : integer   := 0;
  SIGNAL wr_reg0  : integer   := 0;
  SIGNAL wr_reg1  : integer   := 0;
  SIGNAL empty_i  : std_logic := '1';
  SIGNAL empty_q  : std_logic := '1';
  SIGNAL FULL_i   : std_logic := '1';

  SIGNAL rd_rst_i : std_logic;
  SIGNAL rd_rst_int : std_logic;
  SIGNAL rd_rst_reg : std_logic;
  SIGNAL wr_rst_i : std_logic;
  SIGNAL wr_rst_int : std_logic;
  SIGNAL wr_rst_reg : std_logic;

  SIGNAL wr_diff_count   : std_logic_vector(C_WR_PNTR_WIDTH-1 DOWNTO 0)       := (OTHERS => '0');
  SIGNAL rd_diff_count   : std_logic_vector(C_RD_PNTR_WIDTH-1 DOWNTO 0)       := (OTHERS => '0');

  SIGNAL wr_ack_i    : std_logic := '0';
  SIGNAL wr_ack_q    : std_logic := '0';
  SIGNAL wr_ack_q2   : std_logic := '0';
  SIGNAL overflow_i  : std_logic := '0';
  SIGNAL overflow_q  : std_logic := '0';
  SIGNAL overflow_q2 : std_logic := '0';

  SIGNAL valid_i     : std_logic := '0';
  SIGNAL underflow_i : std_logic := '0';

  SIGNAL output_regs_valid : std_logic := '0';

  SIGNAL prog_full_noreg   : std_logic := '1';
  SIGNAL prog_full_reg     : std_logic := '1';
  SIGNAL prog_empty_noreg  : std_logic := '1';
  SIGNAL prog_empty_reg    : std_logic := '1';

-------------------------------------------------------------------------------
-- Linked List types
-------------------------------------------------------------------------------
  TYPE listtyp;
  TYPE listptr IS ACCESS listtyp;
  TYPE listtyp IS RECORD
                    data  : std_logic_vector(C_SMALLER_DATA_WIDTH - 1 DOWNTO 0);
                    older : listptr;
                    newer : listptr;
                  END RECORD;


-------------------------------------------------------------------------------
-- Processes for linked list implementation
-------------------------------------------------------------------------------
  --Create a new linked list
  PROCEDURE newlist (head : INOUT listptr; tail : INOUT listptr) IS
  BEGIN
    head := NULL;
    tail := NULL;
  END;  -- procedure newlist;

  --Add a data element to a linked list
  PROCEDURE add (head : INOUT listptr; tail : INOUT listptr; data : IN std_logic_vector) IS
    VARIABLE oldhead  :       listptr;
    VARIABLE newhead  :       listptr;
  BEGIN
    --Create a pointer to the existing head, if applicable
    IF (head /= NULL) THEN
      oldhead       := head;
    END IF;
    --Create a new node for the list
    newhead         := NEW listtyp;
    --Make the new node point to the old head
    newhead.older   := oldhead;
    --Make the old head point back to the new node (for doubly-linked list)
    IF (head /= NULL) THEN
      oldhead.newer := newhead;
    END IF;
    --Put the data into the new head node
    newhead.data    := data;
    --If the new head we just created is the only node in the list, make the tail point to it
    IF (newhead.older = NULL) THEN
      tail          := newhead;
    END IF;
    --Return the new head pointer
    head            := newhead;
  END;  -- procedure; -- add;


  --Read the data from the tail of the linked list
  PROCEDURE read (tail : INOUT listptr; data : OUT std_logic_vector) IS
  BEGIN
    data := tail.data;
  END;  -- procedure; -- read;


  --Remove the tail from the linked list
  PROCEDURE remove (head : INOUT listptr; tail : INOUT listptr) IS
    VARIABLE oldtail     :       listptr;
    VARIABLE newtail     :       listptr;
  BEGIN
    --Make a copy of the old tail pointer
    oldtail         := tail;
    --If there is no newer node, then set the tail pointer to nothing (list is empty)
    IF (oldtail.newer = NULL) THEN
      newtail       := NULL;
      --otherwise, make the next newer node the new tail, and make it point to nothing older
    ELSE
      newtail       := oldtail.newer;
      newtail.older := NULL;
    END IF;
    --Clean up the memory for the old tail node
    DEALLOCATE(oldtail);
    --If the new tail is nothing, then we have an empty list, and head should also be set to nothing
    IF (newtail = NULL) THEN
      head          := NULL;
    END IF;
    --Return the new tail
    tail            := newtail;
  END;  -- procedure; -- remove;


  --Calculate the size of the linked list
  PROCEDURE sizeof (head : INOUT listptr; size : OUT integer) IS
    VARIABLE curlink     :       listptr;
    VARIABLE tmpsize     :       integer := 0;
  BEGIN
    --If head is null, then there is nothing in the list to traverse
    IF (head /= NULL) THEN
      --start with the head node (which implies at least one node exists)
      curlink                            := head;
      tmpsize                            := 1;
      --Loop through each node until you find the one that points to nothing (the tail)
      WHILE (curlink.older /= NULL) LOOP
        tmpsize                          := tmpsize + 1;
        curlink                          := curlink.older;
      END LOOP;
    END IF;
    --Return the number of nodes
    size                                 := tmpsize;
  END;  -- procedure; -- sizeof;


  -- converts integer to specified length std_logic_vector : dropping least
  -- significant bits if integer is bigger than what can be represented by
  -- the vector
  FUNCTION count( fifo_count    : IN integer;
                  pointer_width : IN integer;
                  counter_width : IN integer
                  ) RETURN std_logic_vector IS
    VARIABLE temp               :    std_logic_vector(pointer_width-1 DOWNTO 0)   := (OTHERS => '0');
    VARIABLE output             :    std_logic_vector(counter_width - 1 DOWNTO 0) := (OTHERS => '0');

  BEGIN

    temp     := CONV_STD_LOGIC_VECTOR(fifo_count, pointer_width);
    IF (counter_width < = pointer_width) THEN
      output := temp(pointer_width - 1 DOWNTO pointer_width - counter_width);
    ELSE
      output := temp(counter_width - 1 DOWNTO 0);
    END IF;
    RETURN output;
  END count;

  
-------------------------------------------------------------------------------
-- architecture begins here
-------------------------------------------------------------------------------
BEGIN



-------------------------------------------------------------------------------
-- Prepare input signals
-------------------------------------------------------------------------------
--Single RST
grst : IF (C_HAS_RST=1) GENERATE
  PROCESS (WR_CLK, RST)
  BEGIN
    IF (RST = '1') THEN
      wr_rst_reg < = '1';
    ELSIF (WR_CLK'event and WR_CLK = '1') THEN
      IF (wr_rst_int = '1') THEN
        wr_rst_reg < = '0';
      END IF;
    END IF;

    IF (RST = '1') THEN
      wr_rst_int < = '1';
    ELSIF (WR_CLK'event and WR_CLK = '1') THEN
      wr_rst_int < = wr_rst_reg;
    END IF;
  END PROCESS;

  PROCESS (RD_CLK, RST)
  BEGIN
    IF (RST = '1') THEN
      rd_rst_reg < = '1';
    ELSIF (RD_CLK'event and RD_CLK = '1') THEN
      IF (rd_rst_int = '1') THEN
        rd_rst_reg < = '0';
      END IF;
    END IF;

    IF (RST = '1') THEN
      rd_rst_int < = '1';
    ELSIF (RD_CLK'event and RD_CLK = '1') THEN
      rd_rst_int < = rd_rst_reg;
    END IF;

  END PROCESS;

  wr_rst_i < = wr_rst_int;
  rd_rst_i < = rd_rst_int;
END GENERATE grst;

--No RST
norst  : IF (C_HAS_RST=0) GENERATE
  rd_rst_i < = '0';
  wr_rst_i < = '0';
END GENERATE norst;



  --Calculate WR_ACK based on C_WR_ACK_LOW parameters
    gwalow : IF (C_WR_ACK_LOW = 0) GENERATE
      --WR_ACK < = wr_ack_q;
      WR_ACK < = wr_ack_i;
    END GENERATE gwalow;
    gwahgh : IF (C_WR_ACK_LOW = 1) GENERATE
      --WR_ACK < = NOT wr_ack_q;
      WR_ACK < = NOT wr_ack_i;
    END GENERATE gwahgh;

  --Calculate OVERFLOW based on C_OVERFLOW_LOW parameters
    govlow : IF (C_OVERFLOW_LOW = 0) GENERATE
      --OVERFLOW < = overflow_q;
      OVERFLOW < = overflow_i;
    END GENERATE govlow;
    govhgh : IF (C_OVERFLOW_LOW = 1) GENERATE
      --OVERFLOW < = NOT overflow_q;
      OVERFLOW < = NOT overflow_i;
    END GENERATE govhgh;

  --Calculate VALID based on C_PRELOAD_LATENCY, C_PRELOAD_REGS, and C_VALID_LOW settings
  gvlat1 : IF ((C_PRELOAD_LATENCY = 1) AND (C_PRELOAD_REGS = 0)) GENERATE
    gnvl : IF (C_VALID_LOW = 0) GENERATE
      VALID < = valid_i;
    END GENERATE gnvl;
    gnvh : IF (C_VALID_LOW = 1) GENERATE
      VALID < = NOT valid_i;
    END GENERATE gnvh;
  END GENERATE gvlat1;

  --Calculate VALID based on C_PRELOAD_LATENCY, C_PRELOAD_REGS, and C_VALID_LOW settings
  gvreg1 : IF ((C_PRELOAD_LATENCY = 0) AND (C_PRELOAD_REGS = 1)) GENERATE
    gnvl : IF (C_VALID_LOW = 0) GENERATE
      VALID < = valid_i;
    END GENERATE gnvl;
    gnvh : IF (C_VALID_LOW = 1) GENERATE
      VALID < = NOT valid_i;
    END GENERATE gnvh;
  END GENERATE gvreg1;  

  --Calculate UNDERFLOW based on C_PRELOAD_LATENCY and C_VALID_LOW settings
  guflat1 : IF ((C_PRELOAD_LATENCY = 1) AND (C_PRELOAD_REGS = 0)) GENERATE
    gnul  : IF (C_UNDERFLOW_LOW = 0) GENERATE
      UNDERFLOW < = underflow_i;
    END GENERATE gnul;
    gnuh  : IF (C_UNDERFLOW_LOW = 1) GENERATE
      UNDERFLOW < = NOT underflow_i;
    END GENERATE gnuh;
  END GENERATE guflat1;

  --Calculate UNDERFLOW based on C_PRELOAD_LATENCY and C_VALID_LOW settings
  gufreg1 : IF ((C_PRELOAD_LATENCY = 0) AND (C_PRELOAD_REGS = 1)) GENERATE
    gnul  : IF (C_UNDERFLOW_LOW = 0) GENERATE
      UNDERFLOW < = underflow_i;
    END GENERATE gnul;
    gnuh  : IF (C_UNDERFLOW_LOW = 1) GENERATE
      UNDERFLOW < = NOT underflow_i;
    END GENERATE gnuh;
  END GENERATE gufreg1;  

  --Insert registered delay based on C_PROG_FULL_REG setting
  gpfnreg: IF (C_PROG_FULL_REG = 0) GENERATE
    PROG_FULL < = prog_full_noreg;
  END GENERATE gpfnreg;

  gpfreg: IF (C_PROG_FULL_REG = 1) GENERATE
    PROG_FULL < = prog_full_reg;
  END GENERATE gpfreg;

  --Insert registered delay based on C_PROG_EMPTY_REG setting
  gpenreg: IF (C_PROG_EMPTY_REG = 0) GENERATE
    PROG_EMPTY < = prog_empty_noreg;
  END GENERATE gpenreg;

  gpereg: IF (C_PROG_EMPTY_REG = 1) GENERATE
    PROG_EMPTY < = prog_empty_reg;
  END GENERATE gpereg;  

  rdcg: if (C_RD_DATA_COUNT_WIDTH>C_RD_PNTR_WIDTH) generate
    RD_DATA_COUNT < = '0' & rd_diff_count;
  end generate rdcg;
  nrdcg: if (C_RD_DATA_COUNT_WIDTH< =C_RD_PNTR_WIDTH) generate
    RD_DATA_COUNT < = rd_diff_count(C_RD_PNTR_WIDTH-1 DOWNTO C_RD_PNTR_WIDTH-C_RD_DATA_COUNT_WIDTH);
  end generate nrdcg;
         
  wdcg: if (C_RD_DATA_COUNT_WIDTH>C_RD_PNTR_WIDTH) generate
    WR_DATA_COUNT < = '0' & wr_diff_count;      
  end generate wdcg;
  nwdcg: if (C_RD_DATA_COUNT_WIDTH< =C_RD_PNTR_WIDTH) generate
    WR_DATA_COUNT < = wr_diff_count(C_WR_PNTR_WIDTH-1 DOWNTO C_WR_PNTR_WIDTH-C_WR_DATA_COUNT_WIDTH);
  end generate nwdcg;
         
  FULL          < = FULL_i;
  EMPTY         < = empty_i;

-------------------------------------------------------------------------------
-- Asynchrounous FIFO using linked lists
-------------------------------------------------------------------------------
  FIFO_PROC : PROCESS (WR_CLK, RD_CLK, rd_rst_i, wr_rst_i)

    VARIABLE head : listptr;
    VARIABLE tail : listptr;
    VARIABLE size : integer                                     := 0;
    --VARIABLE data : std_logic_vector(c_dout_width - 1 DOWNTO 0) := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);        
    VARIABLE data : std_logic_vector(c_dout_width - 1 DOWNTO 0) := (others => '0');

    VARIABLE prog_empty_actual_assert_thresh : integer := 0;
    VARIABLE prog_empty_actual_negate_thresh : integer := 0;
    VARIABLE prog_full_actual_assert_thresh : integer := 0;
    VARIABLE prog_full_actual_negate_thresh : integer := 0;

  BEGIN

    --Calculate the current contents of the FIFO (size)
    -- Warning: This value should only be calculated once each time this
    -- process is entered. It is updated instantaneously.
    sizeof(head, size);
    
    -- RESET CONDITIONS
    IF wr_rst_i = '1' THEN
      -- Whenever user is attempting to write to
      -- a FULL FIFO, the core should report an overflow error, even if
      -- the core is in a RESET condition.
      overflow_i  < = FULL_i and WR_EN;
      overflow_q  < = FULL_i and WR_EN;
      overflow_q2 < = FULL_i and WR_EN;
      wr_ack_q    < = '0';
      wr_ack_q2   < = '0';
      wr_ack_i    < = '0';

      FULL_i          < = C_FULL_RESET_VAL;         --'1';
      ALMOST_FULL     < = C_ALMOST_FULL_RESET_VAL;  --'1';

      wr_point < = 0;
      rd_reg0  < = 0;
      rd_reg1  < = 0;
      wr_diff_count < = (others => '0');

      IF wr_rst_reg = '0' THEN
        prog_full_noreg < = '0';
        prog_full_reg   < = '0';
      ELSE
        prog_full_noreg < = C_PROG_FULL_RESET_VAL;
        prog_full_reg   < = C_PROG_FULL_RESET_VAL;
      END IF;

        --Create new linked list
      newlist(head, tail);

      --Clear data output queue
      data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);

      ---------------------------------------------------------------------------
      -- Write to FIFO
      ---------------------------------------------------------------------------
    ELSIF WR_CLK'event AND WR_CLK = '1' THEN

      --Create registered versions of these internal signals
      wr_ack_q    < = wr_ack_i;
      wr_ack_q2   < = wr_ack_q;
      overflow_q  < = overflow_i;
      overflow_q2 < = overflow_q;

      rd_reg0 < = rd_point;
      rd_reg1 < = rd_reg0;

      prog_full_reg < = prog_full_noreg;

      wr_diff_count < = conv_std_logic_vector((size/C_RATIO_R), C_WR_PNTR_WIDTH);


        IF (WR_EN = '1' and FULL_i = '0') THEN

          --If writing, then it is not possible to predict how many
          --words will actually be in the FIFO after the write concludes
          --(because the number of reads which happen in this time can
          -- not be determined).
          --Therefore, treat it pessimistically and always assume that
          -- the write will happen without a read (assume the FIFO is
          -- C_RATIO_R fuller than it is).
          IF (size+C_RATIO_R >= C_FIFO_WR_DEPTH*C_RATIO_R-C_RATIO_R+1) THEN
            FULL_i      < = '1';
            ALMOST_FULL < = '1';
          ELSIF (size+C_RATIO_R >= C_FIFO_WR_DEPTH*C_RATIO_R-(2*C_RATIO_R)+1) THEN
            FULL_i      < = '0';
            ALMOST_FULL < = '1';
          ELSE
            FULL_i      < = '0';
            ALMOST_FULL < = '0';
          END IF;

        ELSE --IF (WR_EN='0' or FULL_i='1')

          --If not writing, then use the actual number of words in the FIFO
          -- to determine if the FIFO should be reporting FULL or not.
          IF (size >= C_FIFO_WR_DEPTH*C_RATIO_R-C_RATIO_R+1) THEN
            FULL_i      < = '1';
            ALMOST_FULL < = '1';
          ELSIF (size >= C_FIFO_WR_DEPTH*C_RATIO_R-(2*C_RATIO_R)+1) THEN
            FULL_i      < = '0';
            ALMOST_FULL < = '1';
          ELSE
            FULL_i      < = '0';
            ALMOST_FULL < = '0';
          END IF;

        END IF;  --WR_EN



      -- User is writing to the FIFO
      IF WR_EN = '1' THEN
        -- User is writing to a FIFO which is NOT reporting FULL
        IF FULL_i /= '1' THEN

          -- FIFO really is Full        
          IF size/C_RATIO_R = C_FIFO_WR_DEPTH THEN
            --Report Overflow and do not acknowledge the write
            overflow_i < = '1';
            wr_ack_i   < = '0';

            -- FIFO is almost full
          ELSIF size/C_RATIO_R + 1 = C_FIFO_WR_DEPTH THEN
            -- This write will succeed, and FIFO will go FULL
            overflow_i < = '0';
            wr_ack_i   < = '1';
            FULL_i     < = '1';
            FOR h IN C_RATIO_R DOWNTO 1 LOOP
              add(head, tail, DIN( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
            END LOOP;
            wr_point   < = (wr_point + 1) MOD C_FIFO_WR_DEPTH;

            -- FIFO is one away from almost full
          ELSIF size/C_RATIO_R + 2 = C_FIFO_WR_DEPTH THEN
            -- This write will succeed, and FIFO will go ALMOST_FULL
            overflow_i  < = '0';
            wr_ack_i    < = '1';
            ALMOST_FULL < = '1';
            FOR h IN C_RATIO_R DOWNTO 1 LOOP
              add(head, tail, DIN( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
            END LOOP;
            wr_point    < = (wr_point + 1) MOD C_FIFO_WR_DEPTH;

            -- FIFO is no where near FULL
          ELSE
            --Write will succeed, no change in status
            overflow_i < = '0';
            wr_ack_i   < = '1';
            FOR h IN C_RATIO_R DOWNTO 1 LOOP
              add(head, tail, DIN( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
            END LOOP;
            wr_point   < = (wr_point + 1) MOD C_FIFO_WR_DEPTH;
          END IF;

          -- User is writing to a FIFO which IS reporting FULL
        ELSE                            --IF FULL_i = '1'
          --Write will fail
          overflow_i < = '1';
          wr_ack_i   < = '0';
        END IF;  --FULL_i

      ELSE                              --WR_EN/='1'
        --No write attempted, so neither overflow or acknowledge
        overflow_i < = '0';
        wr_ack_i   < = '0';
      END IF;  --WR_EN

      -------------------------------------------------------------------------
      -- Programmable FULL flags
      -------------------------------------------------------------------------
      -- Determine the assert and negate thresholds for comparison
      -- (Subtract 2 read words when using Preload 0)

      --Single Constant Threshold
      IF (C_PROG_FULL_TYPE = 1) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_full_actual_assert_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R-2*C_RATIO_W;
          prog_full_actual_negate_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R-2*C_RATIO_W;
        else  
          prog_full_actual_assert_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R;
          prog_full_actual_negate_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R;
        end if;


        --Dual Constant Thresholds
      ELSIF (C_PROG_FULL_TYPE = 2) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_full_actual_assert_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R-2*C_RATIO_W;
          prog_full_actual_negate_thresh := C_PROG_FULL_THRESH_NEGATE_VAL*C_RATIO_R-2*C_RATIO_W;
        else  
          prog_full_actual_assert_thresh := C_PROG_FULL_THRESH_ASSERT_VAL*C_RATIO_R;
          prog_full_actual_negate_thresh := C_PROG_FULL_THRESH_NEGATE_VAL*C_RATIO_R;
        end if;

        --Single threshold input port
      ELSIF (C_PROG_FULL_TYPE = 3) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_full_actual_assert_thresh := conv_integer(PROG_FULL_THRESH)*C_RATIO_R-2*C_RATIO_W;
          prog_full_actual_negate_thresh := conv_integer(PROG_FULL_THRESH)*C_RATIO_R-2*C_RATIO_W;
        else  
          prog_full_actual_assert_thresh := conv_integer(PROG_FULL_THRESH)*C_RATIO_R;
          prog_full_actual_negate_thresh := conv_integer(PROG_FULL_THRESH)*C_RATIO_R;
        end if;

        --Dual threshold input ports
      ELSIF (C_PROG_FULL_TYPE = 4) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_full_actual_assert_thresh := conv_integer(PROG_FULL_THRESH_ASSERT)*C_RATIO_R-2*C_RATIO_W;
          prog_full_actual_negate_thresh := conv_integer(PROG_FULL_THRESH_NEGATE)*C_RATIO_R-2*C_RATIO_W;
        else  
          prog_full_actual_assert_thresh := conv_integer(PROG_FULL_THRESH_ASSERT)*C_RATIO_R;
          prog_full_actual_negate_thresh := conv_integer(PROG_FULL_THRESH_NEGATE)*C_RATIO_R;
        end if;

      END IF;  --C_PROG_FULL_TYPE

  
        -- If we will be going at or above the prog_full_actual_assert_thresh threshold
        -- on the next clock cycle, then assert PROG_FULL

        -- WARNING: For the fifo with separate clocks, it is possible that
        -- the core could be both reading and writing simultaneously, with
        -- the writes occuring faster. This means that the number of words
        -- in the FIFO is increasing, and PROG_FULL should be asserted. So,
        -- we assume the worst and assert PROG_FULL if we are close and
        -- writing, since RD_EN may or may not have an impact on the number
        -- of words in the FIFO.
        IF ((size = prog_full_actual_assert_thresh-1) AND WR_EN = '1') THEN
          prog_full_noreg < = '1';

        -- If we are at or above the prog_full_actual_assert_thresh, then assert
        -- PROG_FULL
        ELSIF (size >= prog_full_actual_assert_thresh) THEN
          prog_full_noreg < = '1';
        --If we are below the prog_full_actual_negate_thresh, then de-assert PROG_FULL 
        ELSIF (size < prog_full_actual_negate_thresh) THEN
          prog_full_noreg < = '0';
        END IF;




    END IF;  --WR_CLK
    
    ---------------------------------------------------------------------------
    -- Read from FIFO
    ---------------------------------------------------------------------------
    IF rd_rst_i = '1' THEN
      -- Whenever user is attempting to read from
      -- an EMPTY FIFO, the core should report an underflow error, even if
      -- the core is in a RESET condition.
      underflow_i       < = empty_i and RD_EN;
      valid_i           < = '0';
      output_regs_valid < = '0';
      empty_i           < = '1';
      empty_q           < = '1';
      ALMOST_EMPTY      < = '1';

      rd_point < = 0;
      wr_reg0  < = 0;
      wr_reg1  < = 0;
      rd_diff_count < = (others => '0');

      prog_empty_noreg < = '1';
      prog_empty_reg < = '1';

      --Clear data output queue
      data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);

    ELSIF RD_CLK'event AND RD_CLK = '1' THEN

      --These values are set to this value
      -- (unless overridden later in this process)
      underflow_i < = '0';
      valid_i     < = '0';
      empty_q     < = empty_i;
      
      prog_empty_reg < = prog_empty_noreg;
      --Default
      wr_reg0           < = wr_point;
      wr_reg1           < = wr_reg0;
      rd_diff_count < = conv_std_logic_vector((size/C_RATIO_W), C_RD_PNTR_WIDTH);

      ---------------------------------------------------------------------------
      -- Read Latency 1
      ---------------------------------------------------------------------------

        IF size/C_RATIO_W = 0 THEN
          empty_i      < = '1';
          ALMOST_EMPTY < = '1';
        ELSIF size/C_RATIO_W = 1 THEN
          empty_i      < = '0';            
          ALMOST_EMPTY < = '1';
        ELSE
          empty_i      < = '0';
          ALMOST_EMPTY < = '0';
        END IF;

        IF RD_EN = '1' THEN

          IF empty_i /= '1' THEN
            -- FIFO full
            IF size/C_RATIO_W = 2 THEN
              ALMOST_EMPTY < = '1';
              valid_i      < = '1';
              FOR h IN C_RATIO_W DOWNTO 1 LOOP
                read(tail, data( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
                remove(head, tail);
              END LOOP;
              rd_point     < = (rd_point + 1) MOD C_FIFO_RD_DEPTH;
              -- almost empty
            ELSIF size/C_RATIO_W = 1 THEN
              ALMOST_EMPTY < = '1';
              empty_i      < = '1';
              valid_i      < = '1';
              FOR h IN C_RATIO_W DOWNTO 1 LOOP
                read(tail, data( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
                remove(head, tail);
              END LOOP;
              rd_point     < = (rd_point + 1) MOD C_FIFO_RD_DEPTH;
              -- fifo empty
            ELSIF size/C_RATIO_W = 0 THEN
              underflow_i  < = '1';
              -- middle counts
            ELSE
              valid_i      < = '1';
              FOR h IN C_RATIO_W DOWNTO 1 LOOP
                read(tail, data( (C_SMALLER_DATA_WIDTH*h)-1 DOWNTO C_SMALLER_DATA_WIDTH*(h-1) ) );
                remove(head, tail);
              END LOOP;
              rd_point     < = (rd_point + 1) MOD C_FIFO_RD_DEPTH;
            END IF;
          ELSE
            underflow_i    < = '1';
          END IF;
        END IF;  --RD_EN

      ---------------------------------------------------------------------------
      -- Programmable EMPTY Flags
      ---------------------------------------------------------------------------
      -- Determine the assert and negate thresholds for comparison
      -- (Subtract 2 read words when using Preload 0)

  
      --Single Constant Threshold
      IF (C_PROG_EMPTY_TYPE = 1) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_empty_actual_assert_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W - 2*C_RATIO_W;
          prog_empty_actual_negate_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W - 2*C_RATIO_W;
        else  
          prog_empty_actual_assert_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W;
          prog_empty_actual_negate_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W;
        end if;

        --Dual constant thresholds
      ELSIF (C_PROG_EMPTY_TYPE = 2) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_empty_actual_assert_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W - 2*C_RATIO_W;
          prog_empty_actual_negate_thresh := C_PROG_EMPTY_THRESH_NEGATE_VAL*C_RATIO_W - 2*C_RATIO_W;
        else  
          prog_empty_actual_assert_thresh := C_PROG_EMPTY_THRESH_ASSERT_VAL*C_RATIO_W;
          prog_empty_actual_negate_thresh := C_PROG_EMPTY_THRESH_NEGATE_VAL*C_RATIO_W;
        end if;

  --Single threshold input port
      ELSIF (C_PROG_EMPTY_TYPE = 3) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_empty_actual_assert_thresh := conv_integer(PROG_EMPTY_THRESH)*C_RATIO_W - 2*C_RATIO_W;
          prog_empty_actual_negate_thresh := conv_integer(PROG_EMPTY_THRESH)*C_RATIO_W - 2*C_RATIO_W;
        else  
          prog_empty_actual_assert_thresh := conv_integer(PROG_EMPTY_THRESH)*C_RATIO_W;
          prog_empty_actual_negate_thresh := conv_integer(PROG_EMPTY_THRESH)*C_RATIO_W;
        end if;

        --Dual threshold input ports
      ELSIF (C_PROG_EMPTY_TYPE = 4) THEN
        if (C_PRELOAD_REGS=1 and C_PRELOAD_LATENCY=0) then
          prog_empty_actual_assert_thresh := conv_integer(PROG_EMPTY_THRESH_ASSERT)*C_RATIO_W - 2*C_RATIO_W;
          prog_empty_actual_negate_thresh := conv_integer(PROG_EMPTY_THRESH_NEGATE)*C_RATIO_W - 2*C_RATIO_W;
        else  
          prog_empty_actual_assert_thresh := conv_integer(PROG_EMPTY_THRESH_ASSERT)*C_RATIO_W;
          prog_empty_actual_negate_thresh := conv_integer(PROG_EMPTY_THRESH_NEGATE)*C_RATIO_W;
        end if;

     END IF;


        -- If we will be going at or below the prog_empty_actual_assert_thresh threshold
        -- on the next clock cycle, then assert PROG_EMPTY
        --
        -- WARNING: For the fifo with separate clocks, it is possible that
        -- the core could be both reading and writing simultaneously, with
        -- the reads occuring faster. This means that the number of words
        -- in the FIFO is decreasing, and PROG_EMPTY should be asserted. So,
        -- we assume the worst and assert PROG_EMPTY if we are close and
        -- reading, since WR_EN may or may not have an impact on the number
        -- of words in the FIFO.
        IF ((size = prog_empty_actual_assert_thresh+1) AND RD_EN = '1') THEN
          prog_empty_noreg < = '1';

          -- If we are at or below the prog_empty_actual_assert_thresh, then assert
          -- PROG_EMPTY
        ELSIF (size < = prog_empty_actual_assert_thresh) THEN
          prog_empty_noreg          < = '1';

          --If we are above the prog_empty_actual_negate_thresh, then de-assert PROG_EMPTY
        ELSIF (size> prog_empty_actual_negate_thresh) THEN
          prog_empty_noreg < = '0';
        END IF;







    END IF;  --RD_CLK

    DOUT < = data;

  END PROCESS;

END behavioral;