-
Notifications
You must be signed in to change notification settings - Fork 3
/
ps2.vhd
360 lines (318 loc) · 16.8 KB
/
ps2.vhd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
-------------------------------------------------------------------------------
-- Title : PS/2 interface
-- Project : https://opencores.org/websvn/filedetails?repname=ps2core&path=%2Fps2core%2Ftrunk%2Frtl%2Fvhdl%2Fps2.vhd
-------------------------------------------------------------------------------
-- File : ps2.vhd
-- Author : Daniel Quintero <danielqg@infonegocio.com>
-- Company : Itoo Software
-- Created : 2003-04-14
-- Last update: 2003-10-30
-- Platform : VHDL'87
-------------------------------------------------------------------------------
-- Description: PS/2 generic UART for mice/keyboard
-------------------------------------------------------------------------------
-- This code is distributed under the terms and conditions of the
-- GNU General Public License
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2003-04-14 1.0 daniel Created
-------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity ps2 is
port (
clk_i : in std_logic; -- Global clk
rst_i : in std_logic; -- GLobal Asinchronous reset
data_o : out std_logic_vector(7 downto 0); -- Data in
data_i : in std_logic_vector(7 downto 0); -- Data out
ibf_clr_i : in std_logic; -- Ifb flag clear input
obf_set_i : in std_logic; -- Obf flag set input
ibf_o : out std_logic; -- Received data available
obf_o : out std_logic; -- Data ready to sent
frame_err_o : out std_logic; -- Error receiving data
parity_err_o : out std_logic; -- Error in received data parity
busy_o : out std_logic; -- uart busy
err_clr_i : in std_logic; -- Clear error flags
wdt_o : out std_logic; -- Watchdog timer out every 400uS
ps2_clk_io : inout std_logic; -- PS2 Clock line
ps2_data_io : inout std_logic); -- PS2 Data line
end ps2;
architecture rtl of ps2 is
type states is (idle, write_request, start, data, parity, stop);
type debounce_states is (stable, rise, fall, wait_stable);
--constant DEBOUNCE_TIMEOUT : integer := 200; -- clks to debounce the ps2_clk signal
constant DEBOUNCE_BITS : integer := 8;
--constant WATCHDOG_TIMEOUT : integer := 19200 / DEBOUNCE_TIMEOUT; -- clks to wait 400uS
constant WATCHDOG_BITS : integer := 8;
signal state : states;
signal debounce_state : debounce_states;
signal debounce_cnt : std_logic_vector(DEBOUNCE_BITS-1 downto 0);
signal debounce_cao : std_logic;
signal ps2_clk_syn : std_logic; -- PS2 clock input syncronized
signal ps2_clk_clean : std_logic; -- PS2 clock debounced and clean
signal ps2_clk_fall : std_logic; -- PS2 clock fall edge
signal ps2_clk_rise : std_logic; -- PS2 clock rise edge
signal ps2_data_syn : std_logic; -- PS2 data input syncronized
signal ps2_clk_out : std_logic; -- PS2 clock output
signal ps2_data_out : std_logic; -- PS2 clock output
signal writing : std_logic; -- read / write cycle flag
signal shift_cnt : std_logic_vector(2 downto 0);
signal shift_cao : std_logic; -- Shift counter carry out
signal shift_reg : std_logic_vector(8 downto 0);
signal shift_in : std_logic; -- Shift register to right
signal shift_load : std_logic; -- Shift register parallel load
signal shift_calc_parity : std_logic; -- Shift register set parity
signal wdt_cnt : std_logic_vector(WATCHDOG_BITS-1 downto 0);
signal wdt_rst : std_logic; -- watchdog reset
signal wdt_cao : std_logic; -- watchdog carry out
signal shift_parity : std_logic; -- Current parity of shift_reg
signal ibf : std_logic; -- IBF, In Buffer Full
signal obf : std_logic; -- OBF, Out Buffer Full
signal parity_err : std_logic; -- Parity error
signal frame_err : std_logic; -- Frame error
begin -- rtl
-- Sincronize input signals
syn_ps2 : process (clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
ps2_clk_syn <= '0';
ps2_data_syn <= '0';
elsif clk_i'event and clk_i = '1' then -- rising clock edge
ps2_clk_syn <= TO_X01(ps2_clk_io);
ps2_data_syn <= TO_X01(ps2_data_io);
end if;
end process syn_ps2;
-- clk debounce timer
debounce_count : process (clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
debounce_cnt <= (others => '0');
elsif clk_i'event and clk_i = '1' then -- rising clock edge
if (ps2_clk_fall or ps2_clk_rise or debounce_cao) = '1' then
debounce_cnt <= (others => '0');
else
debounce_cnt <= debounce_cnt + 1;
end if;
end if;
end process;
debounce_cao <= debounce_cnt(DEBOUNCE_BITS-1);
-- debounce_cao <= '1' when debounce_cnt =
-- CONV_STD_LOGIC_VECTOR(DEBOUNCE_TIMEOUT-1, DEBOUNCE_BITS)
-- else '0';
-- PS2 clock debounce and edge detector
debounce_stm : process (clk_i, rst_i)
begin
if rst_i = '0' then
debounce_state <= stable;
ps2_clk_clean <= '0';
elsif clk_i'event and clk_i = '1' then
case debounce_state is
when stable =>
if ps2_clk_clean /= ps2_clk_syn then
if ps2_clk_syn = '1' then
debounce_state <= rise;
else
debounce_state <= fall;
end if;
end if;
when wait_stable =>
if debounce_cao = '1' then
debounce_state <= stable;
end if;
when rise => debounce_state <= wait_stable;
ps2_clk_clean <= '1';
when fall => debounce_state <= wait_stable;
ps2_clk_clean <= '0';
when others => null;
end case;
end if;
end process;
ps2_clk_fall <= '1' when debounce_state = fall else '0';
ps2_clk_rise <= '1' when debounce_state = rise else '0';
-- PS2 watchdog
wdt_proc : process(clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
wdt_cnt <= (others => '0');
elsif clk_i'event and clk_i = '1' then -- rising clock edge
if (wdt_rst or wdt_cao) = '1' then
wdt_cnt <= (others => '0');
elsif debounce_cao = '1' then
wdt_cnt <= wdt_cnt + 1;
end if;
end if;
end process;
wdt_cao <= wdt_cnt(WATCHDOG_BITS-1);
-- wdt_cao <= '1' when wdt_cnt =
-- CONV_STD_LOGIC_VECTOR(WATCHDOG_TIMEOUT-1, WATCHDOG_BITS)
-- else '0';
wdt_rst <= ps2_clk_fall;
-- Shift register
shift : process (clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
shift_reg <= (others => '0');
elsif clk_i'event and clk_i = '1' then -- rising clock edge
if shift_load = '1' then
shift_reg(7 downto 0) <= data_i;
shift_reg(8) <= '0';
elsif shift_calc_parity = '1' then
shift_reg(8) <= not shift_parity;
elsif shift_in = '1' then
shift_reg(7 downto 0) <= shift_reg(8 downto 1);
shift_reg(8) <= ps2_data_syn;
end if;
end if;
end process;
-- Shift counter
sft_cnt : process(clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
shift_cnt <= (others => '0');
elsif clk_i'event and clk_i = '1' then -- rising clock edge
if state = start then
shift_cnt <= (others => '0');
elsif state = data and ps2_clk_fall = '1' then
shift_cnt <= shift_cnt + 1;
end if;
end if;
end process;
shift_cao <= '1' when shift_cnt = "111" else '0';
-- Odd Parity generator
shift_parity <= (shift_reg(0) xor
shift_reg(1) xor
shift_reg(2) xor
shift_reg(3) xor
shift_reg(4) xor
shift_reg(5) xor
shift_reg(6) xor
shift_reg(7));
-- Main State Machine
stm : process (clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
state <= idle;
writing <= '0';
elsif clk_i'event and clk_i = '1' then -- rising clock edge
case state is
-- Waiting for clk
when idle => if obf_set_i = '1' and writing = '0' then
state <= write_request;
writing <= '1';
elsif ps2_clk_fall = '1' then
state <= start;
end if;
-- Write request, clk low
when write_request => if wdt_cao = '1' then
state <= idle;
end if;
-- Clock 1, start bit
when start => if wdt_cao = '1' then
state <= idle;
elsif ps2_clk_fall = '1' then
state <= data;
end if;
-- Clocks 2-9, Data bits (LSB first)
when data => if wdt_cao = '1' then
state <= idle;
elsif ps2_clk_fall = '1' and
shift_cao = '1' then
state <= parity;
end if;
-- Clock 10, Parity bit
when parity => if wdt_cao = '1' then
state <= idle;
elsif ps2_clk_fall = '1' then
state <= stop;
end if;
-- Clock 11, Stop bit
when stop => writing <= '0';
state <= idle;
when others => null;
end case;
end if;
end process;
-- State flags
flags_proc : process (clk_i, rst_i, state, writing)
begin -- process stm_out
-- Input Buffer write flag
if rst_i = '0' then -- asynchronous reset (active low)
--obf <= '0';
ibf <= '0';
parity_err <= '0';
frame_err <= '0';
elsif clk_i'event and clk_i = '1' then -- rising clock edge
-- Parity error flag
if err_clr_i = '1' then
parity_err <= '0';
elsif writing = '0' and state = stop then
if shift_reg(8) /= not shift_parity then
parity_err <= '1';
end if;
end if;
-- Frame error flag
if err_clr_i = '1' then
frame_err <= '0';
elsif (state = start or
state = data or state = parity) and wdt_cao = '1' then
frame_err <= '1';
end if;
-- Input Buffer full flag
if ibf_clr_i = '1' then
ibf <= '0';
elsif writing = '0' and state = stop then
if shift_reg(8) = not shift_parity then
ibf <= '1';
end if;
end if;
-- Output buffer full flag
--if state = stop and writing = '1' then
-- obf <= '0';
--elsif obf_set_i = '1' then
-- obf <= '1';
--end if;
end if;
end process;
obf <= writing;
-- Shift register control
shift_load <= '1' when obf_set_i = '1' else '0';
shift_calc_parity <= '1' when state = idle and writing = '1' else '0';
shift_in <= ps2_clk_fall when state = data or state = start else '0';
-- PS2 Registered outputs
syn_ps2_out : process (clk_i, rst_i)
begin
if rst_i = '0' then -- asynchronous reset (active low)
ps2_data_out <= '1';
ps2_clk_out <= '1';
elsif clk_i'event and clk_i = '1' then -- rising clock edge
-- PS2 Data out
if writing = '1' then
if state = idle then
ps2_data_out <= '0';
elsif state = data or state = start then
ps2_data_out <= shift_reg(0);
else
ps2_data_out <= '1';
end if;
end if;
-- PS2 Clk out
if state = write_request then
ps2_clk_out <= '0';
else
ps2_clk_out <= '1';
end if;
end if;
end process;
data_o <= shift_reg(7 downto 0);
ibf_o <= ibf;
obf_o <= obf;
busy_o <= '0' when state = idle and writing = '0' else '1';
parity_err_o <= parity_err;
frame_err_o <= frame_err;
wdt_o <= wdt_cao;
ps2_clk_io <= '0' when ps2_clk_out = '0' else 'Z';
ps2_data_io <= '0' when ps2_data_out = '0' else 'Z';
end rtl;