-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathps2.v
173 lines (154 loc) · 3.83 KB
/
ps2.v
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
`timescale 1ns / 100ps
/*
* Generic PS2 interface module
*
* istrobe, oreq, oack and timeout are all 1-clk strobes,
* ibyte must be latched on that strobe, obyte is latched
* as oreq is detected, oreq is ignore while already
* sending.
*
* we ignore bad parity on input for now
*/
module ps2(input sysclk,
input clk_en,
input reset,
// inout ps2dat,
// inout ps2clk,
input ps2dat,
input ps2clk,
output istrobe,
output [7:0] ibyte,
input oreq,
input [7:0] obyte,
output oack,
output timeout,
output[1:0] dbg_state
);
reg [7:0] clkbuf;
reg [7:0] datbuf;
reg clksync;
reg clkprev;
reg datsync;
reg [10:0] shiftreg;
reg [3:0] shiftcnt;
wire shiftend;
reg [1:0] state;
wire datout;
reg [23:0] timecnt;
wire clkdown;
wire opar;
/* State machine */
localparam ps2_state_idle = 0;
localparam ps2_state_ring = 1;
localparam ps2_state_send = 2;
localparam ps2_state_recv = 3;
always@(posedge sysclk or posedge reset) begin
if (reset)
state <= ps2_state_idle;
else if (clk_en) begin
if (timeout && !oreq)
state <= ps2_state_idle;
else
case(state)
ps2_state_idle: begin
if (oreq)
state <= ps2_state_ring;
else if (clkdown)
state <= ps2_state_recv;
end
ps2_state_ring: begin
if (timecnt[12])
state <= ps2_state_send;
end
ps2_state_send: begin
if (shiftend)
state <= ps2_state_idle;
end
ps2_state_recv: begin
if (oreq)
state <= ps2_state_ring;
else if (shiftend)
state <= ps2_state_idle;
end
endcase
end
end
assign dbg_state = state;
/* Tristate control of clk & data */
assign datout = state == ps2_state_ring || state == ps2_state_send;
// assign ps2dat = (datout & ~shiftreg[0]) ? 1'b0 : 1'bz;
// assign ps2clk = (state == ps2_state_ring) ? 1'b0 : 1'bz;
/* Bit counter */
always@(posedge sysclk or posedge reset) begin
if (reset)
shiftcnt <= 10;
else if (clk_en) begin
if (state == ps2_state_idle)
shiftcnt <= 10;
else if (state == ps2_state_ring)
shiftcnt <= 11;
else if (clkdown && state != ps2_state_ring)
shiftcnt <= shiftcnt - 1'b1;
end
end
/* Shift register, ticks on falling edge of ps2 clock */
always@(posedge sysclk or posedge reset) begin
if (reset)
shiftreg <= 0;
else if (clk_en) begin
if (oreq)
shiftreg <= { 1'b1, opar, obyte, 1'b0 };
else if (clkdown && state != ps2_state_ring)
shiftreg <= { datsync, shiftreg[10:1] };
end
end
/* Ack/strobe logic */
assign shiftend = shiftcnt == 0;
assign oack = (state == ps2_state_send && shiftend);
assign istrobe = (state == ps2_state_recv && shiftend);
assign ibyte = shiftreg[8:1];
/* Filters/synchronizers on PS/2 clock */
always@(posedge sysclk or posedge reset) begin
if (reset) begin
clkbuf <= 0;
clksync <= 0;
clkprev <= 0;
end else if (clk_en) begin
clkprev <= clksync;
clkbuf <= { clkbuf[6:0], ps2clk };
if (clkbuf[7:2] == 6'b000000)
clksync <= 0;
if (clkbuf[7:2] == 6'b111111)
clksync <= 1;
end
end
assign clkdown = clkprev & ~clksync;
/* Filters/synchronizers on PS/2 data */
always@(posedge sysclk or posedge reset) begin
if (reset) begin
datbuf <= 0;
datsync <= 0;
end else if (clk_en) begin
datbuf <= { datbuf[6:0], ps2dat };
if (datbuf[7:2] == 6'b000000)
datsync <= 0;
if (datbuf[7:2] == 6'b111111)
datsync <= 1;
end
end
/* Parity for output byte */
assign opar = ~(obyte[0] ^ obyte[1] ^ obyte[2] ^ obyte[3] ^
obyte[4] ^ obyte[5] ^ obyte[6] ^ obyte[7]);
/* Timeout logic */
always@(posedge sysclk or posedge reset) begin
if (reset)
timecnt <= 0;
else if (clk_en) begin
if (clkdown | oreq)
timecnt <= 0;
else
timecnt <= timecnt + 1'b1;
end
end
assign timeout = (timecnt == 24'hff_ffff);
endmodule