aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/fifo/axi_fifo_2clk.v
blob: a1af5ee8efb2cb77262c1c370e9c57b052875f15 (plain)
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