Project

General

Profile

---------------------------------------------------------------------------
-- (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;
(69-69/87)