1 |
11 |
samiam9512 |
--
|
2 |
|
|
-- This circuit accepts a serial datastream and clock from a PS/2 keyboard
|
3 |
|
|
-- and outputs the scancode for any key that is pressed.
|
4 |
|
|
--
|
5 |
|
|
-- Notes:
|
6 |
|
|
--
|
7 |
|
|
-- 1. The clock from the PS/2 keyboard does not drive the clock inputs of
|
8 |
|
|
-- any of the registers in this circuit. Instead, it is sampled at the
|
9 |
|
|
-- frequency of the main clock input and edges are extracted from the samples.
|
10 |
|
|
-- So you have to apply a main clock that is substantially faster than
|
11 |
|
|
-- the 10 KHz PS/2 clock. It should be 200 KHz or more.
|
12 |
|
|
--
|
13 |
|
|
-- 2. The scancode is only valid when the ready signal is high. The scancode
|
14 |
|
|
-- should be registered by an external circuit on the first clock edge
|
15 |
|
|
-- after the ready signal goes high.
|
16 |
|
|
--
|
17 |
|
|
-- 3. The ready signal pulses only after the key is released.
|
18 |
|
|
--
|
19 |
|
|
-- 4. The error flag is set whenever the PS/2 clock stops pulsing and the
|
20 |
|
|
-- PS/2 clock is either at a low level or less than 11 bits of serial
|
21 |
|
|
-- data have been received (start + 8 data + parity + stop). The circuit
|
22 |
|
|
-- locks up once an error is detected and will not resume operation until
|
23 |
|
|
-- a reset is applied.
|
24 |
|
|
--
|
25 |
|
|
|
26 |
|
|
|
27 |
|
|
|
28 |
|
|
library IEEE;
|
29 |
|
|
use IEEE.std_logic_1164.all;
|
30 |
|
|
use IEEE.numeric_std.all;
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
package ps2_kbd_pckg is
|
34 |
|
|
component ps2_kbd
|
35 |
|
|
generic(
|
36 |
|
|
FREQ : natural := 100_000 -- frequency of the main clock (KHz)
|
37 |
|
|
);
|
38 |
|
|
port(
|
39 |
|
|
clk : in std_logic; -- main clock
|
40 |
|
|
rst : in std_logic; -- asynchronous reset
|
41 |
|
|
ps2_clk : in std_logic; -- clock from keyboard
|
42 |
|
|
ps2_data : in std_logic; -- data from keyboard
|
43 |
|
|
scancode : out std_logic_vector(7 downto 0); -- key scancode
|
44 |
|
|
parity : out std_logic; -- parity bit for scancode
|
45 |
|
|
busy : out std_logic; -- busy receiving scancode
|
46 |
|
|
rdy : out std_logic; -- scancode ready pulse
|
47 |
|
|
error : out std_logic -- error receiving scancode
|
48 |
|
|
);
|
49 |
|
|
end component ps2_kbd;
|
50 |
|
|
end package ps2_kbd_pckg;
|
51 |
|
|
|
52 |
|
|
|
53 |
|
|
|
54 |
|
|
library IEEE;
|
55 |
|
|
use IEEE.std_logic_1164.all;
|
56 |
|
|
use IEEE.numeric_std.all;
|
57 |
|
|
|
58 |
|
|
|
59 |
|
|
entity ps2_kbd is
|
60 |
|
|
generic(
|
61 |
|
|
FREQ : natural := 100_000 -- frequency of the main clock (KHz)
|
62 |
|
|
);
|
63 |
|
|
port(
|
64 |
|
|
clk : in std_logic; -- main clock
|
65 |
|
|
rst : in std_logic; -- asynchronous reset
|
66 |
|
|
ps2_clk : in std_logic; -- clock from keyboard
|
67 |
|
|
ps2_data : in std_logic; -- data from keyboard
|
68 |
|
|
scancode : out std_logic_vector(7 downto 0); -- key scancode
|
69 |
|
|
parity : out std_logic; -- parity bit for scancode
|
70 |
|
|
busy : out std_logic; -- busy receiving scancode
|
71 |
|
|
rdy : out std_logic; -- scancode ready pulse
|
72 |
|
|
error : out std_logic -- error receiving scancode
|
73 |
|
|
);
|
74 |
|
|
end entity ps2_kbd;
|
75 |
|
|
|
76 |
|
|
|
77 |
|
|
architecture arch of ps2_kbd is
|
78 |
|
|
|
79 |
|
|
constant YES : std_logic := '1';
|
80 |
|
|
constant NO : std_logic := '0';
|
81 |
|
|
constant PS2_FREQ : natural := 10; -- keyboard clock frequency (KHz)
|
82 |
|
|
constant TIMEOUT : natural := FREQ / PS2_FREQ; -- ps2_clk quiet timeout
|
83 |
|
|
constant KEY_RELEASE : std_logic_vector(7 downto 0) := "11110000"; -- scancode sent when key is released
|
84 |
|
|
|
85 |
|
|
signal timer_x, timer_r : natural range 0 to TIMEOUT; -- counts time since last PS/2 clock edge
|
86 |
|
|
signal bitcnt_x, bitcnt_r : natural range 0 to 11; -- counts number of received scancode bits
|
87 |
|
|
signal ps2_clk_x, ps2_clk_r : std_logic_vector(5 downto 1); -- PS/2 clock synchronization / edge detect shift register
|
88 |
|
|
signal ps2_clk_fall_edge : std_logic; -- pulses on falling edge of PS/2 clock
|
89 |
|
|
signal ps2_clk_rise_edge : std_logic; -- pulses on rising edge of PS/2 clock
|
90 |
|
|
signal ps2_clk_edge : std_logic; -- pulses on either edge of PS/2 clock
|
91 |
|
|
signal ps2_clk_quiet : std_logic; -- pulses when no edges on PS/2 clock for TIMEOUT
|
92 |
|
|
signal sc_x, sc_r : std_logic_vector(9 downto 0); -- scancode shift register
|
93 |
|
|
signal keyrel_x, keyrel_r : std_logic; -- this flag is set when the key release scancode is received
|
94 |
|
|
signal scancode_rdy : std_logic; -- indicates when any scancode has been received
|
95 |
|
|
signal rdy_x, rdy_r : std_logic; -- this flag is set when scancode for the pressed key is ready
|
96 |
|
|
signal error_x, error_r : std_logic; -- this flag is set when an error occurs
|
97 |
|
|
|
98 |
|
|
begin
|
99 |
|
|
|
100 |
|
|
-- shift the level on the PS/2 clock into a shift register
|
101 |
|
|
ps2_clk_x <= ps2_clk_r(4 downto 1) & ps2_clk;
|
102 |
|
|
|
103 |
|
|
-- look at the PS/2 clock levels stored in the shift register and find rising or falling edges
|
104 |
|
|
ps2_clk_fall_edge <= YES when ps2_clk_r(5 downto 2) = "1100" else NO;
|
105 |
|
|
ps2_clk_rise_edge <= YES when ps2_clk_r(5 downto 2) = "0011" else NO;
|
106 |
|
|
ps2_clk_edge <= ps2_clk_fall_edge or ps2_clk_rise_edge;
|
107 |
|
|
|
108 |
|
|
-- shift the keyboard scancode into the shift register on the falling edge of the PS/2 clock
|
109 |
|
|
sc_x <= ps2_data & sc_r(9 downto 1) when ps2_clk_fall_edge = YES else sc_r;
|
110 |
|
|
|
111 |
|
|
-- clear the timer right after a PS/2 clock edge and then keep incrementing it until the next edge
|
112 |
|
|
timer_x <= 0 when ps2_clk_edge = YES else timer_r + 1;
|
113 |
|
|
|
114 |
|
|
-- indicate when the PS/2 clock has stopped pulsing and is at a high level.
|
115 |
|
|
ps2_clk_quiet <= YES when timer_r = TIMEOUT and ps2_clk_r(2) = '1' else NO;
|
116 |
|
|
|
117 |
|
|
-- increment the bit counter on each falling edge of the PS/2 clock.
|
118 |
|
|
-- reset the bit counter if the PS/2 clock stops pulsing or if there was an error receiving the scancode.
|
119 |
|
|
-- otherwise, keep the bit counter unchanged.
|
120 |
|
|
bitcnt_x <= bitcnt_r + 1 when ps2_clk_fall_edge = YES else
|
121 |
|
|
|
122 |
|
|
bitcnt_r;
|
123 |
|
|
|
124 |
|
|
-- a scancode has been received if the bit counter is 11 and the PS/2 clock has stopped pulsing
|
125 |
|
|
scancode_rdy <= YES when bitcnt_r = 11 and ps2_clk_quiet = YES else NO;
|
126 |
|
|
|
127 |
|
|
-- look for the scancode sent when the key is released
|
128 |
|
|
keyrel_x <= YES when sc_r(scancode'range) = KEY_RELEASE and scancode_rdy = YES else
|
129 |
|
|
NO when rdy_r = YES or error_r = YES else
|
130 |
|
|
keyrel_r;
|
131 |
|
|
|
132 |
|
|
-- the scancode for the pressed key arrives after receiving the key-release scancode
|
133 |
|
|
rdy_x <= YES when keyrel_r = YES and scancode_rdy = YES else NO;
|
134 |
|
|
|
135 |
|
|
-- indicate an error if the clock is low for too long or if it stops pulsing in the middle of a scancode
|
136 |
|
|
error_x <= YES when (timer_r = TIMEOUT and ps2_clk_r(2) = '0') or
|
137 |
|
|
(ps2_clk_quiet = YES and bitcnt_r/=11 and bitcnt_r/=0) else
|
138 |
|
|
error_r;
|
139 |
|
|
|
140 |
|
|
scancode <= sc_r(scancode'range); -- output scancode
|
141 |
|
|
parity <= sc_r(scancode'high+1); -- output parity bit for the scancode
|
142 |
|
|
busy <= YES when bitcnt_r/=0 else NO; -- output busy signal when receiving a scancode
|
143 |
|
|
rdy <= rdy_r; -- output scancode ready flag
|
144 |
|
|
error <= error_r; -- output error flag
|
145 |
|
|
|
146 |
|
|
-- update the various registers
|
147 |
|
|
process(rst, clk)
|
148 |
|
|
begin
|
149 |
|
|
if rst = YES then
|
150 |
|
|
ps2_clk_r <= (others => '1'); -- start by assuming PS/2 clock has been high for a while
|
151 |
|
|
sc_r <= (others => '0'); -- clear scancode register
|
152 |
|
|
keyrel_r <= NO; -- key-release scancode has not been received yet
|
153 |
|
|
rdy_r <= NO; -- no scancodes received yet
|
154 |
|
|
timer_r <= 0; -- clear PS/2 clock pulse timer
|
155 |
|
|
bitcnt_r <= 0; -- clear scancode bit counter
|
156 |
|
|
error_r <= NO; -- clear any errors
|
157 |
|
|
elsif rising_edge(clk) then
|
158 |
|
|
ps2_clk_r <= ps2_clk_x;
|
159 |
|
|
sc_r <= sc_x;
|
160 |
|
|
keyrel_r <= keyrel_x;
|
161 |
|
|
rdy_r <= rdy_x;
|
162 |
|
|
timer_r <= timer_x;
|
163 |
|
|
bitcnt_r <= bitcnt_x;
|
164 |
|
|
error_r <= error_x;
|
165 |
|
|
end if;
|
166 |
|
|
end process;
|
167 |
|
|
|
168 |
|
|
end architecture arch;
|