-------------------------------------------------------------------------------
-- sram512kleft16bit50mhzreadreq-sv05.vhd
--
-- Author(s):     James Brennan
-- Created:       15 Feb 2001
-- Last Modified: 16 Feb 2001
-- 
-- Entity: sraminterface
--
-- * Provides a simple interface to the SRAM on an XSV Board, v1.0
-- * 512k address space (512 * 1024 addressable locations, meaning
--   that addresses are 19 bits wide).
-- * 16 bit data (each location holds 16 bits). 
-- * Only uses left-bank of SRAM. (Technically you could connect this entity
--   to either bank of SRAM in your top-level file. The data and address
--   lines have simply been named assuming that the entity will be connected
--   to the left bank of SRAM).
--
-- * Writes are performed in 2 clock cycles. Reads are performed in 2 clock
--   cycles.
--
-- * Read data is NOT registered internally. It must be registered externally.
-------------------------------------------------------------------------------

-- ---------------------------------
-- Clock:
-- 50Mhz or slower required.
-- Must have a 50% duty cycle.
-- ---------------------------------

-- The two signals canRead and canWrite are are high when new requests
-- for reads and writes respectively can be made. 

-- Writes and reads can be performed in any order and can be
-- interspersed in any way.

-- To perform a write:
-- *	Place the write address on writeAddr and the write data on 
--		writeData.
-- *	Wait until canWrite is '1'. Then assert doWrite (set to '1').
--		You can assert doWrite on the same clock cycle that canWrite
--		is high. Asserting doWrite is also called making a
--		"write request".
-- *	The sraminterface will see doWrite is '1' on the next rising
--		clock edge and on the same edge will register (i.e. place in
--		registers) writeAddr and writeData. The write will take place
--		in the 2 cycles after this first edge and will be completed at
--		the third rising clock edge after doWrite is asserted.
-- To perform a read:
-- *	Place the read address on readAddr.
-- *	Wait until canRead is '1'. Then assert doRead (set to '1').
--		You can assert doRead on the same clock cycle that canRead
--		is high. Asserting doRead is also called making a
--		"read request".
-- *	The sraminterface will see doRead is '1' on the next rising
--		clock edge and on the same edge will register (i.e. place in
--		registers) the value on readAddr. The read will take place
--		over two clock cycles. In the first cycle, canRead (and
--		canWrite will be low. In the second cycle, canRead (and
--		canWrite) will be high. This indicates both that the
--		readData will be valid at the end of this cycle	and that the
--		sraminterface can at this point handle another read or write
--		request.
-- *	At the end of the clock cycle in which canRead goes high
--		again, the user of the sraminterface must register the value
--		on readData. This value must be registered, as on the next
--		clock cycle it is not guaranteed to be the same and may
--		instead change.

library ieee;
use ieee.std_logic_1164.all;

entity external_sram_interface is
    port (
        clock_in			: in 	std_logic;						-- clock signal.
        reset_n_in			: in 	std_logic;						-- synchronous reset
        do_read_in			: in 	std_logic;						-- set this to make a read request.							
        do_write_in			: in 	std_logic;						-- set this to make a write request.
        read_address_in		: in 	std_logic_vector (19 downto 0);	-- address to read from (user-side).
        write_address_in	: in 	std_logic_vector (19 downto 0);	-- address to write to (user-side).	
        write_data_in		: in 	std_logic_vector (31 downto 0);	-- data to write (user-side).
        read_data_out		: out 	std_logic_vector (31 downto 0);	-- data read (user-side).
        can_read_out		: out 	std_logic;						-- is '1' when a read request can be handled.							
        can_write_out		: out 	std_logic;						-- is '1' when a write request can be handled.
        ce_n_out			: out 	std_logic;						-- cen signal to left sram bank.
        oe_n_out			: out 	std_logic;						-- oen signal to left sram bank.
        we_n_out			: out 	std_logic;						-- wen signal to left sram bank.
        sram_address_out	: out 	std_logic_vector (19 downto 0);	-- address bus to left sram bank.
        sram_data_io		: inout std_logic_vector (31 downto 0)	-- data bus to left sram bank.
    );
end external_sram_interface;

architecture synthesis of external_sram_interface is

	-- ========================================
	-- architechture declarations:
	-- ========================================	

	-- constants:

	-- general enabled/disabled constants:
	constant const_enabled : std_logic := '1';
	constant const_disabled : std_logic := '0';
	
	-- signals for registers:
	signal addrreg : std_logic_vector(18 downto 0);
	signal writedatareg : std_logic_vector(15 downto 0);
	
	-- clock-enable controls for the registers:
	signal regwriteaddr : std_logic;
	signal regreadaddr : std_logic;
	signal regwritedata : std_logic;
	
	-- internal values of sram control signals:
	signal cen : std_logic;
	signal oen : std_logic;
	signal wen : std_logic;

	-- flip-flop signals for signalling when we are
	-- in the "write" states (these are effectively
	-- duplicating some of the presstate signal to
	-- try and improve the timing of the circuit):
	signal doingwrite1 : std_logic;
	signal doingwrite2 : std_logic;

	-- other control signals for the data path:
	signal enablewritedata : std_logic;

	-- 16-bit data bus:
	-- we are only using the left-hand side sram bank.
	signal sramdata : std_logic_vector(15 downto 0);
		
	-- signals for latched-mealy outputs:
	signal nextoen: std_logic;
	signal nextwen: std_logic;
	signal nextdoingwrite1 : std_logic;
	signal nextdoingwrite2 : std_logic;
	
	-- declarations required for the controller fsm.
	type state_type is (stidle, stwrite1, stwrite2,
			stread1, stread2);
	signal presstate, nextstate: state_type;
	
begin
	-- ========================================
	-- architecture body:
	-- ========================================

	-- ========================================
	-- combinational signals
	-- ========================================

	-- control signals:
	celeftn <= cen;
	oeleftn <= oen;
	weleftn <= wen;

	-- sram address bus:
	sramleftaddr <= addrreg;
	
	-- sram data bus:
	sramleftdata <= sramdata;

	readdata <= sramdata;
	
	-- ========================================
	-- implementation of specific structures	
	-- ========================================
	
	-- 3-state buffer placed after the writedatareg register
	-- in the data path:
	process(writedatareg, doingwrite1, doingwrite2, clk)
	begin
		-- we drive the sram data i/o bus with data to be written
		-- only at the following times:
		--		* for the 2nd half of write cycle 1
		--	and * for the entirety of write cycle 2
		--
		-- to do this we rely on the fact that clk = '1' for the
		-- first half of a clock cycle and clk = '0' for the
		-- second half of a cycle.
		-- the reason for the dependancy on clk is as follows:
		-- at the start of the clock cycle in which we are
		-- performing a write, we raise oen. this is to make the
		-- sram stop driving its bidirectional data lines and
		-- instead make its drivers high impedance. we wait half a
		-- clock cycle for the sram's data line drivers to go high
		-- impedance, and then we ourselves drive the sram's
		-- bidirectional data lines with the data that we wish to
		-- be written.
		if	(doingwrite1 = '1' and clk = '0') or
			(doingwrite2 = '1') then
			sramdata <= writedatareg;
		else
			sramdata <= (others => 'z');
		end if;
	end process;

	-- process for wen signal:
	-- this process has been placed here simply because it is similar
	-- to the 3-state buffer on the write data output above. however
	-- we do not use a 3-state buffer for wen.
	process(doingwrite1, doingwrite2, clk)
	begin
		if	(doingwrite1 = '1' and clk = '0') or
			(doingwrite2 = '1' and clk = '1') then
			wen <= '0';
		else
			wen <= '1';
		end if;
	end process;
	

	
	-- ========================================
	-- process for reset and clock-edge events
	-- ========================================
	process(clk, resetn)
	begin
		if resetn = '0' then
			-- default values of signals that are not
			-- controlled by the fsm controller:
			presstate <= stidle;
	
			cen <= '1';
			oen <= '0';

			doingwrite1 <= '0';
			doingwrite2 <= '0';

			addrreg <= (others => '0');
			writedatareg <= (others => '0');
					
		elsif clk'event and clk = '1' then
			cen <= '0';
			
			-- handle the clock-enabling of each register:
			if regreadaddr = '1' then
				addrreg <= readaddr;
			elsif regwriteaddr = '1' then
				addrreg <= writeaddr;
			end if;

			if regwritedata = '1' then
				writedatareg <= writedata;
			end if;

			-- update latched-mealy outputs:
			oen <= nextoen;
			doingwrite1 <= nextdoingwrite1;
			doingwrite2 <= nextdoingwrite2;
					
			-- update current state for controller fsm:
			presstate <= nextstate;
		end if;
	end process;

	
	-- ========================================
	-- process for fsm of controller
	-- ========================================
	process(presstate, doread, dowrite)
	begin
		-- set the defaults for all the signals this fsm
		-- controls:
		nextoen <= '0';
		nextdoingwrite1 <= '0';
		nextdoingwrite2 <= '0';

		regwriteaddr <= '0';
		regwritedata <= '0';
		regreadaddr <= '0';
		
		canread <= '1';
		canwrite <= '1';

		case presstate is
			when stidle =>
				nextstate <= stidle;
				 
				if dowrite = '1' then
					nextstate <= stwrite1;
		
					regwriteaddr <= '1';
					regwritedata <= '1';					

					nextoen <= '1';
					nextdoingwrite1 <= '1';
					nextdoingwrite2 <= '0';
				elsif doread = '1' then
					nextstate <= stread1;
					
					regreadaddr <= '1';
				end if;

			when stwrite1 =>
				nextstate <= stwrite2;
				
				nextoen <= '1';
				nextdoingwrite1 <= '0';
				nextdoingwrite2 <= '1';
				
				canread <= '0';
				canwrite <= '0';
			when stwrite2 =>
				nextstate <= stidle;

				nextoen <= '0';
				nextdoingwrite1 <= '0';
				nextdoingwrite2 <= '0';
				
				canread <= '1';
				canwrite <= '1';	

				if dowrite = '1' then
					nextstate <= stwrite1;
		
					regwriteaddr <= '1';
					regwritedata <= '1';					

					nextoen <= '1';
					nextdoingwrite1 <= '1';
					nextdoingwrite2 <= '0';
				elsif doread = '1' then
					nextstate <= stread1;
					
					regreadaddr <= '1';
				end if;

			when stread1 =>
				nextstate <= stread2;
				
				canwrite <= '0';
				canread <= '0';

			when stread2 =>
				nextstate <= stidle;				

				canwrite <= '1';
				canread <= '1';

				if dowrite = '1' then
					nextstate <= stwrite1;
		
					regwriteaddr <= '1';
					regwritedata <= '1';					

					nextoen <= '1';
					nextdoingwrite1 <= '1';
					nextdoingwrite2 <= '0';
				elsif doread = '1' then
					nextstate <= stread1;
					
					regreadaddr <= '1';
				end if;
			
				-- readaddrreg already contains the read address and
				-- is the default connection for the sram address bus.
				-- therefore all the user needs to do is register the sram
				-- data bus at the end of this clock cycle.
				
		end case;
	end process;

end synthesis;