-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpcs_rx.v
408 lines (373 loc) · 9.46 KB
/
pcs_rx.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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// SPDX-License-Identifier: AGPL-3.0-Only OR CERN-OHL-S-2.0
/*
* Copyright (C) 2022 Sean Anderson <[email protected]>
*/
`include "common.vh"
`include "pcs.vh"
`timescale 1ns/1ns
module pcs_rx_bits (
input clk,
/*
* Whether to start a new frame using the last value of @unaligned
* (instead of @aligned). This will adjust the alignment of @aligned.
* Should be a combinatorial input.
*/
input start,
/*
* Fill the input buffer with 1s. This will take effect the cycle
* after it is asserted. It is possible that an overlapping R/J will
* not be detected, but any legal (non-overlapping) R/J will be
* detected properly. Should be a combinatorial input.
*/
input flush,
/*
* The input bits from the PMA. The @bits[1] should be the
* oldest bit. If only one bit is valid, then @bits[1] will be
* considered valid. There cannot be more than two valid bits in one
* cycle.
*/
input [1:0] bits, bits_valid,
/*
* Whether there was activity detected, as defined by 24.2.4.4.1. When
* this signal is asserted, then @unaligned contains valid code groups
* (such as /I/J/).
*/
output reg activity,
/*
* Whether there are at least 10 1s in the input buffer, aligned
* or unaligned. This signal may be used to detect the end of a carrier
* event, as defined by 24.2.4.4.2.
*/
output reg idle,
/*
* Whether @aligned contains valid code groups. This signal will be
* asserted (on average) every 5 clock cycles, and can be used as
* a clock enable.
*/
output reg indicate,
/*
* The output bits from the alignment process. Despite the name, both
* code groups are aligned. @unaligned assumes that we are not
* receiving and tries to detect a new start of stream. @aligned
* assumes that we are receiving and bases the alignment of its code
* group off of a previous start of stream.
*/
output reg [9:0] aligned, unaligned
);
reg activity_next, idle_next, indicate_next;
reg [9:0] aligned_next, unaligned_next;
/* A shift buffer containing the previous values of @bits. */
reg [9:0] buffer, buffer_next;
initial buffer = { `CODE_I, `CODE_I };
/*
* The buffer combined with the new bits (e.g. the total set of bits we
* have to work with)
*/
wire [11:0] raw_bits = { buffer, bits };
/* buffer_next before being shifted by bits_valid */
reg [11:0] buffer_next_raw;
/*
* The number of bits left to receive for the current code group.
* A value of 0 (or 1 if @bits_valid is 2) indicates that the current
* code group will be finished this cycle, and that @indicate_next will be
* set.
*/
reg [2:0] bits_remaining, bits_remaining_next;
initial bits_remaining = 4;
/*
* Whether the last unaligned code group had an "extra" valid bit. If
* this was the case, then the buffer will already contain an extra
* valid bit of the next code group.
*/
reg extra, extra_next;
/* Detect an IJ pair (or a false carrier) */
function start_ij(input [9:0] bits);
start_ij = !(&bits[9:2]) && !bits[0];
endfunction
always @(*) begin
idle_next = idle;
if (bits_valid != 0)
idle_next = &raw_bits[10:1];
if (bits_valid & 2)
idle_next = idle_next || &raw_bits[9:0];
buffer_next_raw = raw_bits;
if (flush)
buffer_next_raw = { 9'h1FF, extra ? buffer[0] : 1'b1, bits };
/* buffer_next = buffer_next_raw << bits_valid */
if (bits_valid == 0)
buffer_next = buffer_next_raw[11:2];
else if (bits_valid == 1)
buffer_next = buffer_next_raw[10:1];
else
buffer_next = buffer_next_raw[9:0];
/* bits_remaining_next = (bits_remaining - bits_valid) % 5 */
if (bits_valid > bits_remaining)
bits_remaining_next = 5 + bits_remaining - bits_valid;
else
bits_remaining_next = bits_remaining - bits_valid;
if (start)
bits_remaining_next = 4 - bits_valid - extra;
/* indicate = bits_remaining < bits_remaining_next */
indicate_next = 0;
if (bits_valid != 0)
indicate_next = bits_remaining == 0;
if (bits_valid & 2)
indicate_next = indicate_next || bits_remaining == 1;
/*
* If we are re-aligning, then indicate will not be valid
* (since it is using the old alignment). There should always
* be at least 3 clock cycles between indicates, so it's safe
* to just ignore it.
*/
if (start)
indicate_next = 0;
aligned_next = raw_bits[10:1];
if (bits_valid & 2 && bits_remaining & 1)
aligned_next = raw_bits[9:0];
activity_next = 0;
extra_next = 0;
unaligned_next = raw_bits[10:1];
if (bits_valid == 1) begin
activity_next = start_ij(raw_bits[10:1]);
end else if (bits_valid & 2) begin
if (start_ij(raw_bits[10:1])) begin
activity_next = 1;
extra_next = 1;
end else if (start_ij(raw_bits[9:0])) begin
activity_next = 1;
unaligned_next = raw_bits[9:0];
end
end
/*
* If we are flushing then activity is based on stale data.
* Ignore it so we don't accidentally detect activity for data
* we are going to flush anyway.
*/
if (flush)
activity_next = 0;
end
always @(posedge clk) begin
buffer <= buffer_next;
bits_remaining <= bits_remaining_next;
extra <= extra_next;
activity <= activity_next;
idle <= idle_next;
indicate <= indicate_next;
aligned <= aligned_next;
unaligned <= unaligned_next;
end
endmodule
/* Receive process */
module pcs_rx (
/* MII */
input clk,
output reg ce,
output reg valid,
output reg [3:0] data,
output reg err,
/* PMA */
input [1:0] bits,
input [1:0] bits_valid,
input link_status,
/* Internal */
output reg rx,
output reg false_carrier
);
localparam IDLE = 0;
localparam START_J = 1;
localparam START_K = 2;
localparam BAD_SSD = 3;
localparam DATA = 4;
localparam PREMATURE = 5;
localparam FAILED = 6;
reg start, flush;
wire activity, idle, indicate;
wire [9:0] aligned, unaligned;
reg [3:0] data_next;
reg ce_next, valid_next, err_next;
reg [2:0] state, state_next;
initial state = IDLE;
/* Whether we are aligned and receiving */
reg rx_next, false_carrier_next;
pcs_rx_bits rx_bits (
.clk(clk),
.start(start),
.flush(flush),
.bits(bits),
.bits_valid(bits_valid),
.activity(activity),
.idle(idle),
.indicate(indicate),
.aligned(aligned),
.unaligned(unaligned)
);
always @(*) begin
case (aligned[9:5])
`CODE_0: data_next = 4'h0;
`CODE_1: data_next = 4'h1;
`CODE_2: data_next = 4'h2;
`CODE_3: data_next = 4'h3;
`CODE_4: data_next = 4'h4;
`CODE_5: data_next = 4'h5;
`CODE_6: data_next = 4'h6;
`CODE_7: data_next = 4'h7;
`CODE_8: data_next = 4'h8;
`CODE_9: data_next = 4'h9;
`CODE_A: data_next = 4'hA;
`CODE_B: data_next = 4'hB;
`CODE_C: data_next = 4'hC;
`CODE_D: data_next = 4'hD;
`CODE_E: data_next = 4'hE;
`CODE_F: data_next = 4'hF;
`CODE_J: data_next = 4'h5;
`CODE_K: data_next = 4'h5;
/* This doesn't do anything :( */
default: data_next = 4'hX;
endcase
start = 0;
/*
* XXX: flush (unlike everything else here) is combinatorial;
* we should only flush if we are actually evaluating the
* state.
*/
flush = 0;
rx_next = rx;
ce_next = indicate;
state_next = state;
valid_next = valid;
err_next = 0;
false_carrier_next = 0;
`define BAD_SSD begin \
state_next = BAD_SSD; \
data_next = 4'b1110; \
err_next = 1; \
end
case (state)
/* These two states evaluate continuously */
IDLE: begin
rx_next = 0;
valid_next = 0;
if (activity) begin
start = 1;
rx_next = 1;
ce_next = 0;
if (unaligned == { `CODE_I, `CODE_J })
state_next = START_J;
else begin
`BAD_SSD;
false_carrier_next = 1;
end
end
end
BAD_SSD: begin
`BAD_SSD;
if (idle)
state_next = IDLE;
end
/* These states transition only on indicate */
START_J: begin
if (aligned[4:0] == `CODE_K) begin
state_next = START_K;
valid_next = 1;
end else begin
`BAD_SSD;
false_carrier_next = indicate;
end
if (!indicate)
state_next = START_J;
end
START_K: begin
if (indicate)
state_next = DATA;
end
DATA: begin
case (aligned[9:5])
`CODE_0,
`CODE_1,
`CODE_2,
`CODE_3,
`CODE_4,
`CODE_5,
`CODE_6,
`CODE_7,
`CODE_8,
`CODE_9,
`CODE_A,
`CODE_B,
`CODE_C,
`CODE_D,
`CODE_E,
`CODE_F:
;
`CODE_T:
if (aligned[4:0] == `CODE_R) begin
if (indicate)
flush = 1;
state_next = IDLE;
valid_next = 0;
end else begin
err_next = 1;
end
`CODE_I: begin
err_next = 1;
if (aligned[4:0] == `CODE_I)
state_next = PREMATURE;
end
default:
err_next = 1;
endcase
if (!indicate)
state_next = DATA;
end
PREMATURE: begin
valid_next = 0;
if (indicate)
state_next = IDLE;
end
FAILED: begin
err_next = 1;
rx_next = 0;
if (indicate)
state_next = IDLE;
end
endcase
if (!link_status) begin
flush = 1;
if (indicate && valid_next) begin
state_next = FAILED;
err_next = 1;
end else begin
state_next = IDLE;
end
end
end
always @(posedge clk) begin
rx <= rx_next;
state <= state_next;
ce <= ce_next;
false_carrier <= false_carrier_next;
if (ce_next) begin
data <= data_next;
valid <= valid_next;
err <= err_next;
end
end
`ifndef SYNTHESIS
wire [4:0] aligned_hi = aligned[9:5];
wire [4:0] aligned_lo = aligned[4:0];
wire [4:0] unaligned_hi = unaligned[9:5];
wire [4:0] unaligned_lo = unaligned[4:0];
reg [255:0] state_text;
always @(*) begin
case (state)
IDLE: state_text = "IDLE";
START_J: state_text = "START_J";
START_K: state_text = "START_K";
BAD_SSD: state_text = "BAD_SSD";
DATA: state_text = "DATA";
PREMATURE: state_text = "PREMATURE";
FAILED: state_text = "FAILED";
endcase
end
`endif
endmodule