LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY ps2_wb IS
	Generic
	(
		f_sys_clk	: real	:= 100.0
	);
	Port
	(
		CLK_I		: in  STD_LOGIC;
		RST_I		: in  STD_LOGIC;
		CYC_I		: in  STD_LOGIC;
		STB_I		: in  STD_LOGIC;
		SEL_I		: in  unsigned(3 downto 0);
		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);
		INT_O		: out STD_LOGIC;
		clk_rx		: in std_logic;
		data_rx		: in std_logic;
		clk_tx		: out std_logic;
		data_tx		: out std_logic
	);
END ps2_wb;

ARCHITECTURE behavior OF ps2_wb IS


	-- Signals for ps2 connections
	signal reg_we_ps2_tx	: std_logic;
	signal reg_re_ps2_rx	: std_logic;
	signal reg_ps2_tx		: unsigned(7 downto 0);
	signal reg_ps2_rx		: unsigned(7 downto 0);
	signal rx_data_present	: std_logic;
	signal ps2_status_port	: unsigned(15 downto 0);
	signal rx_int_en		: std_logic;
	signal tx_int_en		: std_logic;
	signal irq_rx			: std_logic;
	signal irq_tx			: std_logic;
	signal tx_empty			: std_logic;
	signal rx_err_flag		: std_logic;
	signal rx_err_par		: std_logic;
	signal stat_clk			: std_logic;
	signal stat_data		: std_logic;
	
begin

	SRDY_O	<= CYC_I;
	
------------------------------------------------------------------
registers_write: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			reg_we_ps2_tx	<= '0';
			if RST_I = '1' then
				rx_int_en		<= '0';
				tx_int_en		<= '0';
			elsif (STB_I and CYC_I and WE_I) = '1' then
				case ADDR_I(5 downto 2) is
				
				when "0000" =>
					reg_we_ps2_tx	<= '1';
					reg_ps2_tx		<= DAT_I(7 downto 0);
					
				when "0001" =>
					rx_int_en		<= DAT_I(6);
					tx_int_en		<= DAT_I(5);
					
				when others => null;
				end case;
			end if;	
		end if;	
	end process;

registers_read: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			reg_re_ps2_rx	<= '0';
			ACK_O			<= '0';
			if (STB_I and CYC_I) = '1' then
				ACK_O		<= not WE_I;
				DAT_O		<= (others => '0');
				case ADDR_I(5 downto 2) is

				when "0000" =>
					reg_re_ps2_rx		<= not WE_I;
					DAT_O(7 downto 0)	<= unsigned(reg_ps2_rx);

				when "0001" =>
					DAT_O(15 downto 0)	<= ps2_status_port;

				when others => null;
				end case;
			end if;
		end if;	
	end process;

irq_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			INT_O		<= irq_rx or irq_tx;
			irq_tx		<= tx_empty and tx_int_en;
			irq_rx		<= rx_data_present and rx_int_en;
			stat_clk	<= clk_rx;
			stat_data	<= data_rx;
		end if;
	end process;

inst_ps2_core: entity work.ps2_core 
	GENERIC MAP
	(
		f_sys_clk => f_sys_clk
	)
	PORT MAP
	(
		rst => RST_I,
		clk => CLK_I,
		din => reg_ps2_tx,
		dout => reg_ps2_rx,
		tx_empty => tx_empty,
		rx_present => rx_data_present,
		rx_err_flag => rx_err_flag,
		rx_err_par => rx_err_par,
		din_vld => reg_we_ps2_tx,
		read_en => reg_re_ps2_rx,
		ps2_clk_rx => clk_rx,
		ps2_data_rx => data_rx,
		ps2_clk_tx => clk_tx,
		ps2_data_tx => data_tx
	);

	ps2_status_port	<= rx_err_flag & "000" & rx_err_par & '0' & irq_rx & irq_tx & '0' & rx_int_en & tx_int_en & stat_data & stat_clk & rx_data_present & '0' & tx_empty;

end behavior;
