Repository navigation
Expand file tree
/
Copy pathcc_cdc_2phase_clearable.sv
More file actions
485 lines (428 loc) · 18.8 KB
/
Copy pathcc_cdc_2phase_clearable.sv
File metadata and controls
485 lines (428 loc) · 18.8 KB
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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
// Copyright 2018 ETH Zurich and University of Bologna.
//
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Fabian Schuiki <fschuiki@iis.ee.ethz.ch> (original CDC)
// - Manuel Eggimann <meggimann@iis.ee.ethz.ch> (clearability feature)
// - Philippe Sauter <phsauter@iis.ee.ethz.ch> (source/destination split)
//
// Description: Clearable Two-Phase CDC
// Transfer payloads with a two-phase handshake while coordinating synchronous
// and optional asynchronous one-sided clears across source and destination.
/// A two-phase clock domain crossing.
///
/// CONSTRAINT: Requires max_delay of min_period(src_clk_i, dst_clk_i) through
/// all asynchronous paths between the domains. The payload CDC uses
/// async_payload_req, async_payload_ack, and async_payload_data for the actual
/// data handshake. The clear sequencer CDCs use async_reset_src2dst_req,
/// async_reset_dst2src_ack, async_reset_src2dst_next_phase,
/// async_reset_dst2src_req, async_reset_src2dst_ack, and
/// async_reset_dst2src_next_phase to handshake the clearing state machines.
///
///
/// Reset Behavior:
///
/// In contrast to the cc_cdc_2phase version without clear signal, this module
/// supports one-sided warm resets (asynchronously and synchronously). The way
/// this is implemented is described in more detail in the cc_cdc_reset_ctrlr
/// module. To summarize a synchronous clear request i.e. src/dst_clear_i will
/// cause the respective other clock domain to reset as well without introducing
/// any spurious transactions. This is acomplished by an internal module
/// (cc_cdc_reset_ctrlr) that starts a reset sequence on both sides of the CDC in
/// lock-step that first isolates the CDC from the outside world and then resets
/// it. The reset sequencer provides the following behavior:
/// 1. There are no spurious invalid or duplicated transactions regardless how
/// the individual sides are reset (can also happen roughly simultaneosly)
/// 2. The CDC becomes unready at the src side in the next cycle after
/// synchronous reset request until the reset sequence is completed. A currently
/// pending transactions might still complete (if the dst accepts at the
/// exact time the reset is request on the src die).
/// 3. During the reset sequence the dst might withdraw the valid signal. This
/// might violate higher level protocols. If you need this feature you would
/// have to path the existing implementation to wait with the isolate_ack
/// assertion until all open handshakes were acknowledged.
/// 4. If the parameter ClearOnAsyncReset is enabled, the same behavior as
/// above is also valid for asynchronous resets on either side. However, this
/// increases the minimum number of synchronization stages (SyncStages
/// parameter) from 2 to 3 (read the cc_cdc_reset_ctrlr header to figure out
/// why).
///
///
`include "common_cells/registers.svh"
`include "common_cells/assertions.svh"
module cc_cdc_2phase_clearable #(
parameter type data_t = logic,
parameter int unsigned SyncStages = 3,
parameter bit ClearOnAsyncReset = 1
)(
input logic src_rst_ni,
input logic src_clk_i,
input logic src_clear_i,
output logic src_clear_pending_o,
input data_t src_data_i,
input logic src_valid_i,
output logic src_ready_o,
input logic dst_rst_ni,
input logic dst_clk_i,
input logic dst_clear_i,
output logic dst_clear_pending_o,
output data_t dst_data_o,
output logic dst_valid_o,
input logic dst_ready_i
);
// Asynchronous payload handshake signals between the source and destination.
(* dont_touch = "true" *) logic async_payload_req;
(* dont_touch = "true" *) logic async_payload_ack;
(* dont_touch = "true" *) data_t async_payload_data;
// Asynchronous clear-controller phase handshakes between the domains.
(* dont_touch = "true" *) logic async_reset_src2dst_req;
(* dont_touch = "true" *) logic async_reset_dst2src_ack;
(* dont_touch = "true" *) cc_pkg::cdc_clear_seq_phase_e async_reset_src2dst_next_phase;
(* dont_touch = "true" *) logic async_reset_dst2src_req;
(* dont_touch = "true" *) logic async_reset_src2dst_ack;
(* dont_touch = "true" *) cc_pkg::cdc_clear_seq_phase_e async_reset_dst2src_next_phase;
// Clear/isolate contract:
// - src/dst_clear_pending_o are the local isolate requests.
// - local isolate and clear acknowledgements are one-cycle delayed copies of
// the corresponding local requests.
// - isolate gates the external valid/ready interface on both sides.
// - transactions accepted before or during a clear sequence may complete or
// be dropped, but fresh post-clear traffic must not be duplicated,
// reordered, or corrupted.
if (ClearOnAsyncReset) begin : gen_elaboration_assertion
if (SyncStages < 3)
$error("The clearable 2-phase CDC with async reset",
"synchronization requires at least 3 synchronizer stages for the FIFO.");
end else begin : gen_elaboration_assertion
if (SyncStages < 2) begin : gen_elaboration_assertion
$error("A minimum of 2 synchronizer stages is required for proper functionality.");
end
end
// The source side owns the payload CDC half, local clear/isolate
// acknowledgements, and the local reset-controller half.
cc_cdc_2phase_clearable_src #(
.data_t ( data_t ),
.SyncStages ( SyncStages ),
.ClearOnAsyncReset ( ClearOnAsyncReset )
) i_src (
.src_rst_ni ( src_rst_ni ),
.src_clk_i ( src_clk_i ),
.src_clear_i ( src_clear_i ),
.src_clear_pending_o ( src_clear_pending_o ),
.src_data_i ( src_data_i ),
.src_valid_i ( src_valid_i ),
.src_ready_o ( src_ready_o ),
(* async *) .async_payload_req_o ( async_payload_req ),
(* async *) .async_payload_ack_i ( async_payload_ack ),
(* async *) .async_payload_data_o ( async_payload_data ),
(* async *) .async_reset_next_phase_o ( async_reset_src2dst_next_phase ),
(* async *) .async_reset_req_o ( async_reset_src2dst_req ),
(* async *) .async_reset_ack_i ( async_reset_dst2src_ack ),
(* async *) .async_reset_next_phase_i ( async_reset_dst2src_next_phase ),
(* async *) .async_reset_req_i ( async_reset_dst2src_req ),
(* async *) .async_reset_ack_o ( async_reset_src2dst_ack )
);
// The destination side owns the payload CDC half, local
// clear/isolate acknowledgements, and the local reset-controller half.
cc_cdc_2phase_clearable_dst #(
.data_t ( data_t ),
.SyncStages ( SyncStages ),
.ClearOnAsyncReset ( ClearOnAsyncReset )
) i_dst (
.dst_rst_ni ( dst_rst_ni ),
.dst_clk_i ( dst_clk_i ),
.dst_clear_i ( dst_clear_i ),
.dst_clear_pending_o ( dst_clear_pending_o ),
.dst_data_o ( dst_data_o ),
.dst_valid_o ( dst_valid_o ),
.dst_ready_i ( dst_ready_i ),
(* async *) .async_payload_req_i ( async_payload_req ),
(* async *) .async_payload_ack_o ( async_payload_ack ),
(* async *) .async_payload_data_i ( async_payload_data ),
(* async *) .async_reset_next_phase_o ( async_reset_dst2src_next_phase ),
(* async *) .async_reset_req_o ( async_reset_dst2src_req ),
(* async *) .async_reset_ack_i ( async_reset_src2dst_ack ),
(* async *) .async_reset_next_phase_i ( async_reset_src2dst_next_phase ),
(* async *) .async_reset_req_i ( async_reset_src2dst_req ),
(* async *) .async_reset_ack_o ( async_reset_dst2src_ack )
);
`ifndef COMMON_CELLS_ASSERTS_OFF
`ASSERT(no_valid_i_during_clear_i, src_clear_i |-> !src_valid_i, src_clk_i, !src_rst_ni)
`endif
endmodule
/// Destination side for the clearable two-phase CDC.
module cc_cdc_2phase_clearable_dst #(
parameter type data_t = logic,
parameter int unsigned SyncStages = 2,
parameter bit ClearOnAsyncReset = 1
) (
input logic dst_rst_ni,
input logic dst_clk_i,
input logic dst_clear_i,
output logic dst_clear_pending_o,
output data_t dst_data_o,
output logic dst_valid_o,
input logic dst_ready_i,
input logic async_payload_req_i,
output logic async_payload_ack_o,
input data_t async_payload_data_i,
output cc_pkg::cdc_clear_seq_phase_e async_reset_next_phase_o,
output logic async_reset_req_o,
input logic async_reset_ack_i,
input cc_pkg::cdc_clear_seq_phase_e async_reset_next_phase_i,
input logic async_reset_req_i,
output logic async_reset_ack_o
);
logic dst_clear_req;
logic dst_clear_ack_q;
logic dst_isolate_req;
logic dst_isolate_ack_q;
logic dst_valid;
if (ClearOnAsyncReset) begin : gen_elaboration_assertion
if (SyncStages < 3)
$error("The clearable 2-phase CDC with async reset",
"synchronization requires at least 3 synchronizer stages for the FIFO.");
end else begin : gen_elaboration_assertion
if (SyncStages < 2) begin : gen_sync_stage_assertion
$error("A minimum of 2 synchronizer stages is required for proper functionality.");
end
end
// Keep the clear-control path one synchronizer stage ahead of the payload CDC
// when async reset propagation is enabled, but never below two stages.
localparam int unsigned ResetSyncStages = (SyncStages > 2) ? SyncStages - 1 : 2;
cc_cdc_reset_ctrlr_half #(
.SyncStages ( ResetSyncStages ),
.ClearOnAsyncReset ( ClearOnAsyncReset )
) i_cdc_reset_ctrlr_half (
.clk_i ( dst_clk_i ),
.rst_ni ( dst_rst_ni ),
.clear_i ( dst_clear_i ),
.clear_o ( dst_clear_req ),
.clear_ack_i ( dst_clear_ack_q ),
.isolate_o ( dst_isolate_req ),
.isolate_ack_i ( dst_isolate_ack_q ),
(* async *) .async_next_phase_o ( async_reset_next_phase_o ),
(* async *) .async_req_o ( async_reset_req_o ),
(* async *) .async_ack_i ( async_reset_ack_i ),
(* async *) .async_next_phase_i ( async_reset_next_phase_i ),
(* async *) .async_req_i ( async_reset_req_i ),
(* async *) .async_ack_o ( async_reset_ack_o )
);
cc_cdc_2phase_dst_clearable #(
.data_t ( data_t ),
.SyncStages ( SyncStages )
) i_dst (
.rst_ni ( dst_rst_ni ),
.clk_i ( dst_clk_i ),
.clear_i ( dst_clear_req ),
.data_o ( dst_data_o ),
.valid_o ( dst_valid ),
.ready_i ( dst_ready_i & !dst_isolate_req ),
(* async *) .async_req_i ( async_payload_req_i ),
(* async *) .async_ack_o ( async_payload_ack_o ),
(* async *) .async_data_i ( async_payload_data_i )
);
assign dst_valid_o = dst_valid & !dst_isolate_req;
// Isolation and clear are acknowledged one cycle after the local request.
`FF(dst_isolate_ack_q, dst_isolate_req, 1'b0, dst_clk_i, dst_rst_ni)
`FF(dst_clear_ack_q, dst_clear_req, 1'b0, dst_clk_i, dst_rst_ni)
assign dst_clear_pending_o = dst_isolate_req;
endmodule
/// Source side for the clearable two-phase CDC.
module cc_cdc_2phase_clearable_src #(
parameter type data_t = logic,
parameter int unsigned SyncStages = 2,
parameter bit ClearOnAsyncReset = 1
) (
input logic src_rst_ni,
input logic src_clk_i,
input logic src_clear_i,
output logic src_clear_pending_o,
input data_t src_data_i,
input logic src_valid_i,
output logic src_ready_o,
output logic async_payload_req_o,
input logic async_payload_ack_i,
output data_t async_payload_data_o,
output cc_pkg::cdc_clear_seq_phase_e async_reset_next_phase_o,
output logic async_reset_req_o,
input logic async_reset_ack_i,
input cc_pkg::cdc_clear_seq_phase_e async_reset_next_phase_i,
input logic async_reset_req_i,
output logic async_reset_ack_o
);
logic src_clear_req;
logic src_clear_ack_q;
logic src_ready;
logic src_isolate_req;
logic src_isolate_ack_q;
if (ClearOnAsyncReset) begin : gen_elaboration_assertion
if (SyncStages < 3)
$error("The clearable 2-phase CDC with async reset",
"synchronization requires at least 3 synchronizer stages for the FIFO.");
end else begin : gen_elaboration_assertion
if (SyncStages < 2) begin : gen_sync_stage_assertion
$error("A minimum of 2 synchronizer stages is required for proper functionality.");
end
end
// Keep the clear-control path one synchronizer stage ahead of the payload CDC
// when async reset propagation is enabled, but never below two stages.
localparam int unsigned ResetSyncStages = (SyncStages > 2) ? SyncStages - 1 : 2;
cc_cdc_reset_ctrlr_half #(
.SyncStages ( ResetSyncStages ),
.ClearOnAsyncReset ( ClearOnAsyncReset )
) i_cdc_reset_ctrlr_half (
.clk_i ( src_clk_i ),
.rst_ni ( src_rst_ni ),
.clear_i ( src_clear_i ),
.clear_o ( src_clear_req ),
.clear_ack_i ( src_clear_ack_q ),
.isolate_o ( src_isolate_req ),
.isolate_ack_i ( src_isolate_ack_q ),
(* async *) .async_next_phase_o ( async_reset_next_phase_o ),
(* async *) .async_req_o ( async_reset_req_o ),
(* async *) .async_ack_i ( async_reset_ack_i ),
(* async *) .async_next_phase_i ( async_reset_next_phase_i ),
(* async *) .async_req_i ( async_reset_req_i ),
(* async *) .async_ack_o ( async_reset_ack_o )
);
cc_cdc_2phase_src_clearable #(
.data_t ( data_t ),
.SyncStages ( SyncStages )
) i_src (
.rst_ni ( src_rst_ni ),
.clk_i ( src_clk_i ),
.clear_i ( src_clear_req ),
.data_i ( src_data_i ),
.valid_i ( src_valid_i & !src_isolate_req ),
.ready_o ( src_ready ),
(* async *) .async_req_o ( async_payload_req_o ),
(* async *) .async_ack_i ( async_payload_ack_i ),
(* async *) .async_data_o ( async_payload_data_o )
);
assign src_ready_o = src_ready & !src_isolate_req;
// Isolation and clear are acknowledged one cycle after the local request.
`FF(src_isolate_ack_q, src_isolate_req, 1'b0, src_clk_i, src_rst_ni)
`FF(src_clear_ack_q, src_clear_req, 1'b0, src_clk_i, src_rst_ni)
assign src_clear_pending_o = src_isolate_req; // The isolate signal stays
// asserted during the whole
// clear sequence.
endmodule
/// Half of the two-phase clock domain crossing located in the source domain.
module cc_cdc_2phase_src_clearable #(
parameter type data_t = logic,
parameter int unsigned SyncStages = 2
) (
input logic rst_ni,
input logic clk_i,
input logic clear_i,
input data_t data_i,
input logic valid_i,
output logic ready_o,
output logic async_req_o,
input logic async_ack_i,
output data_t async_data_o
);
(* dont_touch = "true" *)
logic req_src_d, req_src_q, ack_synced;
(* dont_touch = "true" *)
data_t data_src_d, data_src_q;
// Synchronize the async ACK
tc_sync #(
.Stages(SyncStages)
) i_sync(
.clk_i,
.rst_ni,
.serial_i( async_ack_i ),
.serial_o( ack_synced )
);
// If we receive the clear signal clear the content of the request flip-flop
// and the data register
always_comb begin
data_src_d = data_src_q;
req_src_d = req_src_q;
if (clear_i) begin
req_src_d = 1'b0;
// The req_src and data_src registers change when a new data item is accepted.
end else if (valid_i && ready_o) begin
req_src_d = ~req_src_q;
data_src_d = data_i;
end
end
`FFNR(data_src_q, data_src_d, clk_i)
`FF(req_src_q, req_src_d, 1'b0, clk_i, rst_ni)
// Output assignments.
assign ready_o = (req_src_q == ack_synced);
assign async_req_o = req_src_q;
assign async_data_o = data_src_q;
// Assertions
`ifndef COMMON_CELLS_ASSERTS_OFF
`ASSUME(no_clear_and_request, clear_i |-> ~valid_i, clk_i, !rst_ni,
"No request allowed while clear_i is asserted.")
`endif
endmodule
/// Half of the two-phase clock domain crossing located in the destination
/// domain.
module cc_cdc_2phase_dst_clearable #(
parameter type data_t = logic,
parameter int unsigned SyncStages = 2
)(
input logic rst_ni,
input logic clk_i,
input logic clear_i,
output data_t data_o,
output logic valid_o,
input logic ready_i,
input logic async_req_i,
output logic async_ack_o,
input data_t async_data_i
);
(* dont_touch = "true" *)
(* async_reg = "true" *)
logic ack_dst_d, ack_dst_q, req_synced, req_synced_q1;
(* dont_touch = "true" *)
data_t data_dst_d, data_dst_q;
//Synchronize the request
tc_sync #(
.Stages(SyncStages)
) i_sync(
.clk_i,
.rst_ni,
.serial_i( async_req_i ),
.serial_o( req_synced )
);
// The ack_dst register changes when a new data item is accepted.
always_comb begin
ack_dst_d = ack_dst_q;
if (clear_i) begin
ack_dst_d = 1'b0;
end else if (valid_o && ready_i) begin
ack_dst_d = ~ack_dst_q;
end
end
// The data_dst register samples when a new data item is presented. This is
// indicated by a transition in the req_synced line.
always_comb begin
data_dst_d = data_dst_q;
if (req_synced != req_synced_q1 && !valid_o) begin
data_dst_d = async_data_i;
end
end
`FFNR(data_dst_q, data_dst_d, clk_i)
`FF(ack_dst_q, ack_dst_d, 1'b0, clk_i, rst_ni)
// The req_synced_q1 is the delayed version of the synchronized req_synced
// used to detect transitions in the request.
`FF(req_synced_q1, req_synced, 1'b0, clk_i, rst_ni)
// Output assignments.
assign valid_o = (ack_dst_q != req_synced_q1);
assign data_o = data_dst_q;
assign async_ack_o = ack_dst_q;
endmodule