--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:    18:05:41 10/22/05
-- Design Name:    
-- Module Name:    ps2_core - Behavioral
-- Project Name:   
-- Target Device:  
-- Tool versions:  
-- Description:
--
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity ps2_core is
	 Generic (f_sys_clk    : real := 100.0);
    Port ( rst : in std_logic;
           clk : in std_logic;
           ce : in std_logic;
           d_in : in std_logic_vector(7 downto 0);
           d_out : out std_logic_vector(7 downto 0);
           rdy_in : out std_logic;
           rdy_out : out std_logic;
           write_en : in std_logic;
           read_en : in std_logic;
           ps2_clk : in std_logic;
           ps2_data : in std_logic);

end ps2_core;

architecture Behavioral of ps2_core is

	COMPONENT prescaler
	GENERIC (divide : integer);
	PORT(
		rst : IN std_logic;
		clk : IN std_logic;
		ce : IN std_logic;          
		rdy : OUT std_logic
		);
	END COMPONENT;

	COMPONENT timeout_counter
	GENERIC (N : integer);
	PORT(
		rst : IN std_logic;
		clk : IN std_logic;
		ce : IN std_logic;
		load : IN integer range 0 to N-1;
		load_en : IN std_logic;          
		rdy : OUT std_logic
		);
	END COMPONENT;

	COMPONENT sipo
	GENERIC (N : integer);
	PORT(
		rst : IN std_logic;
		clk : IN std_logic;
		ce : IN std_logic;
		d_in : IN std_logic;          
		d_out : OUT std_logic_vector(7 downto 0)
		);
	END COMPONENT;

	COMPONENT oneshot
	GENERIC (mode : integer);
	PORT(
		rst : IN std_logic;
		clk : IN std_logic;
		input : IN std_logic;          
		output : OUT std_logic
		);
	END COMPONENT;

-- Types
type read_state_t is (read_start_st, read_data_st, read_parity_st, read_stop_st);

-- Constant
constant prescaler_us   : integer := integer(f_sys_clk);
constant timeout_us     : integer := 1000;

-- Signals
signal en_us            : std_logic;
signal timeout_load_en  : std_logic;
signal timeout_flag     : std_logic;
signal read_cs          : read_state_t;
signal read_ns          : read_state_t;
signal data_cnt_en      : std_logic;
signal data_cnt         : integer range 0 to 7;
signal sipo_en          : std_logic;
signal data_rdy         : std_logic;
signal out_reg_ce       : std_logic;
signal sipo_reg         : std_logic_vector(7 downto 0);

begin

	Inst_prescaler: prescaler
	GENERIC MAP (
		divide => prescaler_us)		
	PORT MAP(
		rst => rst,
		clk => clk,
		ce => ce,
		rdy => en_us
	);

	Inst_timeout_counter: timeout_counter 
	GENERIC MAP (
		N => timeout_us)		
	PORT MAP(
		rst => rst,
		clk => clk,
		ce => en_us,
		load => timeout_us - 1,
		load_en => timeout_load_en,
		rdy => timeout_flag
	);

	Inst_sipo: sipo
	GENERIC MAP (
		N => 8)		
	PORT MAP(
		rst => rst,
		clk => ps2_clk,
		ce => sipo_en,
		d_in => ps2_data,
		d_out => sipo_reg
	);

	Inst_oneshot: oneshot 
	GENERIC MAP (
		mode => 1)		
	PORT MAP(
		rst => rst,
		clk => clk,
		input => data_rdy,
		output => out_reg_ce
	);

	-------------------------------------------
	out_reg: process(rst, clk, out_reg_ce, read_en)
	begin
		if rst = '1' then
			d_out <= (others => '0');
			rdy_out <= '0';
		elsif rising_edge(clk) then
			if out_reg_ce = '1' then
				d_out <= sipo_reg;
				rdy_out <= '1';
			elsif	read_en = '1' then
				rdy_out <= '0';
			end if;
		end if;
	end process;

	read_state_clk: process(rst, ps2_clk)
	begin
		if rst = '1' then
			read_cs <= read_start_st;
		else
			if falling_edge(ps2_clk) then
				read_cs <= read_ns;
				if (data_cnt_en = '1') then
					if (data_cnt /= 0) then
						data_cnt <= data_cnt - 1;
					end if;
				else
					data_cnt <= 7;
				end if;
			end if;
		end if;
	end process;

	read_state_output: process(read_cs)
	begin
	
		timeout_load_en <= '0';
		data_cnt_en     <= '0';
		rdy_in          <= '0';
		sipo_en         <= '0';
		data_rdy        <= '0';
--		rdy_out <= '0';
      case (read_cs) is
         when read_start_st =>
				data_rdy        <= '1';
				timeout_load_en <= '1';
--				rdy_out <= '1';
         when read_data_st =>
				sipo_en         <= '1';
				data_cnt_en     <= '1';
         when others => null; 
      end case;      
	end process;

   read_state_decode: process (read_cs, timeout_flag, data_cnt, ps2_data)
   begin
      --declare default state for next_state to avoid latches
      read_ns <= read_cs;  --default is to stay in current state
      --insert statements to decode next_state
      --below is a simple example
      case (read_cs) is
         when read_start_st =>
            if ps2_data = '0' then
               read_ns <= read_data_st;
            end if;
         when read_data_st =>
            if timeout_flag = '1' then
            	read_ns <= read_start_st;
				elsif data_cnt = 0 then
	            read_ns <= read_parity_st;
				end if;
         when read_parity_st =>
            read_ns <= read_stop_st;
            if timeout_flag = '1' then
            	read_ns <= read_start_st;
            end if;
         when read_stop_st =>
           	read_ns <= read_start_st;
         when others =>
            read_ns <= read_start_st;
      end case;      
   end process;

end Behavioral;
