library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
Library UNISIM;
use UNISIM.vcomponents.all;

entity HyperBus is Port(
	clk100: in std_logic;
	start: in std_logic;
	mem_regs: in std_logic; --0: Mem, 1: Regs
	burstlength: in std_logic; --0: 2Bytes, 1:64Bytes
	adr: in std_logic_vector(21 downto 0); -- 21 ... 9: 8192 Rows, 8 ... 0: 512 Words in 1 Row. (2 ... 0: 8 Words = 1 Half Page)
	snd_rcv: in std_logic; --0: send/write, 1: receive/read
	ready: out std_logic;
	--- SND ---
	data_snd: in std_logic_vector(64*8-1 downto 0);
	--- RCV --
	data_rcv_valid: out std_logic;
	data_rcv: out std_logic_vector(64*8-1 downto 0);
	--- RAM ---
	nReset: out std_logic;
	nCS: out std_logic;
	CK: out std_logic;
	nCK: out std_logic;
	PSC: out std_logic;
	nPSC: out std_logic;
	RWDS: inout std_logic;
	DQ: inout std_logic_vector(7 downto 0));
end HyperBus;

architecture Behavioral of HyperBus is

component HyperBus_IN generic(
	SYS_W: integer := 8;
	DEV_W: integer := 16);
	port(
	data_in_from_pins : in  std_logic_vector(SYS_W-1 downto 0);
	data_in_to_device : out std_logic_vector(DEV_W-1 downto 0);
	clk_in            : in  std_logic; 
	clk_out           : out std_logic;
	io_reset          : in  std_logic);
end component;

component HyperBus_Out generic(
	SYS_W: integer := 8;
	DEV_W: integer := 16);
	port(
	data_out_from_device : in  std_logic_vector(DEV_W-1 downto 0);
	data_out_to_pins     : out std_logic_vector(SYS_W-1 downto 0);
	clk_in               : in  std_logic;
	io_reset             : in  std_logic);
end component;

component HyperBus_Clocks port(
	clk_out_100          : out std_logic;
	clk_out_100_CK       : out std_logic;
	clk_out_100_PSC      : out std_logic;
	clk_in_100           : in std_logic);
end component;

signal MMCM_SYS: std_logic:='0';
signal MMCM_CK: std_logic:='0';
signal MMCM_PSC: std_logic:='0';
signal CK_enable: std_logic:='0';
signal Address_Space: std_logic:='0'; --0: Memory, 1: Registers
signal Burst_Type: std_logic:='1'; --0: wrapped, 1: linear
signal Burst_snd_rcv: std_logic:='0'; --0: send/write, 1: receive/read
signal Burst_length: std_logic:='0';  --0: 2Bytes, 1:64Bytes
signal Burst_counter_100: unsigned(7 downto 0):=(others => '1');
signal RX_Data_Counter: integer range 0 to 127:=0;
signal RX_Data: std_logic_vector(15 downto 0):=(others => '0');
signal TX_Data: std_logic_vector(15 downto 0):=(others => '0');
signal DQ_OUT: std_logic_vector(7 downto 0):=(others => '0');
signal DQ_IN: std_logic_vector(7 downto 0):=(others => '0');
signal DQ_tristate: std_logic:='0';
signal RWDS_CLK: std_logic:='0';
signal RWDS_OUT: std_logic:='0';
signal RWDS_IN: std_logic:='0';
signal RWDS_SET: std_logic:='0';
signal RWDS_tristate: std_logic:='1';
constant Resetcounter_max : integer := 2**16-1;
signal Resetcounter: integer range 0 to Resetcounter_max:=0;
signal end_Burst: integer range 0 to 255:=13;
type Reg_Array is array (0 to 91) of std_logic_vector(7 downto 0); 
signal Reg: Reg_Array :=(others => x"00");
type Rcv_Array is array (0 to 63) of std_logic_vector(7 downto 0); 
signal Rcv: Rcv_Array :=(others => x"00");

begin

HB_IN: HyperBus_IN port map( 
	data_in_from_pins => DQ_IN,
	data_in_to_device => RX_Data,                     
	clk_in => RWDS_IN,                          
	clk_out => RWDS_CLK,
	io_reset => '0');

HB_Out: HyperBus_Out port map( 
	data_out_from_device => TX_Data,
	data_out_to_pins => DQ_OUT,                        
	clk_in => MMCM_SYS,   
	io_reset => '0');
	
MMCM_HyperBus : HyperBus_Clocks port map( 
	clk_out_100 => MMCM_SYS,
	clk_out_100_CK => MMCM_CK,
	clk_out_100_PSC => MMCM_PSC,
	clk_in_100 => clk100);

DQ <= DQ_OUT when DQ_tristate = '0' else "ZZZZZZZZ";
DQ_IN <= DQ;


RWDS <= RWDS_OUT when RWDS_tristate = '0' else 'Z';
RWDS_IN <= RWDS;

