| 
    
       ---------------------------------------------------------------------------
 
     | 
  
  
     | 
    
       -- (c) 2013 mark watson
 
     | 
  
  
     | 
    
       -- I am happy for anyone to use this for non-commercial use.
 
     | 
  
  
     | 
    
       -- If my vhdl files are used commercially or otherwise sold,
 
     | 
  
  
     | 
    
       -- please contact me for explicit permission at scrameta (gmail).
 
     | 
  
  
     | 
    
       -- This applies for source and binary form and derived works.
 
     | 
  
  
     | 
    
       ---------------------------------------------------------------------------
 
     | 
  
  
     | 
    
       LIBRARY ieee;
 
     | 
  
  
     | 
    
       USE ieee.std_logic_1164.all;
 
     | 
  
  
     | 
    
       use ieee.numeric_std.all;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       --	KEY_OUT : OUT STD_LOGIC_vector(7 downto 0); -- Pokey scan code
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       --	KEY_PRESSED : OUT STD_LOGIC; -- high for 1 cycle on new key pressed
 
     | 
  
  
     | 
    
       --	SHIFT_PRESSED : OUT STD_LOGIC; -- high while shift held
 
     | 
  
  
     | 
    
       --	CONTROL_PRESSED : OUT STD_LOGIC; -- high while control held
 
     | 
  
  
     | 
    
       --	BREAK_PRESSED : OUT STD_LOGIC -- high for 1 cycle on break key pressed (pause - no need for modifiers)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ENTITY ps2_keyboard IS
 
     | 
  
  
     | 
    
       PORT 
 
     | 
  
  
     | 
    
       ( 
 
     | 
  
  
     | 
    
       	CLK : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	RESET_N : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	PS2_CLK : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	PS2_DAT : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	KEY_EVENT : OUT STD_LOGIC; -- high for 1 cycle on new key pressed(or repeated)/released
 
     | 
  
  
     | 
    
       	KEY_VALUE : OUT STD_LOGIC_VECTOR(7 downto 0); -- valid on event, raw scan code
 
     | 
  
  
     | 
    
       	KEY_EXTENDED : OUT STD_LOGIC;           -- valid on event, if scan code extended
 
     | 
  
  
     | 
    
       	KEY_UP : OUT STD_LOGIC                 -- value on event, if key released
 
     | 
  
  
     | 
    
       );
 
     | 
  
  
     | 
    
       END ps2_keyboard;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ARCHITECTURE vhdl OF ps2_keyboard IS
 
     | 
  
  
     | 
    
       	component enable_divider IS
 
     | 
  
  
     | 
    
       	generic(COUNT : natural := 1);
 
     | 
  
  
     | 
    
       	PORT 
 
     | 
  
  
     | 
    
       	( 
 
     | 
  
  
     | 
    
       		CLK : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       		RESET_N : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		ENABLE_IN : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		ENABLE_OUT : OUT STD_LOGIC
 
     | 
  
  
     | 
    
       	);
 
     | 
  
  
     | 
    
       	END component;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	function To_Std_Logic(L: BOOLEAN) return std_ulogic is
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		if L then
 
     | 
  
  
     | 
    
       			return('1');
 
     | 
  
  
     | 
    
       		else
 
     | 
  
  
     | 
    
       			return('0');
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       	end function To_Std_Logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- PS2 keyboard sends on its own clock high->low transition
 
     | 
  
  
     | 
    
       	-- start, 8 data bits, parity, stop
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- Codes are either 1 bytes or 2 bytes (extended) on press
 
     | 
  
  
     | 
    
       	-- XX
 
     | 
  
  
     | 
    
       	-- EX YY
 
     | 
  
  
     | 
    
       	-- Codes are eighter 2 bytes or 3 bytes (extended) on release
 
     | 
  
  
     | 
    
       	-- F0 XX
 
     | 
  
  
     | 
    
       	-- EX F0 YY
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- Some keys have multiple codes. e.g. break sends E1,14 and 77. It also sends release immediately E1 F0 14,F0 77
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- LSB first
 
     | 
  
  
     | 
    
       	-- Start bit 0
 
     | 
  
  
     | 
    
       	-- Stop bit 1
 
     | 
  
  
     | 
    
       	-- Parity = not(data(0) xor data(1) xor data(2) xor data(3) xor data(4) xor data(5) xor data(6) xor data(7))
 
     | 
  
  
     | 
    
       	-- e.g. 
 
     | 
  
  
     | 
    
       	-- '0 1100 0010 0 1'
 
     | 
  
  
     | 
    
       	-- not(1 xor 1 xor 0 xor 0 xor 0 xor 0 xor 1 xor 0) = not(1) = 0
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	-- Receive raw data from ps2 serial interface
 
     | 
  
  
     | 
    
       	signal ps2_shiftreg_next : std_logic_vector(10 downto 0);
 
     | 
  
  
     | 
    
       	signal ps2_shiftreg_reg : std_logic_vector(10 downto 0);
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal idle_next : std_logic_vector(3 downto 0);
 
     | 
  
  
     | 
    
       	signal idle_reg : std_logic_vector(3 downto 0);	
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal bitcount_next : std_logic_vector(3 downto 0);
 
     | 
  
  
     | 
    
       	signal bitcount_reg : std_logic_vector(3 downto 0);	
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal enable_ps2 : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal last_ps2_clk_next : std_logic;
 
     | 
  
  
     | 
    
       	signal last_ps2_clk_reg : std_logic;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal ps2_clk_reg : std_logic;
 
     | 
  
  
     | 
    
       	signal ps2_dat_reg : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal parity : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- Once we have whole parity checked bytes
 
     | 
  
  
     | 
    
       	signal byte_next : std_logic_vector(7 downto 0);
 
     | 
  
  
     | 
    
       	signal byte_reg : std_logic_vector(7 downto 0);
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal byte_received_next : std_logic;
 
     | 
  
  
     | 
    
       	signal byte_received_reg : std_logic;	
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- Decode if they are press(or repeat)/release or extended
 
     | 
  
  
     | 
    
       	signal pending_extended_next : std_logic;
 
     | 
  
  
     | 
    
       	signal pending_extended_reg : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal pending_keyup_next : std_logic;
 
     | 
  
  
     | 
    
       	signal pending_keyup_reg : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- To eventually get the code itself
 
     | 
  
  
     | 
    
       	signal key_event_next : std_logic;
 
     | 
  
  
     | 
    
       	signal key_event_reg : std_logic;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	signal key_value_next : std_logic_vector(9 downto 0);
 
     | 
  
  
     | 
    
       	signal key_value_reg : std_logic_vector(9 downto 0);
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- Store the last value, so I can filter repeat. I want repeat handled by Atari OS, not PS2 keyboard
 
     | 
  
  
     | 
    
       	signal key_value_last_next : std_logic_vector(9 downto 0);
 
     | 
  
  
     | 
    
       	signal key_value_last_reg : std_logic_vector(9 downto 0);
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       BEGIN
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       sync_clk: ENTITY work.synchronizer 
 
     | 
  
  
     | 
    
       	PORT MAP
 
     | 
  
  
     | 
    
       	( 
 
     | 
  
  
     | 
    
       		CLK => CLK,
 
     | 
  
  
     | 
    
       		RAW => PS2_CLK,
 
     | 
  
  
     | 
    
       		SYNC => PS2_CLK_REG
 
     | 
  
  
     | 
    
       	);
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       sync_dat: ENTITY work.synchronizer 
 
     | 
  
  
     | 
    
       	PORT MAP
 
     | 
  
  
     | 
    
       	( 
 
     | 
  
  
     | 
    
       		CLK => CLK,
 
     | 
  
  
     | 
    
       		RAW => PS2_DAT,
 
     | 
  
  
     | 
    
       		SYNC => PS2_DAT_REG
 
     | 
  
  
     | 
    
       	);
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	-- register
 
     | 
  
  
     | 
    
       	process(clk,reset_n)
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		if (reset_n = '0') then
 
     | 
  
  
     | 
    
       			-- Convert to bytes/verify
 
     | 
  
  
     | 
    
       			last_ps2_clk_reg <= '0';
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			ps2_shiftreg_reg<= (others=>'0');		
 
     | 
  
  
     | 
    
       			idle_reg <= (others=>'0');
 
     | 
  
  
     | 
    
       			bitcount_reg <= (others=>'0');
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			byte_received_reg <= '0';
 
     | 
  
  
     | 
    
       			byte_reg <= (others=>'0');
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			-- Handle simple byte strings (extended,byte extended,release,byte byte release,byte)
 
     | 
  
  
     | 
    
       			pending_extended_reg <= '0';
 
     | 
  
  
     | 
    
       			pending_keyup_reg <= '0';			
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       			-- Output registers
 
     | 
  
  
     | 
    
       			key_event_reg <= '0';
 
     | 
  
  
     | 
    
       			key_value_reg <= (others=>'0');
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			key_value_last_reg <= (others=>'0');
 
     | 
  
  
     | 
    
       		elsif (clk'event and clk='1') then
 
     | 
  
  
     | 
    
       			-- Convert to bytes/verify
 
     | 
  
  
     | 
    
       			last_ps2_clk_reg <= last_ps2_clk_next;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			ps2_shiftreg_reg<= ps2_shiftreg_next;		
 
     | 
  
  
     | 
    
       			idle_reg <= idle_next;
 
     | 
  
  
     | 
    
       			bitcount_reg <= bitcount_next;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			byte_received_reg <= byte_received_next;
 
     | 
  
  
     | 
    
       			byte_reg <= byte_next;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			-- Handle simple byte strings (extended,byte extended,release,byte byte release,byte)
 
     | 
  
  
     | 
    
       			pending_extended_reg <= pending_extended_next;
 
     | 
  
  
     | 
    
       			pending_keyup_reg <= pending_keyup_next;			
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       			-- Output registers
 
     | 
  
  
     | 
    
       			key_event_reg <= key_event_next;
 
     | 
  
  
     | 
    
       			key_value_reg <= key_value_next;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			key_value_last_reg <= key_value_last_next;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	-- Divide clock by 256 to get approx 4*ps2 clock
 
     | 
  
  
     | 
    
       	enable_div : enable_divider
 
     | 
  
  
     | 
    
       		generic map (COUNT=>256)
 
     | 
  
  
     | 
    
       		port map(clk=>clk,reset_n=>reset_n,enable_in=>'1',enable_out=>enable_ps2);
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- capture bytes from ps2
 
     | 
  
  
     | 
    
       	parity<= not(ps2_shiftreg_reg(8) xor ps2_shiftreg_reg(7) xor ps2_shiftreg_reg(6) xor ps2_shiftreg_reg(5) xor  ps2_shiftreg_reg(4) xor ps2_shiftreg_reg(3) xor ps2_shiftreg_reg(2) xor ps2_shiftreg_reg(1));
 
     | 
  
  
     | 
    
       	process(last_ps2_clk_reg,ps2_clk_reg, ps2_dat_reg, ps2_shiftreg_reg,idle_reg,enable_ps2,bitcount_reg,parity)
 
     | 
  
  
     | 
    
       	begin	
 
     | 
  
  
     | 
    
       		ps2_shiftreg_next <= ps2_shiftreg_reg;
 
     | 
  
  
     | 
    
       		last_ps2_clk_next <= last_ps2_clk_reg;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		bitcount_next <= bitcount_reg;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		idle_next <= idle_reg;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		byte_received_next <= '0';
 
     | 
  
  
     | 
    
       		byte_next <= (others=>'0');
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		if (enable_ps2 = '1') then		
 
     | 
  
  
     | 
    
       			last_ps2_clk_next <= ps2_clk_reg;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			-- sample on falling edge
 
     | 
  
  
     | 
    
       			if (ps2_clk_reg = '0' and last_ps2_clk_reg = '1') then
 
     | 
  
  
     | 
    
       				ps2_shiftreg_next <= ps2_dat_reg&ps2_shiftreg_reg(10 downto 1);
 
     | 
  
  
     | 
    
       				bitcount_next <= std_logic_vector(unsigned(bitcount_reg)+1);
 
     | 
  
  
     | 
    
       			end if;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			-- output to next stage when done
 
     | 
  
  
     | 
    
       			if (bitcount_reg = X"B") then
 
     | 
  
  
     | 
    
       				byte_received_next <= (parity xnor ps2_shiftreg_reg(9)) and not(ps2_shiftreg_reg(0)) and ps2_shiftreg_reg(10);
 
     | 
  
  
     | 
    
       				byte_next <= ps2_shiftreg_reg(8 downto 1);
 
     | 
  
  
     | 
    
       				bitcount_next <= (others=>'0');
 
     | 
  
  
     | 
    
       			end if;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       			-- reset if both high for a time period
 
     | 
  
  
     | 
    
       			idle_next <= std_logic_vector(unsigned(idle_reg) +1);
 
     | 
  
  
     | 
    
       			if (idle_reg = X"F") then
 
     | 
  
  
     | 
    
       				ps2_shiftreg_next <= (others=>'0');
 
     | 
  
  
     | 
    
       				bitcount_next <= (others=>'0');
 
     | 
  
  
     | 
    
       			end if;
 
     | 
  
  
     | 
    
       			if (ps2_clk_reg = '0' or ps2_dat_reg = '0') then
 
     | 
  
  
     | 
    
       				idle_next <= X"0";
 
     | 
  
  
     | 
    
       			end if;		
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       	
 
     | 
  
  
     | 
    
       	-- process bytes
 
     | 
  
  
     | 
    
       	process(byte_reg,byte_received_reg, pending_extended_reg, pending_keyup_reg, key_value_last_reg)
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		pending_extended_next <=  pending_extended_reg;
 
     | 
  
  
     | 
    
       		pending_keyup_next <=  pending_keyup_reg;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		key_event_next <= '0';
 
     | 
  
  
     | 
    
       		key_value_next <= (others =>'0');
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		key_value_last_next <= key_value_last_reg;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       		if (byte_received_reg = '1') then		
 
     | 
  
  
     | 
    
       			case byte_reg is 
 
     | 
  
  
     | 
    
       				when X"E0" =>
 
     | 
  
  
     | 
    
       					pending_extended_next <= '1';					
 
     | 
  
  
     | 
    
       				when X"E1" =>
 
     | 
  
  
     | 
    
       					pending_extended_next <= '1';
 
     | 
  
  
     | 
    
       				when X"F0" =>
 
     | 
  
  
     | 
    
       					pending_keyup_next <= '1';
 
     | 
  
  
     | 
    
       				when others => 
 
     | 
  
  
     | 
    
       					pending_extended_next <= '0';
 
     | 
  
  
     | 
    
       					pending_keyup_next <= '0';	
 
     | 
  
  
     | 
    
       					
 
     | 
  
  
     | 
    
       					if (not(key_value_last_reg = pending_keyup_reg&pending_extended_reg&byte_reg(7 downto 0))) then
 
     | 
  
  
     | 
    
       						key_event_next <= '1';
 
     | 
  
  
     | 
    
       						key_value_next <= pending_keyup_reg&pending_extended_reg&byte_reg(7 downto 0);
 
     | 
  
  
     | 
    
       						
 
     | 
  
  
     | 
    
       						key_value_last_next <= pending_keyup_reg&pending_extended_reg&byte_reg(7 downto 0);
 
     | 
  
  
     | 
    
       					end if;
 
     | 
  
  
     | 
    
       			end case;
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       			
 
     | 
  
  
     | 
    
       	-- Output
 
     | 
  
  
     | 
    
       	key_event <= key_event_reg;
 
     | 
  
  
     | 
    
       	key_value <= key_value_reg(7 downto 0);
 
     | 
  
  
     | 
    
       	key_extended <= key_value_reg(8);
 
     | 
  
  
     | 
    
       	key_up <= key_value_reg(9);
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       END vhdl;
 
     |