----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:04:58 09/24/2009 
-- Design Name: 
-- Module Name:    FIFO - beh 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;

-- clk and reset needed for FIFO. rd increments to read pointer to the next element
-- in the queue (actually removes an element), r_data is ALWAYS available on the output
-- and is defined by the current value of the r_ptr_reg. When wr is asserted, on next
-- rising edge, w_data is placed into the FIFO and THEN the w_ptr_reg is incremented.
-- After initialization, both r_ptr_reg and w_ptr_reg point to the same element in
-- the FIFO.
entity FIFO is
   Generic(
      NUM_DATA_BITS: natural := 8;
      NUM_ADDR_BITS: natural := 4);
   Port ( 
      clk : in  std_logic;
      reset : in  std_logic;
      rd, wr : in std_logic;
      w_data : in std_logic_vector(NUM_DATA_BITS-1 downto 0);
      empty, full : out  std_logic;
      r_data : out std_logic_vector(NUM_DATA_BITS-1 downto 0)
      );
end FIFO;

----------------------------------------------------------------------------------
-- All operations occur in one clock cycle
architecture beh of FIFO is
   type reg_file_type is array (2**NUM_ADDR_BITS-1 downto 0) of
      std_logic_vector(NUM_DATA_BITS-1 downto 0); 
   signal array_reg : reg_file_type;
   signal w_ptr_reg, w_ptr_next, w_ptr_succ : std_logic_vector(NUM_ADDR_BITS-1 downto 0);
   signal r_ptr_reg, r_ptr_next, r_ptr_succ : std_logic_vector(NUM_ADDR_BITS-1 downto 0);
   signal full_reg, empty_reg, full_next, empty_next : std_logic;
   signal wr_op : std_logic_vector(1 downto 0);
   signal wr_en : std_logic;

   begin

-- Read port. After initialization, register 0's value is held on the output port
   r_data <= array_reg(to_integer(unsigned(r_ptr_reg))); 
   
-- Write enabled ONLY when FIFO is not full
   wr_en <= wr and (not full_reg);

----------------------------------
-- register file
----------------------------------
   process (clk, reset)
      begin
      if ( reset = '1' ) then
         array_reg <= (others => (others => '0'));
      elsif ( clk'event and clk = '1' ) then
         if ( wr_en = '1' ) then
            array_reg(to_integer(unsigned(w_ptr_reg))) <= w_data;
         end if;
      end if;
   end process;

----------------------------------
-- FIFO sequential logic
----------------------------------
   process (clk, reset)
      begin
      if (reset = '1') then
         w_ptr_reg <= (others => '0');
         r_ptr_reg <= (others => '0');
         full_reg <= '0';
         empty_reg <= '1';
      elsif (clk'event and clk = '1') then
         w_ptr_reg <= w_ptr_next;
         r_ptr_reg <= r_ptr_next;
         full_reg <= full_next;
         empty_reg <= empty_next;
      end if;
   end process;

-- successive pointer values
   w_ptr_succ <= std_logic_vector(unsigned(w_ptr_reg)+1);
   r_ptr_succ <= std_logic_vector(unsigned(r_ptr_reg)+1);

----------------------------------
-- FIFO next state logic
----------------------------------
   wr_op <= wr & rd;
   process (w_ptr_reg, w_ptr_succ, r_ptr_reg, r_ptr_succ, wr_op, empty_reg, full_reg)
      begin

-- defaults
      w_ptr_next <= w_ptr_reg;
      r_ptr_next <= r_ptr_reg;
      full_next <= full_reg;
      empty_next <= empty_reg;

-- case out requested operation
      case wr_op is

-- no op
         when "00" => 

-- read
         when "01" => 
            if ( empty_reg /= '1' ) then
               r_ptr_next <= r_ptr_succ;
               full_next <= '0';
               if ( r_ptr_succ = w_ptr_reg ) then
                  empty_next <= '1';
               end if;
            end if;
               
-- write
         when "10" => 
            if ( full_reg /= '1' ) then
               w_ptr_next <= w_ptr_succ;
               empty_next <= '0';
               if ( w_ptr_succ = r_ptr_reg ) then
                  full_next <= '1';
               end if;
            end if;

-- write/read
         when others => 
            w_ptr_next <= w_ptr_succ;
            r_ptr_next <= r_ptr_succ;
      end case;
   end process;

-- output state
   full <= full_reg;
   empty <= empty_reg;

end beh;

