-
Notifications
You must be signed in to change notification settings - Fork 0
/
ps2_serial.v
296 lines (247 loc) · 7.14 KB
/
ps2_serial.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
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
/*
* Copyright (c) 2006, Steven Lieberman. All rights reserved. This software
* is supplied as is without expressed or implied warranties of any kind.
*/
module ps2_serial(
input wire clock_25m, // The clock can be any speed that's sufficiently
// faster than the 10-16.7 kHz signal produced
// by the PS/2 serial interface.
input wire clock_valid,
input wire reset_25m,
input wire PS2_CLK,
input wire PS2_DAT,
input wire ack,
output wire [7:0] data,
output reg valid);
reg ps2_dat_sync;
reg ps2_clk_sync;
reg [7:0] ps2_clk_history;
reg data_reg_shift;
reg buf_read;
reg buf_write;
reg buf_write_if_not_full;
reg [7:0] buf_in;
wire buf_empty;
wire buf_full;
/*
* We need to register PS2_DAT because the state machine calculates
* the next state based on it, and since we're registering PS2_DAT, we
* should do the same for PS2_CLK.
*/
always @(posedge clock_25m) begin
ps2_dat_sync <= PS2_DAT;
ps2_clk_sync <= PS2_CLK;
end
always @(posedge clock_25m) begin
if (clock_valid == 1'b1) begin
if (reset_25m == 1'b1) begin
ps2_clk_history <= 8'h0;
buf_in <= 8'h0;
end else begin
/*
* ps2_clk_history maintains the last 8 seen values of PS2_CLK.
* It is always shifting right, filling in the leftmost bits with
* the new values of PS2_CLK.
*/
ps2_clk_history[7] <= ps2_clk_sync;
ps2_clk_history[6:0] <= ps2_clk_history[7:1];
/*
* buf_in records the data bits that come in off the serial
* interface (LSB first).
*/
if (data_reg_shift == 1'b1) begin
buf_in[7] <= ps2_dat_sync;
buf_in[6:0] <= buf_in[7:1];
end
end
end
end
//TODO: incorporate clock_valid
ps2_serial_buf u1 (clock_25m, buf_in, buf_read, reset_25m, buf_write,
buf_empty, buf_full, data);
always @* begin
if (buf_full == 1'b1) begin
buf_write = 1'b0;
end else begin
buf_write = buf_write_if_not_full;
end
end
//===========================================================================
reg [1:0] consumer_state;
reg [1:0] next_consumer_state;
parameter state_consumer_reset = 2'h0;
parameter state_consumer_idle = 2'h1;
parameter state_consumer_valid = 2'h2;
parameter state_consumer_validlow = 2'h3;
always @(posedge clock_25m) begin
if (clock_valid == 1'b0) begin
end else if (reset_25m == 1'b1) begin
consumer_state <= state_consumer_reset;
end else begin
consumer_state <= next_consumer_state;
end
end
always @* begin
valid = 1'b0;
buf_read = 1'b0; // normally, don't consume values
next_consumer_state = state_consumer_reset;
case (consumer_state)
state_consumer_reset: begin
next_consumer_state = state_consumer_idle;
end
state_consumer_idle: begin
if (buf_empty == 1'b1) begin
next_consumer_state = state_consumer_idle; // loop
end else begin
next_consumer_state = state_consumer_valid;
end
end
state_consumer_valid: begin
valid = 1'b1;
if (ack == 1'b0) begin
next_consumer_state = state_consumer_valid; // loop
end else begin
next_consumer_state = state_consumer_validlow;
end
end
state_consumer_validlow: begin
buf_read = 1'b1; // consume the value we sent out last cycle
if (ack == 1'b1) begin
next_consumer_state = state_consumer_validlow; // loop
end else begin
next_consumer_state = state_consumer_idle;
end
end
endcase
end
//===========================================================================
reg next_active_state_wr_en;
reg [3:0] next_active_state_in;
reg [3:0] next_active_state;
reg [3:0] state;
reg [3:0] next_state;
parameter state_start = 4'h0;
parameter state_data0 = 4'h1;
parameter state_data1 = 4'h2;
parameter state_data2 = 4'h3;
parameter state_data3 = 4'h4;
parameter state_data4 = 4'h5;
parameter state_data5 = 4'h6;
parameter state_data6 = 4'h7;
parameter state_data7 = 4'h8;
parameter state_parity = 4'h9;
parameter state_stop = 4'hA;
parameter state_clocklow = 4'hB;
parameter state_clockhigh = 4'hC;
always @(posedge clock_25m) begin
if (clock_valid == 1'b0) begin
end else if (reset_25m == 1'b1) begin
state <= state_clockhigh;
next_active_state <= state_start;
end else begin
state <= next_state;
if (next_active_state_wr_en == 1'b1) begin
next_active_state <= next_active_state_in;
end
end
end
always @* begin
next_active_state_in = state_start;
next_active_state_wr_en = 1'b0;
data_reg_shift = 1'b0;
buf_write_if_not_full = 1'b0;
/*
* Default to state_clockhigh, since the 'active states' all occur just
* after a positive clock edge -- when they are finished, the clock
* should still be high.
*/
next_state = state_clockhigh;
case (state)
state_start: begin
/*
* Attempts to start; if the bit is start, we'll progress to accept
* data, otherwise, we'll just jump loop back to here.
*/
next_active_state_wr_en = 1'b1;
if (PS2_DAT == 1'b1) begin
next_active_state_in = state_start; // loop
end else begin
next_active_state_in = state_data0;
end
end
state_data0: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data1;
end
state_data1: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data2;
end
state_data2: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data3;
end
state_data3: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data4;
end
state_data4: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data5;
end
state_data5: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data6;
end
state_data6: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_data7;
end
state_data7: begin
data_reg_shift = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_parity;
end
state_parity: begin
//TODO: do something with the parity bit -- for now, it's ignored
next_active_state_wr_en = 1'b1;
next_active_state_in = state_stop;
end
state_stop: begin
buf_write_if_not_full = 1'b1;
next_active_state_wr_en = 1'b1;
next_active_state_in = state_start;
end
state_clocklow: begin
if (ps2_clk_history == 8'hFF) begin
/*
* On the positive edges of the ps2_clk_history, which serves
* as our filtered clock, we want to transistion to the next
* 'true state.'
*/
next_state = next_active_state;
end else begin
next_state = state_clocklow; // loop
end
end
state_clockhigh: begin
if (ps2_clk_history == 8'h00) begin
/*
* This is the negative edge, don't do any processing here.
* Just go and wait for the positive clock edge.
*/
next_state = state_clocklow;
end else begin
next_state = state_clockhigh;
end
end
endcase
end
endmodule