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

  CONSTANT C_HAS_FAST_FIFO  : integer := 0;  --DEFAULT_HAS_FAST_FIFO

-------------------------------------------------------------------------------
-- 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_HAS_FAST_FIFO=1 (using Peter Alfke's implementation)
-- *C_PRELOAD_REGS=1 AND C_PRELOAD_LATENCY=1 (preload output register adds 1
-- word of depth to the FIFO)
-- *C_COMMON_CLOCK=1 (sync fifo can use entire memory depth)
-------------------------------------------------------------------------------
  FUNCTION actual_fifo_depth(c_fifo_depth : integer; C_HAS_FAST_FIFO : integer; C_PRELOAD_REGS : integer; C_PRELOAD_LATENCY : integer; C_COMMON_CLOCK : integer) RETURN integer IS
  BEGIN
    IF (C_HAS_FAST_FIFO = 1 OR (C_PRELOAD_REGS = 1 AND C_PRELOAD_LATENCY = 1) OR C_COMMON_CLOCK = 1) THEN
      RETURN c_fifo_depth;
    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_HAS_FAST_FIFO, C_PRELOAD_REGS, C_PRELOAD_LATENCY, C_COMMON_CLOCK);
  CONSTANT C_FIFO_RD_DEPTH : integer := actual_fifo_depth(C_RD_DEPTH, C_HAS_FAST_FIFO, C_PRELOAD_REGS, C_PRELOAD_LATENCY, C_COMMON_CLOCK);

  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 := 1;
  CONSTANT C_PROG_EMPTY_REG : integer := 1;

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

-------------------------------------------------------------------------------
-- Internal Signals
-------------------------------------------------------------------------------
  SIGNAL empty_i        : std_logic := '1';
  SIGNAL full_i         : std_logic := '1';
  SIGNAL almost_empty_i : std_logic := '1';
  SIGNAL almost_full_i  : std_logic := '1';
  
  SIGNAL rst_i : std_logic;
  SIGNAL rst_int : std_logic;
  SIGNAL rst_reg : std_logic;

  SIGNAL 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 rst_q          : std_logic := '0';

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

  SIGNAL power_on_timer  : integer := 3;
  
