-
Notifications
You must be signed in to change notification settings - Fork 11
/
PS2_Ctrl.vhd
147 lines (119 loc) · 4.33 KB
/
PS2_Ctrl.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
-- PS2_Ctrl.vhd
-- ------------------------------------------------
-- Simplified PS/2 Controller (kbd, mouse...)
-- ------------------------------------------------
-- Only the Receive function is implemented !
-- (c) ALSE. http://www.alse-fr.com
library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Numeric_Std.all;
-- --------------------------------------
Entity PS2_Ctrl is
-- --------------------------------------
generic (FilterSize : positive := 8);
port( Clk : in std_logic; -- System Clock
Reset : in std_logic; -- System Reset
PS2_Clk : in std_logic; -- Keyboard Clock Line
PS2_Data : in std_logic; -- Keyboard Data Line
DoRead : in std_logic; -- From outside when reading the scan code
Scan_Err : out std_logic; -- To outside : Parity or Overflow error
Scan_DAV : out std_logic; -- To outside when a scan code has arrived
Scan_Code : out unsigned(7 downto 0) -- Eight bits Data Out
);
end PS2_Ctrl;
-- --------------------------------------
Architecture ALSE_RTL of PS2_Ctrl is
-- --------------------------------------
-- (c) ALSE. http://www.alse-fr.com
-- Author : Bert Cuzeau.
-- Fully synchronous solution, same Filter on PS2_Clk.
-- Still as compact as "Plain_wrong"...
-- Possible improvement : add TIMEOUT on PS2_Clk while shifting
-- Note: PS2_Data is resynchronized though this should not be
-- necessary (qualified by Fall_Clk and does not change at that time).
-- Note the tricks to correctly interpret 'H' as '1' in RTL simulation.
signal PS2_Datr : std_logic;
subtype Filter_t is std_logic_vector(FilterSize-1 downto 0);
signal Filter : Filter_t;
signal Fall_Clk : std_logic;
signal Bit_Cnt : unsigned(3 downto 0);
signal Parity : std_logic;
signal Scan_DAVi : std_logic;
signal S_Reg : unsigned(8 downto 0);
signal PS2_Clk_f : std_logic;
Type State_t is (Idle, Shifting);
signal State : State_t;
begin
Scan_DAV <= Scan_DAVi;
-- This filters digitally the raw clock signal coming from the keyboard :
-- * Eight consecutive PS2_Clk=1 makes the filtered_clock go high
-- * Eight consecutive PS2_Clk=0 makes the filtered_clock go low
-- Implies a (FilterSize+1) x Tsys_clock delay on Fall_Clk wrt Data
-- Also in charge of the re-synchronization of PS2_Data
process (Clk,Reset)
begin
if Reset='1' then
PS2_Datr <= '0';
PS2_Clk_f <= '0';
Filter <= (others=>'0');
Fall_Clk <= '0';
elsif rising_edge (Clk) then
PS2_Datr <= PS2_Data and PS2_Data; -- also turns 'H' into '1'
Fall_Clk <= '0';
Filter <= (PS2_Clk and PS2_CLK) & Filter(Filter'high downto 1);
if Filter = Filter_t'(others=>'1') then
PS2_Clk_f <= '1';
elsif Filter = Filter_t'(others=>'0') then
PS2_Clk_f <= '0';
if PS2_Clk_f = '1' then
Fall_Clk <= '1';
end if;
end if;
end if;
end process;
-- This simple State Machine reads in the Serial Data
-- coming from the PS/2 peripheral.
process(Clk,Reset)
begin
if Reset='1' then
State <= Idle;
Bit_Cnt <= (others => '0');
S_Reg <= (others => '0');
Scan_Code <= (others => '0');
Parity <= '0';
Scan_Davi <= '0';
Scan_Err <= '0';
elsif rising_edge (Clk) then
if DoRead='1' then
Scan_Davi <= '0'; -- note: this assgnmnt can be overriden
end if;
case State is
when Idle =>
Parity <= '0';
Bit_Cnt <= (others => '0');
-- note that we dont need to clear the Shift Register
if Fall_Clk='1' and PS2_Datr='0' then -- Start bit
Scan_Err <= '0';
State <= Shifting;
end if;
when Shifting =>
if Bit_Cnt >= 9 then
if Fall_Clk='1' then -- Stop Bit
-- Error is (wrong Parity) or (Stop='0') or Overflow
Scan_Err <= (not Parity) or (not PS2_Datr) or Scan_DAVi;
Scan_Davi <= '1';
Scan_Code <= S_Reg(7 downto 0);
State <= Idle;
end if;
elsif Fall_Clk='1' then
Bit_Cnt <= Bit_Cnt + 1;
S_Reg <= PS2_Datr & S_Reg (S_Reg'high downto 1); -- Shift right
Parity <= Parity xor PS2_Datr;
end if;
when others => -- never reached
State <= Idle;
end case;
--Scan_Err <= '0'; -- to create an on-purpose error on Scan_Err !
end if;
end process;
end ALSE_RTL;