school/ecen320/tx_encoder/ps2_keyboard_to_ascii.vhd

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;