-----------------------------------------------------------------------
-- $Header: D:\usr\cvsroot/VHDL/lib/misc/flash_port_wb.vhd,v 1.2 2010/03/13 11:06:43 Jens Exp $
-----------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.async_types.all;

	------------------------------------------------------------------
entity flash_port_wb is
	Generic
	(
		f_sysclk		: real		:= 100.0;
		addr_width		: natural	:= 32;
		data_width		: natural	:= 32;
		async_timespec	: async_timespec_t
	);
	Port
	(
		CLK_I		: in  STD_LOGIC;
		RST_I		: in  STD_LOGIC;
		CYC_I		: in  STD_LOGIC;
		STB_I		: in  STD_LOGIC;
		WE_I		: in  STD_LOGIC;
		ACK_O		: out STD_LOGIC;
		SRDY_O		: out STD_LOGIC;
		MRDY_I		: in  STD_LOGIC;
		ADDR_I		: in  unsigned(31 downto 0);
		DAT_I		: in  unsigned(31 downto 0);
		DAT_O		: out unsigned(31 downto 0);
		port_bsyi	: in std_logic;
		port_bsyo	: out std_logic;
		port_a		: out unsigned(addr_width-1 downto 0);
		port_di		: in  unsigned(data_width-1 downto 0);
		port_do		: out unsigned(data_width-1 downto 0);
		port_d_drv	: out STD_LOGIC;
		port_wr		: out STD_LOGIC;
		port_rd		: out STD_LOGIC;
		flash_cs	: out std_logic;
		flash_rst	: out std_logic
	);
end flash_port_wb;

architecture Behavioral of flash_port_wb is

	type async_t is record
		cs		: std_logic;
		wr		: std_logic;
		rd		: std_logic;
		rst		: std_logic;
		drive_d	: std_logic;
	end record;
	
	type async_state_t is (start, reset, idle, go, leadin_rd, read, leadin_wr, write, leadout_rd, leadout_wr, release);
	
	signal s, sn		: async_state_t;
	signal as			: async_t;
	signal ack			: std_logic;
	signal cc_rst		: std_logic;
	signal cycle_cnt	: natural range 0 to 31;
	signal cycle_reload	: natural range 0 to 31;
	signal rdy			: std_logic;
	signal rdyo			: std_logic;
	signal en			: std_logic;
	signal we_reg		: std_logic;
	signal data_reg		: unsigned(data_width-1 downto 0);
	signal addr_reg		: unsigned(addr_width-1 downto 0);

	------------------------------------------------------------------
	begin

	SRDY_O		<= CYC_I and rdyo;
	en			<= CYC_I and STB_I;
	port_a		<= addr_reg;
	port_do		<= data_reg;
	port_d_drv	<= as.drive_d;
	port_wr		<= (not async_timespec.pol_we) xor as.wr;
	port_rd		<= (not async_timespec.pol_oe) xor as.rd;
	
	proc_cycle_counter:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if cc_rst = '1' then
				cycle_cnt <= cycle_reload;
			elsif cycle_cnt /= 0 then
				cycle_cnt <= cycle_cnt - 1;
			end if;
		end if;
	end process;
	
	------------------------------------------------------------------
	proc_state_next:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				s <= start;
			else
				s <= sn;
			end if;
		end if;
	end process;
	
	proc_state:
	process(s, cycle_cnt, en, we_reg, port_bsyi)
	begin
		
		cycle_reload	<= to_cycles(async_timespec.T_pulse_rst, f_sysclk);
		cc_rst			<= '0';
		as.rst			<= '0';
		as.cs			<= '0';
		as.wr			<= '0';
		as.rd			<= '0';
		as.drive_d		<= '0';
		ack				<= '0';
		rdy				<= '0';
		port_bsyo		<= '1';
		
		sn	<= s;
		case s is
		
		when start =>
			cc_rst	<= '1';
			sn		<= reset;
		when reset =>
			as.rst	<= '1';
			if cycle_cnt = 0 then
				sn	<= idle;
			end if;
		when idle =>
			port_bsyo	<= '0';
			rdy			<= '1';
			if en = '1' then
				sn		<= go;
			end if;	
		when go =>
			port_bsyo	<= '0';
			if port_bsyi = '0' then
				as.cs			<= '1';
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_leadin, f_sysclk)-1;
				if we_reg = '0' then
					sn				<= leadin_rd;
				else
					sn				<= leadin_wr;
				end if;
			end if;
		when leadin_rd =>
			as.cs		<= '1';
			if cycle_cnt = 0 then
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_pulse_rd, f_sysclk)-1;
				as.rd			<= '1';
				sn				<= read;
			end if;

		when leadin_wr =>
			as.cs		<= '1';
			if cycle_cnt = 0 then
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_pulse_wr, f_sysclk)-1;
				as.wr			<= '1';
				as.drive_d		<= '1';
				sn				<= write;
			end if;
	
		when read =>
			as.cs		<= '1';
			as.rd		<= '1';
			if cycle_cnt = 0 then
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_leadout, f_sysclk)-1;
--				as.rd			<= '0';
				sn				<= leadout_rd;
				ack				<= '1';
			end if;
				
		when write =>
			as.drive_d	<= '1';
			as.cs		<= '1';
			as.wr		<= '1';
			if cycle_cnt = 0 then
				as.wr			<= '0';
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_leadout, f_sysclk)-1;
				sn				<= leadout_wr;
--				ack				<= '1';
			end if;

		when leadout_rd =>
			as.cs		<= '1';
			if cycle_cnt = 0 then
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_release, f_sysclk)-1;
				sn				<= release;
				as.cs			<= '0';
			end if;

		when leadout_wr =>
			as.cs		<= '1';
			as.drive_d	<= '1';
			if cycle_cnt = 0 then
				cc_rst			<= '1';
				cycle_reload	<= to_cycles(async_timespec.T_release, f_sysclk)-1;
				sn				<= release;
				as.cs			<= '0';
			end if;

		when release =>
			if cycle_cnt = 0 then
				sn				<= idle;
			end if;

		when others =>
			sn <= idle;
			
		end case;		
	end process;

	------------------------------------------------------------------
	output_ctrl:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			flash_cs	<= (not async_timespec.pol_cs) xor as.cs;
			flash_rst	<= (not async_timespec.pol_rst) xor as.rst;
		end if;
	end process;

	------------------------------------------------------------------
	din_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if as.rd = '1' then
				DAT_O(data_width-1 downto 0) 	<= port_di;
			end if;	
		end if;
	end process;

	------------------------------------------------------------------
	data_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if en = '1' and rdy = '1' then
				we_reg		<= WE_I;
				data_reg 	<= DAT_I(data_width-1 downto 0);
			end if;	
		end if;
	end process;

	------------------------------------------------------------------
	addr_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if en = '1' and rdy = '1' then
				addr_reg 	<= ADDR_I(addr_width-1 downto 0);
			end if;	
		end if;
	end process;

	------------------------------------------------------------------
	output_SRDY:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				rdyo 	<= '1';
			elsif en = '1' then
				rdyo 	<= rdy and not rdyo;
			end if;	
		end if;
	end process;

	------------------------------------------------------------------
	output_ACK:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				ACK_O 	<= '0';
			else
				ACK_O 	<= ack;
			end if;	
		end if;
	end process;

	------------------------------------------------------------------

end Behavioral;
