-
Notifications
You must be signed in to change notification settings - Fork 0
/
ps2_iobase.vhd
225 lines (211 loc) · 6.03 KB
/
ps2_iobase.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
-------------------------------------------------------------------------------
-- Title : MC613
-- Project : PS2 Basic Protocol
-- Details : www.ic.unicamp.br/~corte/mc613/
-- www.computer-engineering.org/ps2protocol/
-------------------------------------------------------------------------------
-- File : ps2_base.vhd
-- Author : Thiago Borges Abdnur
-- Company : IC - UNICAMP
-- Last update: 2010/04/12
-------------------------------------------------------------------------------
-- Description:
-- PS2 basic control
-------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
entity ps2_iobase is
generic(
clkfreq : integer -- This is the system clock value in kHz
);
port(
ps2_data : inout std_logic; -- PS2 data pin
ps2_clk : inout std_logic; -- PS2 clock pin
clk : in std_logic; -- system clock (same frequency as defined in
-- 'clkfreq' generic)
en : in std_logic; -- Enable
resetn : in std_logic; -- Reset when '0'
idata_rdy : in std_logic; -- Rise this to signal data is ready to be sent
-- to device
idata : in std_logic_vector(7 downto 0); -- Data to be sent to device
send_rdy : out std_logic; -- '1' if data can be sent to device (wait for
-- this before rising 'idata_rdy'
odata_rdy : out std_logic; -- '1' when data from device has arrived
odata : out std_logic_vector(7 downto 0) -- Data from device
);
end;
architecture rtl of ps2_iobase is
constant CLKSSTABLE : integer := clkfreq / 150;
signal sdata, hdata : std_logic_vector(7 downto 0);
signal sigtrigger, parchecked, sigsending,
sigsendend, sigclkreleased, sigclkheld : std_logic;
begin
-- Trigger for state change to eliminate noise
process(clk, ps2_clk, en, resetn)
variable fcount, rcount : integer range CLKSSTABLE downto 0;
begin
if(rising_edge(clk) and en = '1') then
-- Falling edge noise
if ps2_clk = '0' then
rcount := 0;
if fcount >= CLKSSTABLE then
sigtrigger <= '1';
else
fcount := fcount + 1;
end if;
-- Rising edge noise
elsif ps2_clk = '1' then
fcount := 0;
if rcount >= CLKSSTABLE then
sigtrigger <= '0';
else
rcount := rcount + 1;
end if;
end if;
end if;
if resetn = '0' then
fcount := 0;
rcount := 0;
sigtrigger <= '0';
end if;
end process;
FROMPS2:
process(sigtrigger, sigsending, resetn)
variable count : integer range 0 to 11;
begin
if(rising_edge(sigtrigger) and sigsending = '0') then
if count > 0 and count < 9 then
sdata(count - 1) <= ps2_data;
end if;
if count = 9 then
if (not (sdata(0) xor sdata(1) xor sdata(2) xor sdata(3)
xor sdata(4) xor sdata(5) xor sdata(6) xor sdata(7))) = ps2_data then
parchecked <= '1';
else
parchecked <= '0';
end if;
end if;
count := count + 1;
if count = 11 then
count := 0;
parchecked <= '0';
end if;
end if;
if resetn = '0' or sigsending = '1' then
sdata <= (others => '0');
parchecked <= '0';
count := 0;
end if;
end process;
odata_rdy <= en and parchecked;
odata <= sdata;
-- Edge triggered send register
process(idata_rdy, sigsendend, resetn)
begin
if(rising_edge(idata_rdy)) then
sigsending <= '1';
end if;
if resetn = '0' or sigsendend = '1' then
sigsending <= '0';
end if;
end process;
-- Wait for at least 11ms before allowing to send again
process(clk, sigsending, resetn)
-- clkfreq is the number of clocks within a milisecond
variable countclk : integer range 0 to (12 * clkfreq);
begin
if(rising_edge(clk) and sigsending = '0') then
if countclk = (11 * clkfreq) then
send_rdy <= '1';
else
countclk := countclk + 1;
end if;
end if;
if sigsending = '1' then
send_rdy <= '0';
countclk := 0;
end if;
if resetn = '0' then
send_rdy <= '1';
countclk := 0;
end if;
end process;
-- Host input data register
process(idata_rdy, sigsendend, resetn)
begin
if(rising_edge(idata_rdy)) then
hdata <= idata;
end if;
if resetn = '0' or sigsendend = '1' then
hdata <= (others => '0');
end if;
end process;
-- PS2 clock control
process(clk, sigsendend, resetn)
constant US100CNT : integer := clkfreq / 10;
variable count : integer range 0 to US100CNT + 101;
begin
if(rising_edge(clk) and sigsending = '1') then
if count < US100CNT + 50 then
count := count + 1;
ps2_clk <= '0';
sigclkreleased <= '0';
sigclkheld <= '0';
elsif count < US100CNT + 100 then
count := count + 1;
ps2_clk <= '0';
sigclkreleased <= '0';
sigclkheld <= '1';
else
ps2_clk <= 'Z';
sigclkreleased <= '1';
sigclkheld <= '0';
end if;
end if;
if resetn = '0' or sigsendend = '1' then
ps2_clk <= 'Z';
sigclkreleased <= '1';
sigclkheld <= '0';
count := 0;
end if;
end process;
-- Sending control
TOPS2:
process(sigtrigger, sigsending, sigclkheld, sigclkreleased, resetn)
variable count : integer range 0 to 11;
begin
if(rising_edge(sigtrigger) and sigclkreleased = '1'
and sigsending = '1') then
if count >= 0 and count < 8 then
ps2_data <= hdata(count);
sigsendend <= '0';
end if;
if count = 8 then
ps2_data <= (not (hdata(0) xor hdata(1) xor hdata(2) xor hdata(3)
xor hdata(4) xor hdata(5) xor hdata(6) xor hdata(7)));
sigsendend <= '0';
end if;
if count = 9 then
ps2_data <= 'Z';
sigsendend <= '0';
end if;
if count = 10 then
ps2_data <= 'Z';
sigsendend <= '1';
count := 0;
end if;
count := count + 1;
end if;
if sigclkheld = '1' then
ps2_data <= '0';
sigsendend <= '0';
count := 0;
end if;
if resetn = '0' or sigsending = '0' then
ps2_data <= 'Z';
sigsendend <= '0';
count := 0;
end if;
end process;
end rtl;