-------------------------------------------------------------------------------
-- 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_fifo16 IS



-------------------------------------------------------------------------------
-- 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;
    ELSE
      RETURN c_fifo_depth-1;
    END IF;
  END actual_fifo_depth;

-------------------------------------------------------------------------------
-- fifo16_fifo_depth
--   This is the depth of the memory used in the FIFO for FIFO16
--   implementation. The depth calculation takes into account the number of
--   cascaded primitives.
-------------------------------------------------------------------------------
FUNCTION fifo16_fifo_depth (
  c_fifo_depth : integer; 
  C_PRIM_FIFO_TYPE : integer)
  RETURN integer IS
  VARIABLE actual_depth : integer;
  VARIABLE num_fifo     : integer := 0;
BEGIN
    
  num_fifo := ((c_fifo_depth-1)/C_PRIM_FIFO_TYPE) + 1;
    if (c_fifo_depth>=C_PRIM_FIFO_TYPE) then
      actual_depth := ((C_PRIM_FIFO_TYPE-4)*num_fifo);      
    else
      actual_depth := c_fifo_depth;
    end if;
  RETURN actual_depth;

END fifo16_fifo_depth;  

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

  CONSTANT C_FIFO_WR_DEPTH : integer := fifo16_fifo_depth(C_WR_DEPTH,C_PRIM_FIFO_TYPE);
  CONSTANT C_FIFO_RD_DEPTH : integer := fifo16_fifo_depth(C_RD_DEPTH,C_PRIM_FIFO_TYPE);

  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 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 FULL_i       : std_logic := '1';
  SIGNAL prog_empty_i : std_logic := '1';
  SIGNAL prog_full_i  : std_logic := '1';
  SIGNAL prog_empty_q : std_logic := '1';
  SIGNAL prog_full_q  : std_logic := '1';

  SIGNAL rd_rst_i : std_logic;
  SIGNAL wr_rst_i : 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 message_complete : boolean := false;

