|
---------------------------------------------------------------------------
|
|
-- (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;
|