library IEEE;
use IEEE.std_logic_1164.all;

entity sram_controller is
	port(	REF_CLK, RESET_N : in std_logic;
			
			WR1_Data : in std_logic_vector(14 downto 0);
			WR1 : in std_logic;
			WR1_ADDR, WR1_MAX_ADDR : in std_logic_vector(21 downto 0);
			WR1_Length : in std_logic_vector(8 downto 0);
			WR1_Load, WR1_CLK : in std_logic;
			WR1_Full : out std_logic;
			WR1_Use : out std_logic_vector(8 downto 0);
			
			WR2_Data : in std_logic_vector(14 downto 0);
			WR2 : in std_logic;
			WR2_ADDR, WR2_MAX_ADDR : in std_logic_vector(21 downto 0);
			WR2_Length : in std_logic_vector(8 downto 0);
			WR2_Load, WR2_CLK : in std_logic;
			WR2_Full : out std_logic;
			WR2_Use : out std_logic_vector(8 downto 0);	
		
			RD1_Data : out std_logic_vector(14 downto 0);
			RD1 : in std_logic;
			RD1_ADDR, RD1_MAX_ADDR : in std_logic_vector(21 downto 0);
			RD1_Length : in std_logic_vector(8 downto 0);
			RD1_Load, RD1_CLK : in std_logic;
			RD1_Empty : out std_logic;
			RD1_Use : out std_logic_vector(8 downto 0);
			
			RD2_Data : out std_logic_vector(14 downto 0);
			RD2 : in std_logic;
			RD2_ADDR, RD2_MAX_ADDR : in std_logic_vector(21 downto 0);
			RD2_Length : in std_logic_vector(8 downto 0);
			RD2_Load, RD2_CLK : in std_logic;
			RD2_Empty : out std_logic;
			RD2_Use : out std_logic_vector(8 downto 0);	
			
			SA : out std_logic_vector(11 downto 0);
			BA : out std_logic_vector(1 downto 0);
			CS_N : out std_logic;
			CKE, RAS_N, CAS_N, WE : out std_logic;
			DQ : inout std_logic_vector(15 downto 0);
			DQM : out std_logic_vector(1 downto 0);
			SDR_CLK : out std_logic
		);
end sram_controller;

architecture datapath of sram_controller is
	
	component Sdram_PLL is
		port(	inclk : in std_logic;
				c0, c1 : out std_logic
		);
	end component;

	component command is
		port(	CLK, RESET_N, NOP, 
				READA, WRITEA, REFRESH,
				PRECHARGE, LOAD_MODE, REF_REQ,
				INIT_REQ, PM_STOP, PM_DONE : in std_logic;
				
				SADDR : in std_logic_vector(22 downto 0);
				
				REF_ACK, CM_ACK, OE, CKE,
				RAS_N, CAS_N, WE_N : out std_logic;
				
				BA, CS_N : out std_logic_vector(1 downto 0);
				SA : out std_logic_vector(11 downto 0)		
		);
	end component;
		
	component control_interface is
		port(	CLK, RESET_N, REF_ACK,
				CM_ACK : in std_logic;
				
				CMD : in std_logic_vector (2 downto 0);
				ADDR : in std_logic_vector(22 downto 0);
				
				NOP, READA, WRITEA, REFRESH, PRECHARGE,
				LOAD_MODE, REF_REQ, INIT_REQ, 
				CMD_ACK : out std_logic;
				
				SADDR : out std_logic_vector(22 downto 0)		
		);
	end component;
		
	component sdr_data_path is
		port(	CLK, RESET_N : in std_logic;
				DATAIN : in std_logic_vector(15 downto 0);
				DM : in std_logic_vector(1 downto 0);
				
				DQOUT : out std_logic_vector(15 downto 0);
				DQM : out std_logic_vector(1 downto 0)		
		);
	end component;
		
	component Sdram_FIFO is
		port(	aclr, rdclk, rdreq,
				wrclk, wrreq : in std_logic;
				data : in std_logic_vector(15 downto 0);
				
				rdempty, wrfull : out std_logic;
				rdusedw, wrusedw : out std_logic_vector(8 downto 0);
				q : out std_logic_vector(15 downto 0)		
		);
	
	end component;
	
	signal 	mADDR, rWR1_ADDR, rWR1_MAX_ADDR,
			rWR2_ADDR, rWR2_MAX_ADDR, saddr,
			rRD1_ADDR, rRD1_MAX_ADDR, rRD2_ADDR,
			rRD2_MAX_ADDR : std_logic_vector(22 downto 0);
	
	signal	mDATA_OUT, mDATA_IN, mDATA_IN1,
			mDATA_IN2, DQOUT : std_logic_vector(15 downto 0);
			
	signal 	mLength, rWR1_Length, rWR2_Length, 
			rRD1_Length, rRD2_Length,
			write_side_fifo_rusedw1,
			read_side_fifo_wusedw1,
			write_side_fifo_rusedw2,
			read_side_fifo_wusedw2: std_logic_vector(8 downto 0);
			
	signal	WR_Mask, RD_Mask, CMD, br,
			cs_l, IBA, ICS_N, IDQM : std_logic_vector(1 downto 0);
			
	signal	mWR_Done, mRD_Done, mWR, Pre_WR,
			mRD, Pre_RD, PM_Stop, PM_Done,
			Read, Write, CMDACK, ClE, RlS_N,
			ClS_N, Wl_N, ICKE, IRAS_N, ICAS_N,
			IWE_N, OUT_Valid, IN_REQ, load_mode,
			sop, readi, writei, refresh, precharge,
			oe, ref_ack, ref_req, init_req, cm_ack,
			active, CLK : std_logic;
			
	signal	SADD, ISA : std_logic_vector(11 downto 0);
			
	signal ST : std_logic_vector(9 downto 0);	
	