-------------------------------------------------------------------------------
-- 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 (CLK, RST)
  BEGIN
    IF (RST = '1') THEN
      rst_reg < = '1';
    ELSIF (CLK'event and CLK = '1') THEN
      IF (rst_int = '1') THEN
        rst_reg < = '0';
      END IF;
    END IF;

    IF (RST = '1') THEN
      rst_int < = '1';
    ELSIF (CLK'event and CLK = '1') THEN
      rst_int < = rst_reg;
    END IF;

  END PROCESS;

  rst_i < = rst_int;
END GENERATE grst;

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


  gdc    : IF (C_HAS_DATA_COUNT = 1) GENERATE

    gdcb : IF (C_DATA_COUNT_WIDTH > C_RD_PNTR_WIDTH) GENERATE
      DATA_COUNT(C_RD_PNTR_WIDTH-1 DOWNTO 0)                  < = diff_count;
      DATA_COUNT(C_DATA_COUNT_WIDTH-1 DOWNTO C_DATA_COUNT_WIDTH-C_RD_PNTR_WIDTH) < = (OTHERS => '0');
    END GENERATE;

    gdcs : IF (C_DATA_COUNT_WIDTH < = C_RD_PNTR_WIDTH) GENERATE
      DATA_COUNT < = diff_count(C_RD_PNTR_WIDTH-1 DOWNTO C_RD_PNTR_WIDTH-C_DATA_COUNT_WIDTH);
    END GENERATE;
  END GENERATE;
 
  gndc    : IF (C_HAS_DATA_COUNT = 0) GENERATE
      DATA_COUNT < = (OTHERS => '0');
  END GENERATE;

  --Calculate WR_ACK based on C_WR_ACK_LOW parameters
    gwalow : IF (C_WR_ACK_LOW = 0) GENERATE
      WR_ACK < = wr_ack_i;
    END GENERATE gwalow;
    gwahgh : IF (C_WR_ACK_LOW = 1) GENERATE
      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_i;
    END GENERATE govlow;
    govhgh : IF (C_OVERFLOW_LOW = 1) GENERATE
      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;

  --Insert registered delay based on C_PROG_FULL_REG setting
  gpfnreg: IF (C_PROG_FULL_REG = 0) GENERATE
    PROCESS (power_on_timer, prog_full_noreg)
    BEGIN
      IF (power_on_timer = 0) THEN
        PROG_FULL < = prog_full_noreg;
      ELSE
        PROG_FULL < = C_PROG_FULL_RESET_VAL;
      END IF;
    END PROCESS;
  END GENERATE gpfnreg;

  gpfreg: IF (C_PROG_FULL_REG = 1) GENERATE
    PROCESS (power_on_timer, prog_full_reg)
    BEGIN
      IF (power_on_timer = 0) THEN
        PROG_FULL < = prog_full_reg;
      ELSE
        PROG_FULL < = C_PROG_FULL_RESET_VAL;
      END IF;
    END PROCESS;
  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;  
          
  PROCESS (power_on_timer, full_i)
  BEGIN
    IF (power_on_timer = 0) THEN
      FULL < = full_i;
    ELSE
      FULL < = C_FULL_RESET_VAL;
    END IF;
  END PROCESS;

  PROCESS (power_on_timer, almost_full_i)
  BEGIN
    IF (power_on_timer = 0) THEN
      ALMOST_FULL < = almost_full_i;
    ELSE
      ALMOST_FULL < = C_ALMOST_FULL_RESET_VAL;
    END IF;
  END PROCESS;
  
  EMPTY         < = empty_i;
  ALMOST_EMPTY  < = almost_empty_i;

-------------------------------------------------------------------------------
-- Synchrounous FIFO using linked lists
-------------------------------------------------------------------------------

  -----------------------------------------------------------------------------
  -- Simulataneous Write and Read
  -- Write process will always happen before the read the process
  -----------------------------------------------------------------------------
  FIFO_PROC : PROCESS (CLK, 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( cond_string(C_MEMORY_TYPE /= 1, C_DOUT_RST_VAL, "0"),
                             C_DOUT_WIDTH);        
  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 (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';
      rst_q < = '1';
      power_on_timer < = 0;
      
      full_i          < = C_FULL_RESET_VAL;         
      almost_full_i   < = C_ALMOST_FULL_RESET_VAL;  
      prog_full_reg   < = C_PROG_FULL_RESET_VAL;    
      prog_full_noreg < = C_PROG_FULL_RESET_VAL;    

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

      IF (C_MEMORY_TYPE/=1) THEN         --Most memories asynchronously reset
        data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);        
      END IF;
      IF (C_MEMORY_TYPE=1) THEN         --Block Memory Synchronously resets
        IF (CLK'event AND CLK = '1') THEN
          data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);
        END IF;
      END IF;
      
      --Clear data output queue
      -- 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';
      almost_empty_i  < = '1';
      prog_empty_reg  < = '1';
      prog_empty_noreg< = '1';
      
      diff_count      < = (others => '0');
      
    ELSIF (CLK'event AND CLK = '1') THEN

      --------------------------------------------------------------------------
      -- Write to FIFO
      ------------------------------------------------------------------------- 
      --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;
      rst_q < = rst_i;

      --------------------------------------------------------------------------
      -- Read from FIFO
      --------------------------------------------------------------------------
      underflow_i < = '0';
      valid_i     < = '0';

      -------------------------------------------------------------------------
      -- Synchronous FIFO Condition #1 : Writing and not reading
      -------------------------------------------------------------------------
      IF ((WR_EN = '1') AND (RD_EN = '0')) THEN
      
        --------------------
        -- FIFO is FULL
        --------------------
        IF (size = C_FIFO_WR_DEPTH) THEN
            --Report Overflow and do not acknowledge the write
            overflow_i < = '1';
            wr_ack_i   < = '0';

            --FIFO Remains FULL & ALMOST_FULL
            full_i        < = '1';
            almost_full_i < = '1';
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
    
            --No write, so do not update diff_count
            
        --------------------
        -- FIFO is reporting FULL (Start-up condition)
        --------------------
        ELSIF ((size <  C_FIFO_WR_DEPTH) AND (FULL_i = '1')) THEN
            --Report Overflow and do not acknowledge the write
            overflow_i < = '1';
            wr_ack_i   < = '0';

            --FIFO is not "really" FULL, so clear these values
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO EMPTY in this state can not be determined
            --empty_i        < = ;
            --almost_empty_i < = ;
    
            --No write, so do not update diff_count
            
        --------------------
        -- FIFO is one from FULL
        --------------------
        ELSIF (size + 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';
            almost_full_i < = '1';
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
     
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);

          --------------------
          --FIFO is two from FULL
          --------------------
          ELSIF size + 2 = C_FIFO_WR_DEPTH THEN
            -- This write will succeed, and FIFO will go ALMOST_FULL
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '1';          
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
     
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);

          --------------------
          --FIFO is ALMOST EMPTY
          --------------------
          ELSIF size = 1 THEN
            -- This write will succeed, and FIFO will no longer be ALMOST EMPTY
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';          
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is leaving ALMOST_EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
     
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);

          --------------------
          --FIFO is EMPTY
          --------------------
          ELSIF size = 0 THEN
            -- This write will succeed, and FIFO will no longer be EMPTY
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';          
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is leaving EMPTY, but is still ALMOST_EMPTY
            empty_i        < = '0';
            almost_empty_i < = '1';
     
             --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);

         --------------------
          --FIFO has two or more words in the FIFO, but is not near FULL
          --------------------
          ELSE -- size>1 and size< C_FIFO_DEPTH-2
            -- This write will succeed, and FIFO will no longer be EMPTY
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';          
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Report no underflow. Output not valid.
            underflow_i  < = '0';
            valid_i      < = '0';
            
            --FIFO is no longer EMPTY or ALMOST_EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);

          END IF;
 
 
      -------------------------------------------------------------------------
      -- Synchronous FIFO Condition #2 : Reading and not writing
      -------------------------------------------------------------------------
      ELSIF ((WR_EN = '0') AND (RD_EN = '1')) THEN
      
          --------------------
          --FIFO is EMPTY or reporting EMPTY
          --------------------
          IF ((size = 0) OR (EMPTY_i = '1')) THEN
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Successful read
            underflow_i  < = '1';
            valid_i      < = '0';
            --FIFO is going EMPTY
            empty_i        < = '1';
            almost_empty_i < = '1';
            
            --No read, so do not update diff_count

          --------------------
          --FIFO is ALMOST EMPTY
          --------------------
          ELSIF (size = 1) THEN
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is going EMPTY
            empty_i        < = '1';
            almost_empty_i < = '1';
            
            --This read will succeed, but it's the last one
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
        --------------------
        -- FIFO is two from EMPTY
        --------------------
        ELSIF (size = 2) THEN
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Successful read 
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is going ALMOST_EMPTY
            empty_i        < = '0';
            almost_empty_i < = '1';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
        --------------------
        -- FIFO is one from FULL
        --------------------
        ELSIF (size + 1 = C_FIFO_WR_DEPTH) THEN
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
        --------------------
        -- FIFO is FULL
        --------------------
        ELSIF (size = C_FIFO_WR_DEPTH) THEN
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '1';
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
            

          --------------------
          --FIFO has two or more words in the FIFO, but is not near FULL
          --------------------
          ELSE -- size>2 and size< C_FIFO_DEPTH-1
            --No write attempted, but a read will succeed
            overflow_i    < = '0';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '0';
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is going EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --This read will succeed, but it's the last one
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
                       
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
          END IF;
 
 
      -------------------------------------------------------------------------
      -- Synchronous FIFO Condition #3 : Reading and writing
      -------------------------------------------------------------------------
      ELSIF ((WR_EN = '1') AND (RD_EN = '1')) THEN
      
        --------------------
        -- FIFO is FULL
        --------------------
        IF (size = C_FIFO_WR_DEPTH) THEN
            -- Write to FULL FIFO will fail
            overflow_i    < = '1';
            wr_ack_i      < = '0';
            full_i        < = '0';
            almost_full_i < = '1';

            -- Read will be successful.
            underflow_i  < = '0';
            valid_i      < = '1';
            
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
     
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size-1),C_RD_PNTR_WIDTH);
            
        --------------------
        -- FIFO is reporting FULL, but it is empty
        --  (this is a special case, when coming out of RST)
        --------------------
        ELSIF ((size = 0) AND (FULL_i = '1')) THEN
            -- Write to FULL FIFO will fail
            overflow_i    < = '1';
            wr_ack_i      < = '0';

            --Clear the FULL flags for normal use
            full_i        < = '0';
            almost_full_i < = '0';

            -- Read will be unsuccessful, because we're empty
            underflow_i  < = '1';
            valid_i      < = '0';
            
            --FIFO EMPTY in this state can not be determined
            empty_i        < = '1';
            almost_empty_i < = '1';
    
            --Do Not Read

            --No read or write, don't update data count
            
        --------------------
        -- FIFO is one from FULL
        --------------------
        ELSIF (size + 1 = C_FIFO_WR_DEPTH) THEN
            -- Write will be successful
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '1';
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
                                    
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Simulaneous read and write, no change in diff_count

          --------------------
          --FIFO is ALMOST EMPTY
          --------------------
          ELSIF (size = 1) THEN
            -- Write will be successful
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '1';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
                        
            --Simulaneous read and write, no change in diff_count

          --------------------
          --FIFO is EMPTY
          --------------------
          ELSIF ((size = 0) OR (EMPTY_i = '1')) THEN
            -- Write will be successful
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Read will fail, because core is reporting EMPTY
            underflow_i  < = '1';
            valid_i      < = '0';
            --FIFO is no longer EMPTY
            empty_i        < = '0';
            almost_empty_i < = '1';
            
            --Update count (for DATA_COUNT output)
            diff_count < = conv_std_logic_vector((size+1),C_RD_PNTR_WIDTH);
            

          --------------------
          --FIFO has two or more words in the FIFO, but is not near FULL
          --------------------
          ELSE -- size>1 and size< C_FIFO_DEPTH-1
            -- Write will be successful
            overflow_i    < = '0';
            wr_ack_i      < = '1';
            full_i        < = '0';
            almost_full_i < = '0';
            add(head, tail, DIN( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            
            --Successful read
            underflow_i  < = '0';
            valid_i      < = '1';
            --FIFO is nowhere near EMPTY
            empty_i        < = '0';
            almost_empty_i < = '0';
            
            --Update read
            read(tail, data( (C_SMALLER_DATA_WIDTH)-1 DOWNTO 0 ) );
            remove(head, tail);
            
            --Simulaneous read and write, no change in diff_count

          END IF;
 
      -------------------------------------------------------------------------
      -- Synchronous FIFO Condition #4 : Not reading or writing
      -------------------------------------------------------------------------
      ELSE -- ((WR_EN = '0') AND (RD_EN = '0'))
      
        IF (size = C_FIFO_WR_DEPTH) THEN  --FULL
           -- No write
           overflow_i    < = '0';
           wr_ack_i      < = '0';
           full_i        < = '1';
           almost_full_i < = '1';
           
           --No read
           underflow_i    < = '0';
           valid_i        < = '0';
           empty_i        < = '0';
           almost_empty_i < = '0';
           
        ELSIF (size >= C_FIFO_WR_DEPTH - 1) THEN  --ALMOST_FULL
           -- No write
           overflow_i    < = '0';
           wr_ack_i      < = '0';
           full_i        < = '0';
           almost_full_i < = '1';
           
           --No read
           underflow_i    < = '0';
           valid_i        < = '0';
           empty_i        < = '0';
           almost_empty_i < = '0';

        ELSIF (size = 1) THEN  -- ALMOST_EMPTY
           -- No write
           overflow_i    < = '0';
           wr_ack_i      < = '0';
           full_i        < = '0';
           almost_full_i < = '0';
           
           --No read
           underflow_i    < = '0';
           valid_i        < = '0';
           empty_i        < = '0';
           almost_empty_i < = '1';
           
        ELSIF (size = 0) THEN  -- EMPTY
           -- No write
           overflow_i    < = '0';
           wr_ack_i      < = '0';
           full_i        < = '0';
           almost_full_i < = '0';
           
           --No read
           underflow_i    < = '0';
           valid_i        < = '0';
           empty_i        < = '1';
           almost_empty_i < = '1';
          
        ELSE  -- Not near FULL or EMPTY
           -- No write
           overflow_i    < = '0';
           wr_ack_i      < = '0';
           full_i        < = '0';
           almost_full_i < = '0';
           
           --No read
           underflow_i    < = '0';
           valid_i        < = '0';
           empty_i        < = '0';
           almost_empty_i < = '0';

        END IF;

       
        
      END IF;  -- WR_EN, RD_EN
 


      -------------------------------------------------------------------------
      -- Programmable FULL flags
      -------------------------------------------------------------------------
        IF (C_PROG_FULL_TYPE /= 0) THEN
        
          --------------------------------
          -- Prog FULL Type 3 (single input port)
          --------------------------------
          IF (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
            --Since this is a FIFO with common clocks, we can accurately
            --predict the outcome using WR_EN and RD_EN.
            IF (
              (size >= conv_integer(PROG_FULL_THRESH)) OR
              ((size = conv_integer(PROG_FULL_THRESH)-1) AND WR_EN = '1'
               AND RD_EN = '0')
              ) THEN
                prog_full_noreg < = '1';
            END IF;
                  
            IF (((size = conv_integer(PROG_FULL_THRESH)) AND RD_EN = '1'
               AND WR_EN = '0') OR (rst_q='1' AND rst_i='0')) THEN
                prog_full_noreg < = '0';
            END IF;
            
          --------------------------------
          -- Prog FULL Type 4 (dual input ports)
          --------------------------------
          ELSIF (C_PROG_FULL_TYPE = 4) THEN
            --If we are at or above the PROG_FULL_THRESH, or we will be 
            --  next clock cycle, then assert PROG_FULL
            --Since this is a FIFO with common clocks, we can accurately
            --predict the outcome using WR_EN and RD_EN.
             IF (
               ((size = conv_integer(PROG_FULL_THRESH_ASSERT)-1) AND WR_EN = '1'
                AND RD_EN = '0')
               ) THEN
                 prog_full_noreg < = '1';
             END IF;
             IF (((size = conv_integer(PROG_FULL_THRESH_NEGATE)) AND RD_EN = '1'
                AND WR_EN = '0') OR (rst_q='1' AND rst_i='0')) THEN
                 prog_full_noreg < = '0';
             END IF;
             
          --------------------------------
          -- Prog FULL Type 2 (dual constants)
          --------------------------------
          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
            --Since this is a FIFO with common clocks, we can accurately
            --predict the outcome using WR_EN and RD_EN.
            IF ((size = C_PROG_FULL_THRESH_ASSERT_VAL-1) AND WR_EN = '1'
                AND RD_EN = '0') THEN
                prog_full_noreg < = '1';
            -- If we will be going below C_PROG_FULL_NEGATE on the next clock
            -- cycle, then de-assert PROG_FULL
            ELSIF ((size = C_PROG_FULL_THRESH_NEGATE_VAL) AND RD_EN = '1'
                   AND WR_EN = '0') THEN
                prog_full_noreg < = '0';
            -- If we are at or above the C_PROG_FULL_ASSERT, then assert
            -- PROG_FULL
            ELSIF (size >= C_PROG_FULL_THRESH_ASSERT_VAL) THEN
                prog_full_noreg < = '1';
            --If we are below the C_PROG_FULL_NEGATE, then de-assert PROG_FULL
            ELSIF (size <  C_PROG_FULL_THRESH_NEGATE_VAL) THEN
                prog_full_noreg < = '0';
            END IF;

          --------------------------------
          -- Prog FULL Type 1 (single constant)
          --------------------------------
          ELSE
            IF (
              (size >= C_PROG_FULL_THRESH_ASSERT_VAL) OR
              ((size = C_PROG_FULL_THRESH_ASSERT_VAL-1) AND WR_EN = '1'
               AND RD_EN = '0')
              ) THEN
                prog_full_noreg < = '1';
            END IF;
                  
            IF (((size = C_PROG_FULL_THRESH_ASSERT_VAL) AND RD_EN = '1'
               AND WR_EN = '0') OR (rst_q='1' AND rst_i='0')) THEN
                prog_full_noreg < = '0';
            END IF;
                        
          END IF; --C_PROG_FULL_TYPE

          if (rst_q='1' and rst_i='0') then
            prog_full_reg < = '0';
          else 
            prog_full_reg < = prog_full_noreg;
          end if;

        END IF;  --C_PROG_FULL_TYPE /= 0

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

        IF C_PROG_EMPTY_TYPE /= 0 THEN


          --------------------------------
          -- Prog EMPTY Type 3 (single input port)
          --------------------------------
          IF 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 = conv_integer(PROG_EMPTY_THRESH)+1) AND RD_EN = '1' AND WR_EN = '0') THEN
              prog_empty_noreg      < = '1';
            ELSIF ((size = conv_integer(PROG_EMPTY_THRESH)) AND WR_EN = '1' AND RD_EN = '0') THEN
              prog_empty_noreg      < = '0';
            ELSIF ((size < = conv_integer(PROG_EMPTY_THRESH)) AND RD_EN = '1') THEN
              prog_empty_noreg  < = '1';           
            END IF; --C_PROG_EMPTY_TYPE = 3


          --------------------------------
          -- Prog EMPTY Type 4 (dual input ports)
          --------------------------------
          ELSIF C_PROG_EMPTY_TYPE = 4 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 = conv_integer(PROG_EMPTY_THRESH_ASSERT)+1) AND RD_EN = '1'
               AND WR_EN = '0')
              ) THEN
                prog_empty_noreg      < = '1';
            END IF;
            
            IF (
              ((size = conv_integer(PROG_EMPTY_THRESH_NEGATE)) AND WR_EN = '1'
               AND RD_EN = '0')) THEN
                prog_empty_noreg      < = '0';   
            END IF; --C_PROG_EMPTY_TYPE = 4            


          --------------------------------
          -- Prog EMPTY Type 2 (dual constants)
          --------------------------------
          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
            IF ((size = C_PROG_EMPTY_THRESH_ASSERT_VAL+1) AND RD_EN = '1'
                AND WR_EN = '0') THEN
                prog_empty_noreg          < = '1';
            -- If we will be going above C_PROG_EMPTY_NEGATE on the next clock
            -- cycle, then de-assert PROG_EMPTY
            ELSIF ((size = C_PROG_EMPTY_THRESH_NEGATE_VAL) AND WR_EN = '1'
                   AND RD_EN = '0') THEN
                prog_empty_noreg          < = '0';
            -- If we are at or below the C_PROG_EMPTY_ASSERT, then assert
            -- PROG_EMPTY
            ELSIF (size < = C_PROG_EMPTY_THRESH_ASSERT_VAL) THEN
                prog_empty_noreg          < = '1';
            -- If we are above the C_PROG_EMPTY_THRESH_NEGATE, then de-assert
            -- PROG_EMPTY
            ELSIF (size > C_PROG_EMPTY_THRESH_NEGATE_VAL) THEN
                prog_empty_noreg          < = '0';
            END IF;


          --------------------------------
          -- Prog EMPTY Type 1 (single constant)
          --------------------------------
          ELSIF 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_PROG_EMPTY_THRESH_ASSERT_VAL+1) AND RD_EN = '1' AND WR_EN = '0') THEN
                prog_empty_noreg      < = '1';
            END IF;
            IF ((size = C_PROG_EMPTY_THRESH_ASSERT_VAL) AND WR_EN = '1' AND RD_EN = '0') THEN
                prog_empty_noreg      < = '0';
            END IF;
            
          END IF; -- C_PROG_EMPTY_TYPE

          prog_empty_reg < = prog_empty_noreg;
          
        END IF; --C_PROG_EMPTY_TYPE /= 0

      IF (power_on_timer >= 2) THEN
        data := hexstr_to_std_logic_vec(C_DOUT_RST_VAL, C_DOUT_WIDTH);
      END IF;
      
      IF (power_on_timer > 0) THEN
        power_on_timer < = power_on_timer - 1;
      ELSE
        power_on_timer < = 0;
      END IF;
      
    END IF;	--CLK

    DOUT < = data;

  END PROCESS;
  
END behavioral;