process begin
	wait until rising_edge(MMCM_SYS);
	nReset <= '1';
	data_rcv_valid <= '0';
	if Resetcounter < Resetcounter_max then
		--nCS <= '1';
		Resetcounter <= Resetcounter +1;
		if Resetcounter > Resetcounter_max -400 and Resetcounter < Resetcounter_max -300 then
			nReset <= '0';
		end if;
		
	else
		if Burst_counter_100 < end_Burst+4 then
			Burst_counter_100 <= Burst_counter_100 +1;
			if Burst_counter_100 > 0 and Burst_counter_100 < 3 and RWDS_IN = '1' then
				RWDS_SET <= '1';
			end if;
			if Burst_counter_100 = 0 then
				CK_enable <= '1';
			end if;
			if Burst_counter_100 = 3 then
				if RWDS_SET = '0' then
					Burst_counter_100 <= Burst_counter_100 +7;
				end if;
			end if;
			if Burst_counter_100 = 13 then
				if Burst_snd_rcv = '0' then
					RWDS_tristate <= '0';
				end if;
			end if;
			if Burst_counter_100 = end_Burst then
				TX_Data <= x"0000";
			end if;
			if Burst_counter_100 < end_Burst then
				TX_Data <= Reg(2*to_integer(Burst_counter_100)+3) & Reg(2*to_integer(Burst_counter_100)+2);
			end if;
			if Burst_counter_100 = end_Burst+1 then
				CK_enable <= '0';
			end if;
			if Burst_counter_100 = end_Burst+2 then
				RWDS_tristate <= '0';
				if Burst_snd_rcv = '1' then
					if Burst_length = '0' then
						data_rcv(15 downto 0) <= RX_Data(15 downto 0);
						data_rcv(511 downto 16) <= (others => '0');
					else
						for I in 0 to 61 loop
							data_rcv(I*8+7 downto I*8) <= Rcv(I);
						end loop;
						data_rcv(511 downto 496) <= RX_Data(15 downto 0);
					end if;
					data_rcv_valid <= '1';
				end if;
			end if;
		else
			if start = '1' then
				RWDS_SET <= '0';
				RWDS_tristate <= '1';
				Burst_counter_100 <= (others => '0');
				Burst_length <= burstlength;
				Burst_snd_rcv <= snd_rcv;
				Address_Space <= mem_regs;
				if burstlength = '0' then
					end_Burst <= 2+6+6+1-1;
				else
					end_Burst <= 2+6+6+32-1;
				end if;
				Reg(0) <= snd_rcv & mem_regs & Burst_Type & "00000";
				Reg(1) <= "00000" & adr(21 downto 19);
				Reg(2) <= adr(18 downto 11);
				Reg(3) <= adr(10 downto 3);
				Reg(4) <= "00000000";
				Reg(5) <= "00000" & adr(2 downto 0);
				for I in 0 to 63 loop
					if snd_rcv = '0' then
						Reg(I+28) <= data_snd(I*8+7 downto I*8);
					else
						Reg(I+28) <= x"00";
					end if;
				end loop;
				TX_Data <= "00000" & adr(21 downto 19) & snd_rcv & mem_regs & Burst_Type & "00000";
			end if;
		end if;
	end if;
end process;

process (Burst_counter_100,RWDS_CLK,end_Burst,start)
begin
	if start = '1' then
		RX_Data_Counter <= 0;
	elsif rising_edge(RWDS_CLK) then
		if Burst_counter_100 < end_Burst+2 and Burst_counter_100 > 13 and Burst_snd_rcv = '1' then
			RX_Data_Counter <= RX_Data_Counter +1;
			if RX_Data_Counter > 0 then
				Rcv((RX_Data_Counter-1)*2+1) <= RX_Data(15 downto 8);
				Rcv((RX_Data_Counter-1)*2) <= RX_Data(7 downto 0);
			end if;
		end if;
	end if;
end process;

nCS <= '0' when (start = '1' or Burst_counter_100 < end_Burst+3) and Resetcounter = Resetcounter_max else '1';

ready <= '1' when start = '0' and Burst_counter_100 > end_Burst+3 else '0';

DQ_tristate <= '0' when Burst_counter_100 < 5 or Burst_snd_rcv = '0' else '1';

--PSC <= MMCM_PSC when Burst_counter_100 > 13 and Burst_counter_100 < end_Burst+2 and Burst_snd_rcv = '1' else '0';
--nPSC <= not MMCM_PSC when Burst_counter_100 > 13 and Burst_counter_100 < end_Burst+2 and Burst_snd_rcv = '1' else '1';

PSC <= 'Z';
nPSC <= 'Z';

CK <= MMCM_CK when CK_enable = '1' else '0';
nCK <= not MMCM_CK when CK_enable = '1' else '1';

end Behavioral;