-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmdio.v
219 lines (194 loc) · 4.38 KB
/
mdio.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
// SPDX-License-Identifier: AGPL-3.0-Only OR CERN-OHL-S-2.0
/*
* Copyright (C) 2022 Sean Anderson <[email protected]>
*/
`include "common.vh"
module mdio (
input clk,
input ce,
input mdi,
output reg mdo,
output reg mdo_valid,
input ack, err,
output cyc,
output reg stb, we,
output reg [4:0] addr,
output reg [15:0] data_write,
input [15:0] data_read
);
parameter [PHYAD_BITS-1:0] ADDRESS = 0;
localparam IDLE = 0;
localparam PREAMBLE = 1;
localparam ST = 2;
localparam OP = 3;
localparam PHYAD = 4;
localparam REGAD = 5;
localparam TA = 6;
localparam DATA = 7;
localparam PREAMBLE_BITS = 32;
localparam OP_BITS = 2;
localparam PHYAD_BITS = 5;
localparam REGAD_BITS = 5;
localparam TA_BITS = 2;
localparam DATA_BITS = 16;
localparam OP_READ = 2'b10;
localparam OP_WRITE = 2'b01;
reg mdo_next, mdo_valid_next;
reg stb_next, we_next;
initial stb = 0;
reg [4:0] addr_next;
reg [15:0] data_next;
reg bad, bad_next, saved_err, saved_err_next;
reg [2:0] state, state_next;
reg [4:0] state_counter, state_counter_next;
initial state = IDLE;
/*
* NB: stb_next and data_next are assigned to stb and data_write every
* clock, whereas the other signals are only assigned if ce is high.
* This ensures that no duplicate reads/writes are issued, and that
* data_read is sampled promptly. However, it also means that any
* assignments to these regs in the state machine must be qualified
* by ce.
*/
always @(*) begin
mdo_next = 1'bX;
mdo_valid_next = 0;
stb_next = stb;
we_next = we;
addr_next = addr;
data_next = data_write;
saved_err_next = saved_err;
if (stb && (ack || err)) begin
stb_next = 0;
if (err)
saved_err_next = 1;
else
data_next = data_read;
end
state_next = state;
state_counter_next = state_counter - 1;
bad_next = bad;
case (state)
IDLE: begin
bad_next = 0;
state_counter_next = PREAMBLE_BITS - 1;
if (mdi)
state_next = PREAMBLE;
end
PREAMBLE: begin
if (!state_counter)
state_counter_next = 0;
if (!mdi) begin
if (state_counter)
state_next = IDLE;
else
state_next = ST;
end
end
ST: begin
if (!mdi)
bad_next = 1;
state_next = OP;
state_counter_next = OP_BITS - 1;
end
OP: begin
/* This is a bit of an abuse of we :) */
we_next = mdi;
/* Accordingly, cancel any outstanding transactions */
stb_next = 0;
saved_err_next = 0;
if (!state_counter) begin
case ({ we, mdi })
OP_READ: we_next = 0;
OP_WRITE: we_next = 1;
default: bad_next = 1;
endcase
state_next = PHYAD;
state_counter_next = PHYAD_BITS - 1;
end
end
PHYAD: begin
if (mdi != ADDRESS[state_counter])
bad_next = 1;
if (!state_counter) begin
state_next = REGAD;
state_counter_next = REGAD_BITS - 1;
end
end
REGAD: begin
addr_next = { addr[3:0], mdi };
if (!state_counter) begin
if (ce && !we && !bad)
stb_next = 1;
state_next = TA;
state_counter_next = TA_BITS - 1;
end
end
TA: begin
if (!state_counter) begin
if (!we && !bad) begin
mdo_next = 0;
mdo_valid_next = 1;
if (stb || saved_err) begin
/* No response */
if (ce)
stb_next = 0;
bad_next = 1;
mdo_valid_next = 0;
end
end
state_next = DATA;
state_counter_next = DATA_BITS - 1;
end
end
DATA: begin
if (ce && we) begin
data_next = { data_write[14:0], mdi };
end else if (!bad) begin
/* More data_write abuse */
mdo_next = data_write[15];
mdo_valid_next = 1;
if (ce)
data_next = { data_write[14:0], 1'bX };
end
if (!state_counter) begin
if (ce && we && !bad)
stb_next = 1;
bad_next = 0;
state_next = IDLE;
end
end
endcase
end
always @(posedge clk) begin
stb <= stb_next;
data_write <= data_next;
saved_err <= saved_err_next;
if (ce) begin
mdo <= mdo_next;
mdo_valid <= mdo_valid_next;
we <= we_next;
addr <= addr_next;
state <= state_next;
state_counter <= state_counter_next;
bad <= bad_next;
end
end
/* No multi-beat transactions */
assign cyc = stb;
`ifndef SYNTHESIS
reg [255:0] state_text;
always @(*) begin
case (state)
IDLE: state_text = "IDLE";
PREAMBLE: state_text = "PREAMBLE";
ST: state_text = "ST";
OP: state_text = "OP";
PHYAD: state_text = "PHYAD";
REGAD: state_text = "REGAD";
TA: state_text = "TA";
DATA: state_text = "DATA";
endcase
end
`endif
endmodule