begin
	sdram_pll1 : Sdram_PLL port map(
		inclk => REF_CLK,
		c0 => CLK,
		c1 => SDR_CLK
	);
	
	control1 : control_interface port map(
		CLK => CLK,
		RESET_N => RESET_N,
		CMD => CMD,
		ADDR => mADDR,
		REF_ACK => ref_ack,
		CM_ACK => cm_ack,
		NOP => sop,
		READA => readi,
		WRITEA => writei,
		REFRESH => refresh,
		PRECHARGE => precharge,
		LOAD_MODE => load_mode,
		SADDR => saddr,
		REF_REQ => ref_req,
		INIT_REQ => init_req,
		CMD_ACK => CMDACK
	);
	
	command1 : command port map(
		CLK => CLK,
		RESET_N => RESET_N,
		SADDR => saddr,
		NOP => sop,
		READA => readi,
		WRITEA => writei,
		REFRESH => refresh,
		PRECHARGE => precharge,
		LOAD_MODE => load_mode,
		REF_REQ => ref_req,
		INIT_REQ => init_req,
		REF_ACK => ref_ack,
		CM_ACK => cm_ack,
		OE => oe,
		PM_STOP => PM_Stop,
		PM_DONE => PM_Done,
		SA => ISA,
		BA => IBA,
		CS_N => ICS_N,
		CKE => ICKE,
		RAS_N => IRAS_N,
		CAS_N => ICAS_N,
		WE_N => IWE_N
	);
	
	data_path1 : sdr_data_path port map(
		CLK => CLK,
		RESET_N => RESET_N,
		DATAIN => mDATA_IN,
		DM => "00",
		DQOUT => DQOUT,
		DQM => IDQM
	);
	
	write_fifo1 : Sdram_FIFO port map(
		data => WR1_Data,
		wrreq => WR1,
		wrclk => WR1_CLK,
		aclr => WR1_Load,
		rdreq => (IN_REQ and WR_Mask(0)),
		rdclk => CLK,
		q => mDATA_IN1,
		wrfull => WR1_Full,
		wrusedw => WR1_Use,
		rdusedw => write_side_fifo_rusedw1
	);
	
	write_fifo2 : Sdram_FIFO port map(
		data => WR2_Data,
		wrreq => WR2,
		wrclk => WR2_CLK,
		aclr => WR2_Load,
		rdreq => (IN_REQ and WR_Mask(1)),
		rdclk => CLK,
		q => mDATA_IN1,
		wrfull => WR2_Full,
		wrusedw => WR2_Use,
		rdusedw => write_side_fifo_rusedw2
	);
	
	process(mData_IN, mDATA_IN1, mData_IN2)
	begin
		if(WR_Mask(0)= '1') then
			mData_IN <= mData_IN1;
		else
			mData_IN <= mData_IN2;
		end if;
	end process;
	
	read_fifo1 : Sdram_FIFO port map(
		data => mData_OUT,
		wrreq => (OUT_Valid and RD_Mask(0)),
		wrclk => CLK,
		aclr => RD1_Load,
		rdreq => RD1,
		rdclk => RD1_CLK,
		q => RD1_Data,
		wrusedw => read_side_fifo_wusedw1,
		rdempty => RD1_Empty,
		rdusedw => RD1_Use
	);
	
	read_fifo2 : Sdram_FIFO port map(
		data => mData_OUT,
		wrreq => (OUT_Valid and RD_Mask(1)),
		wrclk => CLK,
		aclr => RD2_Load,
		rdreq => RD2,
		rdclk => RD2_CLK,
		q => RD2_Data,
		wrusedw => read_side_fifo_wusedw2,
		rdempty => RD2_Empty,
		rdusedw => RD2_Use
	);
	
	process(CLK)
	begin
		if (ST = (mLength sla 3)) then
			SADD <= "000000000000";
		else
			SADD <= ISA;
		end if;
		
		br <= IBA;
		cs_l <= ICS_N;
		ClE <= ICKE;
		
		if (ST = (mLength sla 3)) then
			RlS_N <= '0';
		else
			RlS <= IRAS_N;
		end if;

		if (ST = (mLength sla 3)) then
			ClS_N <= '1';
		else
			ClS_N <= ICAS_N;
		end if;
		
		if (ST = (mLength sla 3)) then
			Wl_N <= '0';
		else
			Wl_N <= IWE_N;
		end if;
		
		if (ST = (mLength sla 3)) then
			PM_Stop <= '1';
		else
			PM_Stop <= '0';
		end if;
		
		if (ST = ((mLength sla 3) sla 2)) then
			PM_Done <= '1';
		else 
			PM_Done <= '0';
		end if;
		
		if(active and (ST >= "0000000011")) then
			if ((ST = (mLength sla 3)) and Write) then
				DQM <= "11";
			else
				DQM <= "00";
			end if;
		else 
			DQM <= "11";
		end if;
		mData_OUT <= DQ;
	end process;
		
	process(oe)
	begin
		if(oe) then
			DQ <= DQOUT;
		else
			DQ <= "zzzzzzzzzzzzzzzz";
		end if;
		
		active <= Read or Write;
	end process;
	
	process(CLK, RESET_N)
	begin
		if(RESET_N = 0) then
			CMD <= "00";
			ST <= "0000000000";
			Pre_RD <= '0';
			Read <= '0';
			OUT_Valid <= '0';
			IN_REQ <= '0';
			mWR_Done <= '0';
			mRD_Done <= '0';
		else
			Pre_RD <= mRD;
			Pre_WR <= mWR;
			case ST is
				when 0 =>
					if((Pre_RD&mRD) = "01")then
						Read <= 1;
						Write <= 0;
						CMD <= "01";
						ST <= 1;
					elsif((Pre_WR&mWR) = "01")then
						Read <= 0;
						Write <= 1;
						CMD <= "10";
						ST <= 1;
					end if;
				
				when 1 =>
					if( CMDACK = 1) then
						CMD <= "00";
						ST <= 2;
					end if;
				
				when others =>
					if(ST /= (((mLength sla 3) sla 3) sla 1)) then
						ST <= ST sla 1;
					else
						ST <= 0;
					end if;
			end case;
			
			if(Read) then
				if( ST = (((mLength sla 3) sla 3) sla 1)) then
					OUT_Valid <= '1';
				elsif(ST = (((mLength sla 3) sla 3) sla 1))then
					OUT_Valid <= '0';
					Read <= '0';
					mRD_Done <= '1';
				end if;
			else
				mRD_Done <= '0';
				if(ST = ("000000011" sra 1))then
					IN_REQ <= '1';
				elsif(ST = ((mLength sla 3) sra 1)) then
					IN_REQ <= '0';
				elsif(ST = (mLength sla 3))then
					Write <= '0';
					mWR_Done <= '1';	
				else
					mWR_Done <= '0';
				end if;
			end if;
		end if;
	end process;
	
	process(CLK,RESET_N)
	begin
		if(not RESET_N) then
			rWR1_ADDR <= "0000000000000000000000";
			rWR2_ADDR <= "0100000000000000000000";
			rRD1_ADDR <= "0000000010100000000000";
			rRD2_ADDR <= "0100000010100000000000";
			rWR1_MAX_ADDR <= "0001010000000000000000";
			rWR2_MAX_ADDR <= "0101010000000000000000";
			rRD1_MAX_ADDR <= "0001001101100000000000";
			rRD2_MAX_ADDR <= "0101001101100000000000";
			rWR1_Length <= "100000000";
			rWR2_Length <= "100000000";
			rRD1_Length <= "100000000";
			rRD2_Length <= "100000000";
		else
			if(WR1_Load)then
				rWR1_ADDR <= WR1_ADDR;
				rWR1_Length <= WR1_Length;
			elsif(mWR_Done and WR_Mask(0)) then
				if(rWR1_ADDR < (rWR1_MAX_ADDR - rWR1_Length)) then
					rWR1_ADDR <= rWR1_ADDR+rWR1_Length;
				else
					rWR1_ADDR <= WR1_ADDR;
				end if;
			end if;
			
			if(WR2_Load)then
				rWR2_ADDR <= WR2_ADDR;
				rWR2_Length <= WR2_Length;
			elsif(mWR_Done and WR_Mask(1)) then
				if(rWR2_ADDR < (rWR2_MAX_ADDR - rWR2_Length)) then
					rWR2_ADDR <= rWR2_ADDR+rWR1_Length;
				else
					rWR2_ADDR <= WR2_ADDR;
				end if;
			end if;

			if(RD1_Load)then
				rRD1_ADDR <= RD1_ADDR;
				rRD1_Length <= RD1_Length;
			elsif(mRD_Done and RD_Mask(0)) then
				if(rRD1_ADDR < (rRD1_MAX_ADDR - rRD1_Length)) then
					rRD1_ADDR <= rRD1_ADDR+rRD1_Length;
				else
					rRD1_ADDR <= RD1_ADDR;
				end if;
			end if;			

			if(RD2_Load)then
				rRD2_ADDR <= RD2_ADDR;
				rRD2_Length <= RD2_Length;
			elsif(mRD_Done and RD_Mask(1)) then
				if(rRD2_ADDR < (rRD2_MAX_ADDR - rRD2_Length)) then
					rRD2_ADDR <= rRD2_ADDR+rRD2_Length;
				else
					rRD2_ADDR <= RD2_ADDR;
				end if;
			end if;				
		end if;
	end process;
	
	process(CLK,RESET_N)
	begin
		if(not RESET_N)then
			mWR <= '0';
			mRD <= '0';
			mADDR <= "00000000000000000000000";
			mLength <= "0000000000";
		else
			if(	(mWR=0)and(mRD=0)and(ST=0)and(WR_Mask=0)and
				(RD_Mask=0)and(WR1_Load)and(WR2_Load=0)and
				(RD1_Load=0)and(RD2_Load=0)) then
			
				if((write_side_fifo_rusedw1 >= rWR1_Length)and(rWR1_Length/=0))then
					mADDR <= rWR1_ADDR;
					mLength <= rWR1_Length;
					WR_Mask <= "01";
					RD_Mask <= "00";
					mWR <= '1';
					mRD <= '0';
				elsif((write_side_fifo_rusedw2 >= rWR2_Length) and (rWR2_Length /= 0)) then
					mADDR <= rWR2_ADDR;
					mLength <= rWR2_Length;
					WR_Mask <= "10";
					RD_Mask <= "00";
					mWR <= '1';
					mRD <= '0';
				elsif((read_side_fifo_wusedw1 < rRD1_Length)) then
					mADDR <= rRD1_ADDR;
					mLength <= rRD1_Length;
					WR_Mask <= "00";
					RD_Mask <= "01";
					mWR <= '0';
					mRD <= '1';
				elsif((read_side_fifo_wusedw2 < rRD2_Length)) then
					mADDR <= rRD2_ADDR;
					mLength <= rRD2_Length;
					WR_Mask <= "00";
					RD_Mask <= "10";
					mWR <= '0';
					mRD <= '1';
				end if;
			end if;
			
			if(mWR_Done)then
				WR_Mask <= "00";
				mWR <= '0';
			end if;
			
			if(mRD_Done) then
				RD_Mask <="00";
				mRD <= '0';
			end if;
		end if;
	end process;	 
end datapath;
