-
Notifications
You must be signed in to change notification settings - Fork 0
/
ps2_tx.v
146 lines (136 loc) · 3.65 KB
/
ps2_tx.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
`timescale 1ns / 1ps
module ps2_tx
(
input wire clk, reset,
input wire wr_ps2,
input wire [7:0] din,
inout wire ps2d, ps2c,
output reg tx_idle, tx_done_tick
);
// symbolic state declaration
localparam [2:0]
idle = 3'b000,
rts = 3'b001,
start = 3'b010,
data = 3'b011,
stop = 3'b100;
// signal declaration
reg [2:0] state_reg, state_next;
reg [7:0] filter_reg;
wire [7:0] filter_next;
reg f_ps2c_reg;
wire f_ps2c_next;
reg [3:0] n_reg, n_next;
reg [8:0] b_reg, b_next;
reg [12:0] c_reg, c_next;
wire par, fall_edge;
reg ps2c_out, ps2d_out;
reg tri_c, tri_d;
// body
//=================================================
// filter and falling-edge tick generation for ps2c
//=================================================
always @(posedge clk, posedge reset)
if (reset)
begin
filter_reg <= 0;
f_ps2c_reg <= 0;
end
else
begin
filter_reg <= filter_next;
f_ps2c_reg <= f_ps2c_next;
end
assign filter_next = {ps2c, filter_reg[7:1]};
assign f_ps2c_next = (filter_reg==8'b11111111) ? 1'b1 :
(filter_reg==8'b00000000) ? 1'b0 :
f_ps2c_reg;
assign fall_edge = f_ps2c_reg & ~f_ps2c_next;
//=================================================
// FSMD
//=================================================
// FSMD state & data registers
always @(posedge clk, posedge reset)
if (reset)
begin
state_reg <= idle;
c_reg <= 0;
n_reg <= 0;
b_reg <= 0;
end
else
begin
state_reg <= state_next;
c_reg <= c_next;
n_reg <= n_next;
b_reg <= b_next;
end
// odd parity bit
assign par = ~(^din);
// FSMD next-state logic
always @*
begin
state_next = state_reg;
c_next = c_reg;
n_next = n_reg;
b_next = b_reg;
tx_done_tick = 1'b0;
ps2c_out = 1'b1;
ps2d_out = 1'b1;
tri_c = 1'b0;
tri_d = 1'b0;
tx_idle = 1'b0;
case (state_reg)
idle:
begin
tx_idle = 1'b1;
if (wr_ps2)
begin
b_next = {par, din};
c_next = 13'h1fff; // 2^13-1
state_next = rts;
end
end
rts: // request to send
begin
ps2c_out = 1'b0;
tri_c = 1'b1;
c_next = c_reg - 1;
if (c_reg==0)
state_next = start;
end
start: // assert start bit
begin
ps2d_out = 1'b0;
tri_d = 1'b1;
if (fall_edge)
begin
n_next = 4'h8;
state_next = data;
end
end
data: // 8 data + 1 parity
begin
ps2d_out = b_reg[0];
tri_d = 1'b1;
if (fall_edge)
begin
b_next = {1'b0, b_reg[8:1]};
if (n_reg == 0)
state_next = stop;
else
n_next = n_reg - 1;
end
end
stop: // assume floating high for ps2d
if (fall_edge)
begin
state_next = idle;
tx_done_tick = 1'b1;
end
endcase
end
// tri-state buffers
assign ps2c = (tri_c) ? ps2c_out : 1'bz;
assign ps2d = (tri_d) ? ps2d_out : 1'bz;
endmodule