320 lines
17 KiB
VHDL
320 lines
17 KiB
VHDL
--------------------------------------------------------------------------------
|
|
--
|
|
-- FileName: ps2_keyboard_to_ascii.vhd
|
|
-- Dependencies: ps2_keyboard.vhd, debounce.vhd
|
|
-- Design Software: Quartus II 32-bit Version 12.1 Build 177 SJ Full Version
|
|
--
|
|
-- HDL CODE IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY
|
|
-- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
-- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
|
|
-- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
-- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
|
|
-- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
|
|
-- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
|
|
-- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
|
|
--
|
|
-- Version History
|
|
-- Version 1.0 11/29/2013 Scott Larson
|
|
-- Initial Public Release
|
|
--
|
|
--------------------------------------------------------------------------------
|
|
|
|
LIBRARY ieee;
|
|
USE ieee.std_logic_1164.all;
|
|
|
|
ENTITY ps2_keyboard_to_ascii IS
|
|
GENERIC(
|
|
clk_freq : INTEGER := 50_000_000; --system clock frequency in Hz
|
|
ps2_debounce_counter_size : INTEGER := 8); --set such that 2^size/clk_freq = 5us (size = 8 for 50MHz)
|
|
PORT(
|
|
clk : IN STD_LOGIC; --system clock input
|
|
ps2_clk : IN STD_LOGIC; --clock signal from PS2 keyboard
|
|
ps2_data : IN STD_LOGIC; --data signal from PS2 keyboard
|
|
ascii_new : OUT STD_LOGIC; --output flag indicating new ASCII value
|
|
ascii_code : OUT STD_LOGIC_VECTOR(6 DOWNTO 0)); --ASCII value
|
|
END ps2_keyboard_to_ascii;
|
|
|
|
ARCHITECTURE behavior OF ps2_keyboard_to_ascii IS
|
|
TYPE machine IS(ready, new_code, translate, output); --needed states
|
|
SIGNAL state : machine; --state machine
|
|
SIGNAL ps2_code_new : STD_LOGIC; --new PS2 code flag from ps2_keyboard component
|
|
SIGNAL ps2_code : STD_LOGIC_VECTOR(7 DOWNTO 0); --PS2 code input form ps2_keyboard component
|
|
SIGNAL prev_ps2_code_new : STD_LOGIC := '1'; --value of ps2_code_new flag on previous clock
|
|
SIGNAL break : STD_LOGIC := '0'; --'1' for break code, '0' for make code
|
|
SIGNAL e0_code : STD_LOGIC := '0'; --'1' for multi-code commands, '0' for single code commands
|
|
SIGNAL caps_lock : STD_LOGIC := '0'; --'1' if caps lock is active, '0' if caps lock is inactive
|
|
SIGNAL control_r : STD_LOGIC := '0'; --'1' if right control key is held down, else '0'
|
|
SIGNAL control_l : STD_LOGIC := '0'; --'1' if left control key is held down, else '0'
|
|
SIGNAL shift_r : STD_LOGIC := '0'; --'1' if right shift is held down, else '0'
|
|
SIGNAL shift_l : STD_LOGIC := '0'; --'1' if left shift is held down, else '0'
|
|
SIGNAL ascii : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"FF"; --internal value of ASCII translation
|
|
|
|
--declare PS2 keyboard interface component
|
|
COMPONENT ps2_keyboard IS
|
|
GENERIC(
|
|
clk_freq : INTEGER; --system clock frequency in Hz
|
|
debounce_counter_size : INTEGER); --set such that 2^size/clk_freq = 5us (size = 8 for 50MHz)
|
|
PORT(
|
|
clk : IN STD_LOGIC; --system clock
|
|
ps2_clk : IN STD_LOGIC; --clock signal from PS2 keyboard
|
|
ps2_data : IN STD_LOGIC; --data signal from PS2 keyboard
|
|
ps2_code_new : OUT STD_LOGIC; --flag that new PS/2 code is available on ps2_code bus
|
|
ps2_code : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); --code received from PS/2
|
|
END COMPONENT;
|
|
|
|
BEGIN
|
|
|
|
--instantiate PS2 keyboard interface logic
|
|
ps2_keyboard_0: ps2_keyboard
|
|
GENERIC MAP(clk_freq => clk_freq, debounce_counter_size => ps2_debounce_counter_size)
|
|
PORT MAP(clk => clk, ps2_clk => ps2_clk, ps2_data => ps2_data, ps2_code_new => ps2_code_new, ps2_code => ps2_code);
|
|
|
|
PROCESS(clk)
|
|
BEGIN
|
|
IF(clk'EVENT AND clk = '1') THEN
|
|
prev_ps2_code_new <= ps2_code_new; --keep track of previous ps2_code_new values to determine low-to-high transitions
|
|
CASE state IS
|
|
|
|
--ready state: wait for a new PS2 code to be received
|
|
WHEN ready =>
|
|
IF(prev_ps2_code_new = '0' AND ps2_code_new = '1') THEN --new PS2 code received
|
|
ascii_new <= '0'; --reset new ASCII code indicator
|
|
state <= new_code; --proceed to new_code state
|
|
ELSE --no new PS2 code received yet
|
|
state <= ready; --remain in ready state
|
|
END IF;
|
|
|
|
--new_code state: determine what to do with the new PS2 code
|
|
WHEN new_code =>
|
|
IF(ps2_code = x"F0") THEN --code indicates that next command is break
|
|
break <= '1'; --set break flag
|
|
state <= ready; --return to ready state to await next PS2 code
|
|
ELSIF(ps2_code = x"E0") THEN --code indicates multi-key command
|
|
e0_code <= '1'; --set multi-code command flag
|
|
state <= ready; --return to ready state to await next PS2 code
|
|
ELSE --code is the last PS2 code in the make/break code
|
|
ascii(7) <= '1'; --set internal ascii value to unsupported code (for verification)
|
|
state <= translate; --proceed to translate state
|
|
END IF;
|
|
|
|
--translate state: translate PS2 code to ASCII value
|
|
WHEN translate =>
|
|
break <= '0'; --reset break flag
|
|
e0_code <= '0'; --reset multi-code command flag
|
|
|
|
--handle codes for control, shift, and caps lock
|
|
CASE ps2_code IS
|
|
WHEN x"58" => --caps lock code
|
|
IF(break = '0') THEN --if make command
|
|
caps_lock <= NOT caps_lock; --toggle caps lock
|
|
END IF;
|
|
WHEN x"14" => --code for the control keys
|
|
IF(e0_code = '1') THEN --code for right control
|
|
control_r <= NOT break; --update right control flag
|
|
ELSE --code for left control
|
|
control_l <= NOT break; --update left control flag
|
|
END IF;
|
|
WHEN x"12" => --left shift code
|
|
shift_l <= NOT break; --update left shift flag
|
|
WHEN x"59" => --right shift code
|
|
shift_r <= NOT break; --update right shift flag
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
|
|
--translate control codes (these do not depend on shift or caps lock)
|
|
IF(control_l = '1' OR control_r = '1') THEN
|
|
CASE ps2_code IS
|
|
WHEN x"1E" => ascii <= x"00"; --^@ NUL
|
|
WHEN x"1C" => ascii <= x"01"; --^A SOH
|
|
WHEN x"32" => ascii <= x"02"; --^B STX
|
|
WHEN x"21" => ascii <= x"03"; --^C ETX
|
|
WHEN x"23" => ascii <= x"04"; --^D EOT
|
|
WHEN x"24" => ascii <= x"05"; --^E ENQ
|
|
WHEN x"2B" => ascii <= x"06"; --^F ACK
|
|
WHEN x"34" => ascii <= x"07"; --^G BEL
|
|
WHEN x"33" => ascii <= x"08"; --^H BS
|
|
WHEN x"43" => ascii <= x"09"; --^I HT
|
|
WHEN x"3B" => ascii <= x"0A"; --^J LF
|
|
WHEN x"42" => ascii <= x"0B"; --^K VT
|
|
WHEN x"4B" => ascii <= x"0C"; --^L FF
|
|
WHEN x"3A" => ascii <= x"0D"; --^M CR
|
|
WHEN x"31" => ascii <= x"0E"; --^N SO
|
|
WHEN x"44" => ascii <= x"0F"; --^O SI
|
|
WHEN x"4D" => ascii <= x"10"; --^P DLE
|
|
WHEN x"15" => ascii <= x"11"; --^Q DC1
|
|
WHEN x"2D" => ascii <= x"12"; --^R DC2
|
|
WHEN x"1B" => ascii <= x"13"; --^S DC3
|
|
WHEN x"2C" => ascii <= x"14"; --^T DC4
|
|
WHEN x"3C" => ascii <= x"15"; --^U NAK
|
|
WHEN x"2A" => ascii <= x"16"; --^V SYN
|
|
WHEN x"1D" => ascii <= x"17"; --^W ETB
|
|
WHEN x"22" => ascii <= x"18"; --^X CAN
|
|
WHEN x"35" => ascii <= x"19"; --^Y EM
|
|
WHEN x"1A" => ascii <= x"1A"; --^Z SUB
|
|
WHEN x"54" => ascii <= x"1B"; --^[ ESC
|
|
WHEN x"5D" => ascii <= x"1C"; --^\ FS
|
|
WHEN x"5B" => ascii <= x"1D"; --^] GS
|
|
WHEN x"36" => ascii <= x"1E"; --^^ RS
|
|
WHEN x"4E" => ascii <= x"1F"; --^_ US
|
|
WHEN x"4A" => ascii <= x"7F"; --^? DEL
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
ELSE --if control keys are not pressed
|
|
|
|
--translate characters that do not depend on shift, or caps lock
|
|
CASE ps2_code IS
|
|
WHEN x"29" => ascii <= x"20"; --space
|
|
WHEN x"66" => ascii <= x"08"; --backspace (BS control code)
|
|
WHEN x"0D" => ascii <= x"09"; --tab (HT control code)
|
|
WHEN x"5A" => ascii <= x"0D"; --enter (CR control code)
|
|
WHEN x"76" => ascii <= x"1B"; --escape (ESC control code)
|
|
WHEN x"71" =>
|
|
IF(e0_code = '1') THEN --ps2 code for delete is a multi-key code
|
|
ascii <= x"7F"; --delete
|
|
END IF;
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
|
|
--translate letters (these depend on both shift and caps lock)
|
|
IF((shift_r = '0' AND shift_l = '0' AND caps_lock = '0') OR
|
|
((shift_r = '1' OR shift_l = '1') AND caps_lock = '1')) THEN --letter is lowercase
|
|
CASE ps2_code IS
|
|
WHEN x"1C" => ascii <= x"61"; --a
|
|
WHEN x"32" => ascii <= x"62"; --b
|
|
WHEN x"21" => ascii <= x"63"; --c
|
|
WHEN x"23" => ascii <= x"64"; --d
|
|
WHEN x"24" => ascii <= x"65"; --e
|
|
WHEN x"2B" => ascii <= x"66"; --f
|
|
WHEN x"34" => ascii <= x"67"; --g
|
|
WHEN x"33" => ascii <= x"68"; --h
|
|
WHEN x"43" => ascii <= x"69"; --i
|
|
WHEN x"3B" => ascii <= x"6A"; --j
|
|
WHEN x"42" => ascii <= x"6B"; --k
|
|
WHEN x"4B" => ascii <= x"6C"; --l
|
|
WHEN x"3A" => ascii <= x"6D"; --m
|
|
WHEN x"31" => ascii <= x"6E"; --n
|
|
WHEN x"44" => ascii <= x"6F"; --o
|
|
WHEN x"4D" => ascii <= x"70"; --p
|
|
WHEN x"15" => ascii <= x"71"; --q
|
|
WHEN x"2D" => ascii <= x"72"; --r
|
|
WHEN x"1B" => ascii <= x"73"; --s
|
|
WHEN x"2C" => ascii <= x"74"; --t
|
|
WHEN x"3C" => ascii <= x"75"; --u
|
|
WHEN x"2A" => ascii <= x"76"; --v
|
|
WHEN x"1D" => ascii <= x"77"; --w
|
|
WHEN x"22" => ascii <= x"78"; --x
|
|
WHEN x"35" => ascii <= x"79"; --y
|
|
WHEN x"1A" => ascii <= x"7A"; --z
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
ELSE --letter is uppercase
|
|
CASE ps2_code IS
|
|
WHEN x"1C" => ascii <= x"41"; --A
|
|
WHEN x"32" => ascii <= x"42"; --B
|
|
WHEN x"21" => ascii <= x"43"; --C
|
|
WHEN x"23" => ascii <= x"44"; --D
|
|
WHEN x"24" => ascii <= x"45"; --E
|
|
WHEN x"2B" => ascii <= x"46"; --F
|
|
WHEN x"34" => ascii <= x"47"; --G
|
|
WHEN x"33" => ascii <= x"48"; --H
|
|
WHEN x"43" => ascii <= x"49"; --I
|
|
WHEN x"3B" => ascii <= x"4A"; --J
|
|
WHEN x"42" => ascii <= x"4B"; --K
|
|
WHEN x"4B" => ascii <= x"4C"; --L
|
|
WHEN x"3A" => ascii <= x"4D"; --M
|
|
WHEN x"31" => ascii <= x"4E"; --N
|
|
WHEN x"44" => ascii <= x"4F"; --O
|
|
WHEN x"4D" => ascii <= x"50"; --P
|
|
WHEN x"15" => ascii <= x"51"; --Q
|
|
WHEN x"2D" => ascii <= x"52"; --R
|
|
WHEN x"1B" => ascii <= x"53"; --S
|
|
WHEN x"2C" => ascii <= x"54"; --T
|
|
WHEN x"3C" => ascii <= x"55"; --U
|
|
WHEN x"2A" => ascii <= x"56"; --V
|
|
WHEN x"1D" => ascii <= x"57"; --W
|
|
WHEN x"22" => ascii <= x"58"; --X
|
|
WHEN x"35" => ascii <= x"59"; --Y
|
|
WHEN x"1A" => ascii <= x"5A"; --Z
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
END IF;
|
|
|
|
--translate numbers and symbols (these depend on shift but not caps lock)
|
|
IF(shift_l = '1' OR shift_r = '1') THEN --key's secondary character is desired
|
|
CASE ps2_code IS
|
|
WHEN x"16" => ascii <= x"21"; --!
|
|
WHEN x"52" => ascii <= x"22"; --"
|
|
WHEN x"26" => ascii <= x"23"; --#
|
|
WHEN x"25" => ascii <= x"24"; --$
|
|
WHEN x"2E" => ascii <= x"25"; --%
|
|
WHEN x"3D" => ascii <= x"26"; --&
|
|
WHEN x"46" => ascii <= x"28"; --(
|
|
WHEN x"45" => ascii <= x"29"; --)
|
|
WHEN x"3E" => ascii <= x"2A"; --*
|
|
WHEN x"55" => ascii <= x"2B"; --+
|
|
WHEN x"4C" => ascii <= x"3A"; --:
|
|
WHEN x"41" => ascii <= x"3C"; --<
|
|
WHEN x"49" => ascii <= x"3E"; -->
|
|
WHEN x"4A" => ascii <= x"3F"; --?
|
|
WHEN x"1E" => ascii <= x"40"; --@
|
|
WHEN x"36" => ascii <= x"5E"; --^
|
|
WHEN x"4E" => ascii <= x"5F"; --_
|
|
WHEN x"54" => ascii <= x"7B"; --{
|
|
WHEN x"5D" => ascii <= x"7C"; --|
|
|
WHEN x"5B" => ascii <= x"7D"; --}
|
|
WHEN x"0E" => ascii <= x"7E"; --~
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
ELSE --key's primary character is desired
|
|
CASE ps2_code IS
|
|
WHEN x"45" => ascii <= x"30"; --0
|
|
WHEN x"16" => ascii <= x"31"; --1
|
|
WHEN x"1E" => ascii <= x"32"; --2
|
|
WHEN x"26" => ascii <= x"33"; --3
|
|
WHEN x"25" => ascii <= x"34"; --4
|
|
WHEN x"2E" => ascii <= x"35"; --5
|
|
WHEN x"36" => ascii <= x"36"; --6
|
|
WHEN x"3D" => ascii <= x"37"; --7
|
|
WHEN x"3E" => ascii <= x"38"; --8
|
|
WHEN x"46" => ascii <= x"39"; --9
|
|
WHEN x"52" => ascii <= x"27"; --'
|
|
WHEN x"41" => ascii <= x"2C"; --,
|
|
WHEN x"4E" => ascii <= x"2D"; ---
|
|
WHEN x"49" => ascii <= x"2E"; --.
|
|
WHEN x"4A" => ascii <= x"2F"; --/
|
|
WHEN x"4C" => ascii <= x"3B"; --;
|
|
WHEN x"55" => ascii <= x"3D"; --=
|
|
WHEN x"54" => ascii <= x"5B"; --[
|
|
WHEN x"5D" => ascii <= x"5C"; --\
|
|
WHEN x"5B" => ascii <= x"5D"; --]
|
|
WHEN x"0E" => ascii <= x"60"; --`
|
|
WHEN OTHERS => NULL;
|
|
END CASE;
|
|
END IF;
|
|
|
|
END IF;
|
|
|
|
IF(break = '0') THEN --the code is a make
|
|
state <= output; --proceed to output state
|
|
ELSE --code is a break
|
|
state <= ready; --return to ready state to await next PS2 code
|
|
END IF;
|
|
|
|
--output state: verify the code is valid and output the ASCII value
|
|
WHEN output =>
|
|
IF(ascii(7) = '0') THEN --the PS2 code has an ASCII output
|
|
ascii_new <= '1'; --set flag indicating new ASCII output
|
|
ascii_code <= ascii(6 DOWNTO 0); --output the ASCII value
|
|
END IF;
|
|
state <= ready; --return to ready state to await next PS2 code
|
|
|
|
END CASE;
|
|
END IF;
|
|
END PROCESS;
|
|
|
|
END behavior;
|
|
|
|
|
|
|