----------------------------------------------------------------------------------
-- Company: 
-- Engineer: Darcy Cook 
-- 
-- Create Date:    13:05:04 03/30/2009 
-- Design Name: 
-- Module Name:    global_memory - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description:  This module implements the global memory
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;

entity global_memory is
   generic
	(  		
		MAX_RAM_AWIDTH						 : integer					:= 10;
		NUM_BLOCK_RAMS						 : integer				   := 8;
		D_WIDTH                        : integer              := 16;		
		A_WIDTH								 : integer              := 13    
	);
	Port 
	( 
		clk : in std_logic;
		reset : in STD_LOGIC;
		CE : in  STD_LOGIC;
      read_write : in  STD_LOGIC;
      data : inout  STD_LOGIC_VECTOR (D_WIDTH-1 downto 0);
      address : in  STD_LOGIC_VECTOR (A_WIDTH-1 downto 0)
	);
end global_memory;

architecture Behavioral of global_memory is

signal write_data : std_logic_vector(D_WIDTH-1 downto 0);
signal all_read_data : std_logic_vector ((NUM_BLOCK_RAMS*D_WIDTH)-1 downto 0);
signal read_data : std_logic_vector(D_WIDTH-1 downto 0);
signal ram_EN : std_logic_vector (NUM_BLOCK_RAMS-1 downto 0);
signal ram_WE : std_logic_vector (NUM_BLOCK_RAMS-1 downto 0);
signal selected_index : integer range 0 to NUM_BLOCK_RAMS-1;
signal not_clk : std_logic;

begin
		
	not_clk <= not clk; --invert the clock so that the memory triggers on falling edge
	
	 -- this is the RAM index selected by the upper address bits
	selected_index <= conv_integer('0' & address(A_WIDTH-1 downto MAX_RAM_AWIDTH));	
		
	RAM_0 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data(D_WIDTH-1 downto 0),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(0),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(0)       -- Write Enable Input		
	);
	
	RAM_1 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((2*D_WIDTH)-1 downto D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(1),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(1)       -- Write Enable Input		
	);
	
	RAM_2 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((3*D_WIDTH)-1 downto 2*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(2),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(2)       -- Write Enable Input		
	);
	
	RAM_3 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((4*D_WIDTH)-1 downto 3*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(3),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(3)       -- Write Enable Input		
	);
	
	RAM_4 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((5*D_WIDTH)-1 downto 4*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(4),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(4)       -- Write Enable Input		
	);
	
	RAM_5 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((6*D_WIDTH)-1 downto 5*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(5),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(5)       -- Write Enable Input		
	);
	
	RAM_6 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((7*D_WIDTH)-1 downto 6*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(6),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(6)       -- Write Enable Input		
	);
	
	RAM_7 : RAMB16_S18 
	generic map 
	(
		INIT => X"00000", --  Value of output RAM registers at startup
      SRVAL => X"00000", --  Ouput value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST" --  WRITE_FIRST, READ_FIRST or NO_CHANGE
	)	
	port map 
	(
		DO => all_read_data((8*D_WIDTH)-1 downto 7*D_WIDTH),      -- 16-bit Data Output      
      ADDR => address(MAX_RAM_AWIDTH-1 downto 0),  -- 10-bit Address Input
      CLK => not_clk,    -- Clock
      DI => write_data,      -- 16-bit Data Input
      DIP => "00",    -- 2-bit parity Input
      EN => ram_EN(7),      -- RAM Enable Input
      SSR => reset,    -- Synchronous Set/Reset Input
      WE => ram_WE(7)       -- Write Enable Input		
	);	
		
	address_decoding : process ( selected_index, address, CE, all_read_data, read_data) is	
	begin
		
		-- this will set one of the ram_EN bits to CE and one of the ram_WE bits to read_write 
		-- and the other bits to 0, according to which RAM the address is selecting
		
		case selected_index is
			when 0 =>
				ram_EN <= (0 => CE, others =>'0');
				ram_WE <= (0 => read_write, others => '0');
			when 1 =>
				ram_EN <= (1 => CE, others =>'0');
				ram_WE <= (1 => read_write, others => '0');
			when 2 =>
				ram_EN <= (2 => CE, others =>'0');
				ram_WE <= (2 => read_write, others => '0');
			when 3 =>
				ram_EN <= (3 => CE, others =>'0');
				ram_WE <= (3 => read_write, others => '0');
			when 4 =>
				ram_EN <= (4 => CE, others =>'0');
				ram_WE <= (4 => read_write, others => '0');
			when 5 =>
				ram_EN <= (5 => CE, others =>'0');
				ram_WE <= (5 => read_write, others => '0');
			when 6 =>
				ram_EN <= (6 => CE, others =>'0');
				ram_WE <= (6 => read_write, others => '0');
			when 7 =>
				ram_EN <= (7 => CE, others =>'0');				
				ram_WE <= (7 => read_write, others => '0');
			when others => null;
		end case;
				
		--multiplex the correct output data from the selected RAM
		read_data <= all_read_data(((selected_index+1)*D_WIDTH)-1 downto selected_index*D_WIDTH);
	end process address_decoding;
		
	tristate_process : process ( CE, read_write, write_data, data, read_data ) is
	begin
		if ((CE = '1') and (read_write = '0')) then --this is when a read occurs
			data <= read_data; -- put the value from memory on the data bus
		else -- this is either the memory is inactive or a write is occuring
			data <= (others => 'Z'); -- set the data bus to high impedance
			write_data <= data; --set write_data to the data on the bus
		end if;
	end process tristate_process;	
end Behavioral;

