-
Notifications
You must be signed in to change notification settings - Fork 195
Expand file tree
/
Copy pathcdc_fifo_2phase.sv
More file actions
157 lines (140 loc) · 5.88 KB
/
cdc_fifo_2phase.sv
File metadata and controls
157 lines (140 loc) · 5.88 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
// 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.
//
// Fabian Schuiki <[email protected]>
`include "common_cells/assertions.svh"
/// A clock domain crossing FIFO, using 2-phase hand shakes.
///
/// This FIFO has its push and pop ports in two separate clock domains. Its size
/// can only be powers of two, which is why its depth is given as 2**LOG_DEPTH.
/// LOG_DEPTH must be at least 1.
///
/// # Reset Behavior!!
///
/// This module must not be used if warm reset capabily is a requirement. The
/// only execption is if you consistently use a reset controller that sequences
/// the resets while gating both clock domains (be very careful if you follow
/// this strategy!).
///
/// After this disclaimer, here is how you connect the src_rst_ni and the
/// dst_rst_ni of this module for power-on-reset (POR). The src_rst_ni and
/// dst_rst_ni signal must be asserted SIMULTANEOUSLY (i.e. asynchronous
/// assertion). Othwerwise, spurious transactions could occur in the domain
/// where the reset arrives later than the other. The de-assertion of both reset
/// must be synchronized to their respective clock domain (i.e. src_rst_ni must
/// be deasserted synchronously to the src_clk_i and dst_rst_ni must be
/// deasserted synchronously to dst_clk_i.) You can use the rstgen cell in the
/// common_cells library to achieve this (synchronization of only the
/// de-assertion). However, be careful about reset domain crossings; If you
/// reset both domain asynchronously in their entirety (i.e. POR) you are fine.
/// However, if you use this strategy for warm resets (some parts of the circuit
/// are not reset) you might introduce metastability in this separate
/// reset-domain when you assert the reset (the deassertion synchronizer doen't
/// help here).
///
/// CONSTRAINT: See the constraints for `cdc_2phase`. An additional maximum
/// delay path needs to be specified from fifo_data_q to dst_data_o.
module cdc_fifo_2phase #(
/// The data type of the payload transported by the FIFO.
parameter type T = logic,
/// The FIFO's depth given as 2**LOG_DEPTH.
parameter int LOG_DEPTH = 3
)(
input logic src_rst_ni,
input logic src_clk_i,
input T src_data_i,
input logic src_valid_i,
output logic src_ready_o,
input logic dst_rst_ni,
input logic dst_clk_i,
output T dst_data_o,
output logic dst_valid_o,
input logic dst_ready_i
);
// Check the invariants.
`ifndef COMMON_CELLS_ASSERTS_OFF
`ASSERT_INIT(log_depth_0, LOG_DEPTH > 0)
`endif
localparam int PtrWidth = LOG_DEPTH+1;
typedef logic [PtrWidth-1:0] pointer_t;
typedef logic [LOG_DEPTH-1:0] index_t;
localparam pointer_t PtrFull = (1 << LOG_DEPTH);
localparam pointer_t PtrEmpty = '0;
// Allocate the registers for the FIFO memory with its separate write and read
// ports. The FIFO has the following ports:
//
// - write: fifo_widx, fifo_wdata, fifo_write, src_clk_i
// - read: fifo_ridx, fifo_rdata
index_t fifo_widx, fifo_ridx;
logic fifo_write;
T fifo_wdata, fifo_rdata;
T fifo_data_q [2**LOG_DEPTH];
assign fifo_rdata = fifo_data_q[fifo_ridx];
for (genvar i = 0; i < 2**LOG_DEPTH; i++) begin : g_word
always_ff @(posedge src_clk_i, negedge src_rst_ni) begin
if (!src_rst_ni)
fifo_data_q[i] <= T'('0);
else if (fifo_write && fifo_widx == i)
fifo_data_q[i] <= fifo_wdata;
end
end
// Allocate the read and write pointers in the source and destination domain.
pointer_t src_wptr_q, dst_wptr, src_rptr, dst_rptr_q;
always_ff @(posedge src_clk_i, negedge src_rst_ni) begin
if (!src_rst_ni)
src_wptr_q <= 0;
else if (src_valid_i && src_ready_o)
src_wptr_q <= src_wptr_q + 1;
end
always_ff @(posedge dst_clk_i, negedge dst_rst_ni) begin
if (!dst_rst_ni)
dst_rptr_q <= 0;
else if (dst_valid_o && dst_ready_i)
dst_rptr_q <= dst_rptr_q + 1;
end
// The pointers into the FIFO are one bit wider than the actual address into
// the FIFO. This makes detecting critical states very simple: if all but the
// topmost bit of rptr and wptr agree, the FIFO is in a critical state. If the
// topmost bit is equal, the FIFO is empty, otherwise it is full.
assign src_ready_o = ((src_wptr_q ^ src_rptr) != PtrFull);
assign dst_valid_o = ((dst_rptr_q ^ dst_wptr) != PtrEmpty);
// Transport the read and write pointers across the clock domain boundary.
cdc_2phase #( .T(pointer_t) ) i_cdc_wptr (
.src_rst_ni ( src_rst_ni ),
.src_clk_i ( src_clk_i ),
.src_data_i ( src_wptr_q ),
.src_valid_i ( 1'b1 ),
.src_ready_o ( ),
.dst_rst_ni ( dst_rst_ni ),
.dst_clk_i ( dst_clk_i ),
.dst_data_o ( dst_wptr ),
.dst_valid_o ( ),
.dst_ready_i ( 1'b1 )
);
cdc_2phase #( .T(pointer_t) ) i_cdc_rptr (
.src_rst_ni ( dst_rst_ni ),
.src_clk_i ( dst_clk_i ),
.src_data_i ( dst_rptr_q ),
.src_valid_i ( 1'b1 ),
.src_ready_o ( ),
.dst_rst_ni ( src_rst_ni ),
.dst_clk_i ( src_clk_i ),
.dst_data_o ( src_rptr ),
.dst_valid_o ( ),
.dst_ready_i ( 1'b1 )
);
// Drive the FIFO write and read ports.
assign fifo_widx = src_wptr_q;
assign fifo_wdata = src_data_i;
assign fifo_write = src_valid_i && src_ready_o;
assign fifo_ridx = dst_rptr_q;
assign dst_data_o = fifo_rdata;
endmodule