-------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------
  rd_rst_i < = RST;
  wr_rst_i < = RST;



  --Calculate WR_ACK based on C_WR_RESPONSE_LATENCY and 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_WR_RESPONSE_LATENCY and 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 and C_VALID_LOW settings
  gvlat1 : IF (C_PRELOAD_LATENCY = 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 gvlat1;

  --Calculate UNDERFLOW based on C_PRELOAD_LATENCY and C_VALID_LOW settings
  guflat1 : IF (C_PRELOAD_LATENCY = 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 guflat1;

  RD_DATA_COUNT < = rd_diff_count(C_RD_PNTR_WIDTH-1 DOWNTO C_RD_PNTR_WIDTH-C_RD_DATA_COUNT_WIDTH);
  WR_DATA_COUNT < = wr_diff_count(C_WR_PNTR_WIDTH-1 DOWNTO C_WR_PNTR_WIDTH-C_WR_DATA_COUNT_WIDTH);
  FULL          < = FULL_I;
  EMPTY         < = empty_i;

-------------------------------------------------------------------------------
-- Asynchrounous FIFO using linked lists
-------------------------------------------------------------------------------
  FIFO_PROC : PROCESS (WR_CLK, RD_CLK, 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) := (OTHERS => '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
      assert (message_complete) report "Warning: When using Virtex-4 built-in FIFO configurations for the FIFO Generator, it is strongly recommended that you use the structural simulation model instead of the behavioral model. This will ensure accurate behavior and latencies during simulation. You can enable this from CORE Generator by selecting Project -> Project Options -> Generation tab -> Structural Simulation. See the FIFO Generator User Guide for more information." severity NOTE;
      message_complete < = true;
      overflow_i  < = '0';
      overflow_q  < = '0';
      overflow_q2 < = '0';
      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';
      PROG_FULL       < = C_PROG_FULL_RESET_VAL;    --'1';
      prog_full_i     < = C_PROG_FULL_RESET_VAL;    --'1';
      prog_full_q     < = C_PROG_FULL_RESET_VAL;    --'1';

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

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

      --Clear data output queue
--       data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);
      data := (OTHERS => '0');      

      ---------------------------------------------------------------------------
      -- Write to FIFO
      ---------------------------------------------------------------------------
    ELSIF ((WR_CLK'event AND WR_CLK = '1') OR (CLK'event AND 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;

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


      --Set the current state of FULL and ALMOST_FULL values
      -- (these may be overwritten later in this process        if the user is writing to the FIFO)
      IF (C_RATIO_R = 1) THEN
        IF (size = C_FIFO_WR_DEPTH) THEN
          FULL_i      < = '1';
          ALMOST_FULL < = '1';
        ELSIF size >= C_FIFO_WR_DEPTH - 1 THEN
          FULL_i      < = '0';
          ALMOST_FULL < = '1';
        ELSE
          FULL_i      < = '0';
          ALMOST_FULL < = '0';
        END IF;
      ELSE

        IF (WR_EN = '0') THEN

          --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;

        ELSE                            --IF (WR_EN='1')

          --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;

        END IF;  --WR_EN

      END IF;  --C_RATIO_R



      -- 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
      -------------------------------------------------------------------------

      --Single Constant Threshold
      IF (C_PROG_FULL_TYPE = 1) THEN

        -- If we are at or above the PROG_FULL_THRESH, or we will be 
        --  next clock cycle, then assert PROG_FULL
        IF ((size/C_RATIO_R >= C_PROG_FULL_THRESH_ASSERT_VAL) OR ((size/C_RATIO_R = C_PROG_FULL_THRESH_ASSERT_VAL-1) AND WR_EN = '1')) THEN
          prog_full_i < = '1';
        ELSE
          prog_full_i < = '0';
        END IF;  -- C_HAS_PROG_FULL_THRESH = 1


        --Dual Constant Thresholds
      ELSIF (C_PROG_FULL_TYPE = 2) THEN

        -- If we will be going at or above the C_PROG_FULL_ASSERT 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/C_RATIO_R = C_PROG_FULL_THRESH_ASSERT_VAL-1) AND WR_EN = '1') THEN
          prog_full_i < = '1';

        ELSIF (size/C_RATIO_R >= C_PROG_FULL_THRESH_ASSERT_VAL) THEN
          prog_full_i < = '1';
          --If we are below the C_PROG_FULL_NEGATE, then de-assert PROG_FULL 
        ELSIF (size/C_RATIO_R <  C_PROG_FULL_THRESH_NEGATE_VAL) THEN
          prog_full_i < = '0';
        END IF;

        --Single threshold input port
      ELSIF (C_PROG_FULL_TYPE = 3) THEN

        -- If we are at or above the PROG_FULL_THRESH, or we will be 
        --  next clock cycle, then assert PROG_FULL
        IF ((size/C_RATIO_R >= conv_integer(PROG_FULL_THRESH)) OR ((size/C_RATIO_R = conv_integer(PROG_FULL_THRESH)-1) AND WR_EN = '1')) THEN
          prog_full_i < = '1';
        ELSE
          prog_full_i < = '0';
        END IF;  -- C_HAS_PROG_FULL_THRESH = 1

        --Dual threshold input ports
      ELSIF (C_PROG_FULL_TYPE = 4) THEN


        -- If we will be going at or above the C_PROG_FULL_ASSERT threshold
        -- on the next clock cycle, then assert PROG_FULL
        IF ((size/C_RATIO_R = conv_integer(PROG_FULL_THRESH_ASSERT)-1) AND WR_EN = '1') THEN
          prog_full_i < = '1';
          -- If we are at or above the C_PROG_FULL_ASSERT, then assert
          -- PROG_FULL
        ELSIF (size/C_RATIO_R >= conv_integer(PROG_FULL_THRESH_ASSERT)) THEN
          prog_full_i < = '1';
          --If we are below the C_PROG_FULL_NEGATE, then de-assert PROG_FULL 
        ELSIF (size/C_RATIO_R <  conv_integer(PROG_FULL_THRESH_NEGATE)) THEN
          prog_full_i < = '0';
        END IF;


      END IF;  --C_PROG_FULL_TYPE

      prog_full_q < = prog_full_i;
      PROG_FULL < = prog_full_q;

    END IF;  --WR_CLK

    ---------------------------------------------------------------------------
    -- Read from FIFO
    ---------------------------------------------------------------------------
    IF rd_rst_i = '1' THEN
      underflow_i       < = '0';
      valid_i           < = '0';
      output_regs_valid < = '0';
      empty_i           < = '1';
      ALMOST_EMPTY      < = '1';
      PROG_EMPTY        < = '1';
      prog_empty_i      < = '1';
      prog_empty_q      < = '1';     

      rd_point < = 0;
      wr_reg0  < = 0;
      wr_reg1  < = 0;
      rd_diff_count < = (others => '0');
      
      --Clear data output queue
--       data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);
      data := (OTHERS => '0');

    ELSIF ((RD_CLK'event AND RD_CLK = '1') OR (CLK'event AND CLK = '1')) THEN

      underflow_i < = '0';
      valid_i     < = '0';

      --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 (C_PRELOAD_LATENCY = 1) THEN

        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
      END IF;  --C_PRELOAD_LATENCY


      ---------------------------------------------------------------------------
      -- Programmable EMPTY Flags
      ---------------------------------------------------------------------------

      --Single Constant Threshold
      IF (C_PROG_EMPTY_TYPE = 1) THEN

        -- If we are at or below the PROG_EMPTY_THRESH, or we will be 
        -- the next clock cycle, then assert PROG_EMPTY
        IF (
          (size/C_RATIO_W < = C_PROG_EMPTY_THRESH_ASSERT_VAL) OR
          ((size/C_RATIO_W = C_PROG_EMPTY_THRESH_ASSERT_VAL+1) AND RD_EN = '1')
          ) THEN
          prog_empty_i      < = '1';
        ELSE
          prog_empty_i      < = '0';
        END IF;

        --Dual constant thresholds
      ELSIF (C_PROG_EMPTY_TYPE = 2) THEN


        -- If we will be going at or below the C_PROG_EMPTY_ASSERT 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/C_RATIO_W = C_PROG_EMPTY_THRESH_ASSERT_VAL+1) AND RD_EN = '1') THEN
          prog_empty_i < = '1';
          -- If we are at or below the C_PROG_EMPTY_ASSERT, then assert
          -- PROG_EMPTY
        ELSIF (size/C_RATIO_W < = C_PROG_EMPTY_THRESH_ASSERT_VAL) THEN
          prog_empty_i          < = '1';

          --If we are above the C_PROG_EMPTY_NEGATE, then de-assert PROG_EMPTY
        ELSIF (size/C_RATIO_W > C_PROG_EMPTY_THRESH_NEGATE_VAL) THEN
          prog_empty_i < = '0';
        END IF;

        --Single threshold input port
      ELSIF (C_PROG_EMPTY_TYPE = 3) THEN

        -- If we are at or below the PROG_EMPTY_THRESH, or we will be 
        -- the next clock cycle, then assert PROG_EMPTY
        IF (
          (size/C_RATIO_W < = conv_integer(PROG_EMPTY_THRESH)) OR
          ((size/C_RATIO_W = conv_integer(PROG_EMPTY_THRESH)+1) AND RD_EN = '1')
          ) THEN
          prog_empty_i      < = '1';
        ELSE
          prog_empty_i      < = '0';
        END IF;


        --Dual threshold input ports
      ELSIF (C_PROG_EMPTY_TYPE = 4) THEN

        -- If we will be going at or below the C_PROG_EMPTY_ASSERT threshold
        -- on the next clock cycle, then assert PROG_EMPTY
        IF ((size/C_RATIO_W = conv_integer(PROG_EMPTY_THRESH_ASSERT)+1) AND RD_EN = '1') THEN
          prog_empty_i          < = '1';
          -- If we are at or below the C_PROG_EMPTY_ASSERT, then assert
          -- PROG_EMPTY
        ELSIF (size/C_RATIO_W < = conv_integer(PROG_EMPTY_THRESH_ASSERT)) THEN
          prog_empty_i          < = '1';

          --If we are above the C_PROG_EMPTY_NEGATE, then de-assert PROG_EMPTY
        ELSIF (size/C_RATIO_W > conv_integer(PROG_EMPTY_THRESH_NEGATE)) THEN
          prog_empty_i < = '0';
        END IF;


      END IF;


    prog_empty_q < = prog_empty_i;
    PROG_EMPTY   < = prog_empty_q;


    END IF;  --RD_CLK


    DOUT       < = data;

  END PROCESS;


END behavioral;