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
|
/////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Ettus Research, A National Instruments Company
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_fifo_2clk.v
//
// Purpose:
// An asynchronous clock crossing for AXI-Stream buses
// The width (WIDTH) and depth (SIZE) of the FIFO is configurable
// For depths less than the technology's SRL threshold, an SRL
// will be instantiated. For depths less the minimum RAM block
// depth (that corresponds to the max width), a single BRAM block
// will be instantiated. For other larger depths, a BRAM block
// plus a regular axi_fifo will be instantiated. The depth of the
// combined FIFO in that case will be larger than the user request.
//
// Requirements:
// Implementation for fifo_short_2clk, fifo_4k_2clk that infer SRL
// and BRAM based clock-crossing FIFOs respectively
//
//////////////////////////////////////////////////////////////////////
module axi_fifo_2clk #(
parameter WIDTH = 69, // Width of input/output data word
parameter SIZE = 9, // log2 of the depth of the FIFO
parameter PIPELINE = "NONE", // Which ports to pipeline? {NONE, IN, OUT, INOUT}
parameter DEVICE = "7SERIES" // FPGA technology identifier (for optimal inference)
)(
input wire reset,
input wire i_aclk,
input wire [WIDTH-1:0] i_tdata,
input wire i_tvalid,
output wire i_tready,
input wire o_aclk,
output wire [WIDTH-1:0] o_tdata,
output wire o_tvalid,
input wire o_tready
);
wire i_arst, o_arst;
synchronizer #(.INITIAL_VAL(1'b1)) i_rst_sync_i (
.clk(i_aclk), .rst(1'b0), .in(reset), .out(i_arst)
);
synchronizer #(.INITIAL_VAL(1'b1)) o_rst_sync_i (
.clk(o_aclk), .rst(1'b0), .in(reset), .out(o_arst)
);
//----------------------------------------------
// Pipeline Logic
//----------------------------------------------
wire [WIDTH-1:0] i_pipe_tdata, o_pipe_tdata;
wire i_pipe_tvalid, o_pipe_tvalid;
wire i_pipe_tready, o_pipe_tready;
generate
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH(WIDTH)) in_pipe_i (
.clk(i_aclk), .reset(i_arst), .clear(1'b0),
.i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready),
.o_tdata(i_pipe_tdata), .o_tvalid(i_pipe_tvalid), .o_tready(i_pipe_tready),
.space(), .occupied()
);
end else begin
assign {i_pipe_tdata, i_pipe_tvalid} = {i_tdata, i_tvalid};
assign i_tready = i_pipe_tready;
end
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
axi_fifo_flop2 #(.WIDTH(WIDTH)) out_pipe_i (
.clk(o_aclk), .reset(o_arst), .clear(1'b0),
.i_tdata(o_pipe_tdata), .i_tvalid(o_pipe_tvalid), .i_tready(o_pipe_tready),
.o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
.space(), .occupied()
);
end else begin
assign {o_tdata, o_tvalid} = {o_pipe_tdata, o_pipe_tvalid};
assign o_pipe_tready = o_tready;
end
endgenerate
//----------------------------------------------
// FIFO Logic
//----------------------------------------------
wire [WIDTH-1:0] o_ext_tdata;
wire o_ext_tvalid;
wire o_ext_tready;
// Derive constants based on device.
// First triple of values is for Intel's MAX10 FPGAs. The FIFO generator for
// those devices supports embedded memory only (SRL_THRESHOLD = 0).
// The later triple has been optimized for Xilinx 7Series FPGAs. They also
// work for Spartan6 but may not be optimal.
localparam BASE_WIDTH = (DEVICE == "MAX10") ? 36 : 72;
localparam SRL_THRESHOLD = (DEVICE == "MAX10") ? 0 : 5;
localparam RAM_THRESHOLD = (DEVICE == "MAX10") ? 8 : 9;
// How many parallel FIFOs to instantiate to fit WIDTH
localparam NUM_FIFOS = ((WIDTH-1)/BASE_WIDTH)+1;
localparam INT_WIDTH = BASE_WIDTH * NUM_FIFOS;
wire [INT_WIDTH-1:0] wr_data, rd_data;
wire [NUM_FIFOS-1:0] full, empty;
wire wr_en, rd_en;
// Read/write logic for FIFO sections
assign wr_data = {{(INT_WIDTH-WIDTH){1'b0}}, i_pipe_tdata};
assign wr_en = i_pipe_tready & i_pipe_tvalid;
assign i_pipe_tready = &(~full);
assign o_ext_tdata = rd_data[WIDTH-1:0];
assign o_ext_tvalid = &(~empty);
assign rd_en = o_ext_tready & o_ext_tvalid;
// FIFO IP instantiation
genvar i;
generate
for (i = 0; i < NUM_FIFOS; i = i + 1) begin: fifo_section
if (SIZE <= SRL_THRESHOLD) begin
fifo_short_2clk impl_srl_i (
.rst (i_arst),
.wr_clk (i_aclk),
.din (wr_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]),
.wr_en (wr_en),
.full (full[i]),
.wr_data_count(),
.rd_clk (o_aclk),
.dout (rd_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]),
.rd_en (rd_en),
.empty (empty[i]),
.rd_data_count()
);
end else begin
fifo_4k_2clk impl_bram_i (
.rst (i_arst),
.wr_clk (i_aclk),
.din (wr_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]),
.wr_en (wr_en),
.full (full[i]),
.wr_data_count(),
.rd_clk (o_aclk),
.dout (rd_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]),
.rd_en (rd_en),
.empty (empty[i]),
.rd_data_count()
);
end
end
endgenerate
//----------------------------------------------
// Extension FIFO (for large sizes)
//----------------------------------------------
generate
if (SIZE > RAM_THRESHOLD) begin
wire [WIDTH-1:0] ext_pipe_tdata;
wire ext_pipe_tvalid;
wire ext_pipe_tready;
// Add a register slice between BRAM cascades
axi_fifo_flop2 #(.WIDTH(WIDTH)) ext_fifo_pipe_i (
.clk(o_aclk), .reset(o_arst), .clear(1'b0),
.i_tdata(o_ext_tdata), .i_tvalid(o_ext_tvalid), .i_tready(o_ext_tready),
.o_tdata(ext_pipe_tdata), .o_tvalid(ext_pipe_tvalid), .o_tready(ext_pipe_tready),
.space(), .occupied()
);
// Bolt on an extension FIFO if the requested depth is larger than the BRAM
// 2clk FIFO primitive (IP)
axi_fifo_bram #(.WIDTH(WIDTH), .SIZE(SIZE)) ext_fifo_i (
.clk(o_aclk), .reset(o_arst), .clear(1'b0),
.i_tdata(ext_pipe_tdata), .i_tvalid(ext_pipe_tvalid), .i_tready(ext_pipe_tready),
.o_tdata(o_pipe_tdata), .o_tvalid(o_pipe_tvalid), .o_tready(o_pipe_tready),
.space(), .occupied()
);
end else begin
assign {o_pipe_tdata, o_pipe_tvalid} = {o_ext_tdata, o_ext_tvalid};
assign o_ext_tready = o_pipe_tready;
end
endgenerate
endmodule
|