File: fft_post_processing.sv

package info (click to toggle)
uhd 4.8.0.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 183,172 kB
  • sloc: cpp: 279,415; python: 109,850; ansic: 103,348; vhdl: 57,230; tcl: 20,007; xml: 8,581; makefile: 2,863; sh: 2,797; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (378 lines) | stat: -rw-r--r-- 13,119 bytes parent folder | download
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
//
// Copyright 2024 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: fft_post_processing
//
// Description:
//
//   This module contains the optional post-processing stages of an FFT,
//   including FFT output reordering, magnitude, and magnitude-squared
//   calculations.
//
//   For the magnitude, the result is clipped to a signed 16-bit result in the
//   range [0, 0x7FFF]. The result is placed in the real part of the sc16
//   output (the upper 16 bits) and the imaginary part (the lower 16 bits) is
//   set to 0.
//
//   For the magnitude squared, it computes (i^2 + q^2) / 0x8000, rounding and
//   clipping the result to a signed 16-bit value in the range [0, 0x7FFF]. The
//   division helps to avoid saturation and to put the result in a useful
//   range. The result is placed in the real part of the sc16 output (the upper
//   16 bits) and the imaginary part (the lower 16 bits) is set to 0.
//
//   Note that the I and Q may be swapped in the RFNoC transport adapter, so
//   this order will likely be reversed by the time it makes it back to a host
//   computer.
//
// Parameters:
//
//   EN_FFT_ORDER      : Set to 1 to add the optional FFT reorder core. Set to
//                       0 to remove it and save resources.
//   EN_CP_INSERTION   : Controls whether to include the cyclic prefix
//                       insertion logic, which is a subset of EN_FFT_ORDER.
//   EN_MAGNITUDE      : Set to 1 to add the magnitude output calculation core.
//                       Set to 0 to remove it and save resources.
//   EN_MAGNITUDE_SQ   : Set to 1 to add the magnitude squared output
//                       calculation core. Set to 0 to remove it and save
//                       resources.
//   USE_APPROX_MAG    : Controls which magnitude calculation to use. Set to 1
//                       to use a simpler circuit that gives pretty good
//                       results in order to save resources. Set to 0 to use
//                       the CORDIC IP to calculate the magnitude.
//   MAX_FFT_SIZE_LOG2 : Set to the log base 2 of the maximum FFT size to be
//                       supported. For example, a value of 14 means the
//                       maximum FFT size is 2**14 = 4096.
//
// Signals:
//
//   fft_order_sel : 0 - Normal (0 Hz in the center)
//                   1 - Reverse (same as normal but in reverse)
//                   2 - Natural (0 Hz on the left)
//   magnitude_sel : 0 - Normal complex output (No magnitude calculation)
//                   1 - Magnitude output
//                   2 - Magnitude-squared output
//   fft_size_log2 : Log base-2 of the FFT size. That is, the FFT size is
//                   exactly 2**fft_size_log2. The packet size must match.
//   s_axis_*      : AXI-Stream data input. s_axis_tuser contains the cyclic
//                   prefix length and must be valid on the first transfer of
//                   the packet.

//   m_axis_*      : AXI-Stream data output

`default_nettype none


module fft_post_processing #(
  bit EN_FFT_ORDER        = 1,
  bit EN_CP_INSERTION     = 1,
  bit EN_MAGNITUDE        = 1,
  bit EN_MAGNITUDE_SQ     = 1,
  bit USE_APPROX_MAG      = 1,
  int MAX_FFT_SIZE_LOG2   = 12,

  localparam int FFT_SIZE_LOG2_W = $clog2(MAX_FFT_SIZE_LOG2+1),
  localparam int CP_LEN_W = MAX_FFT_SIZE_LOG2
) (
  input wire clk,
  input wire rst,

  input wire [1:0] fft_order_sel,
  input wire [1:0] magnitude_sel,

  input wire [FFT_SIZE_LOG2_W-1:0] fft_size_log2,

  input  wire [        31:0] s_axis_tdata,
  input  wire [CP_LEN_W-1:0] s_axis_tuser,
  input  wire                s_axis_tlast,
  input  wire                s_axis_tvalid,
  output wire                s_axis_tready,

  output wire [31:0] m_axis_tdata,
  output wire        m_axis_tlast,
  output wire        m_axis_tvalid,
  input  wire        m_axis_tready
);

  //---------------------------------------------------------------------------
  // FFT Reorder
  //---------------------------------------------------------------------------

  import fft_reorder_pkg::*;

  wire [31:0] reorder_tdata;
  wire        reorder_tlast;
  wire        reorder_tvalid;
  wire        reorder_tready;

  if (EN_FFT_ORDER) begin : gen_fft_reorder
    logic [1:0]                 old_fft_order_sel;
    logic [FFT_SIZE_LOG2_W-1:0] old_fft_size_log2;
    logic                       fft_cfg_wr;

    // Update the FFT config whenever it changes
    always_ff @(posedge clk) begin
      fft_cfg_wr <= 0;
      if (
        (old_fft_order_sel != fft_order_sel) ||
        (old_fft_size_log2 != fft_size_log2)
      ) begin
        fft_cfg_wr <= 1;
      end
      old_fft_order_sel <= fft_order_sel;
      old_fft_size_log2 <= fft_size_log2;
    end

    fft_reorder #(
      .INPUT_ORDER     (BIT_REVERSE),
      .MAX_FFT_LEN_LOG2(MAX_FFT_SIZE_LOG2),
      .DATA_W          (32),
      .EN_CP_INSERTION (EN_CP_INSERTION)
    ) fft_reorder_i (
      .clk          (clk),
      .rst          (rst),
      .fft_cfg_wr   (fft_cfg_wr),
      .fft_len_log2 (fft_size_log2),
      .fft_out_order(fft_order_t'(fft_order_sel)),
      .i_tdata      (s_axis_tdata),
      .i_tuser      (s_axis_tuser),
      .i_tlast      (s_axis_tlast),
      .i_tvalid     (s_axis_tvalid),
      .i_tready     (s_axis_tready),
      .o_tdata      (reorder_tdata),
      .o_tlast      (reorder_tlast),
      .o_tvalid     (reorder_tvalid),
      .o_tready     (reorder_tready)
    );
  end else begin : gen_no_fft_reorder
    // Pass the data directly through when reordering is disabled.
    assign reorder_tdata  = s_axis_tdata;
    assign reorder_tlast  = s_axis_tlast;
    assign reorder_tvalid = s_axis_tvalid;
    assign s_axis_tready  = reorder_tready;
  end

  //---------------------------------------------------------------------------
  // Demultiplex Magnitude Options
  //---------------------------------------------------------------------------

  wire [31:0] mag_bypass_tdata;
  wire        mag_bypass_tlast;
  wire        mag_bypass_tvalid;
  wire        mag_bypass_tready;

  wire [31:0] mag_in_tdata;
  wire        mag_in_tlast;
  wire        mag_in_tvalid;
  wire        mag_in_tready;

  wire [31:0] mag_sq_in_tdata;
  wire        mag_sq_in_tlast;
  wire        mag_sq_in_tvalid;
  wire        mag_sq_in_tready;

  if (EN_MAGNITUDE || EN_MAGNITUDE_SQ) begin : gen_mag_demux
    axi_demux #(
      .WIDTH         (32),
      .SIZE          (3),
      .PRE_FIFO_SIZE (0),
      .POST_FIFO_SIZE(0)
    ) axi_demux_i (
      .clk     (clk),
      .reset   (rst),
      .clear   (1'b0),
      .header  (),
      .dest    (magnitude_sel),
      .i_tdata (reorder_tdata),
      .i_tlast (reorder_tlast),
      .i_tvalid(reorder_tvalid),
      .i_tready(reorder_tready),
      .o_tdata ({mag_sq_in_tdata , mag_in_tdata , mag_bypass_tdata }),
      .o_tlast ({mag_sq_in_tlast , mag_in_tlast , mag_bypass_tlast }),
      .o_tvalid({mag_sq_in_tvalid, mag_in_tvalid, mag_bypass_tvalid}),
      .o_tready({mag_sq_in_tready, mag_in_tready, mag_bypass_tready})
    );
  end

  //---------------------------------------------------------------------------
  // Magnitude
  //---------------------------------------------------------------------------

  wire [31:0] mag_out_tdata;
  wire        mag_out_tlast;
  wire        mag_out_tvalid;
  wire        mag_out_tready;

  if (EN_MAGNITUDE) begin : gen_magnitude
    wire [16:0] round_in_tdata;
    wire [31:0] round_in_tdata_tmp;
    wire        round_in_tlast;
    wire        round_in_tvalid;
    wire        round_in_tready;

    wire [15:0] mag_out_tdata_tmp;

    if (!USE_APPROX_MAG) begin : gen_cordic
      wire [47:0] m_axis_dout_tdata;
      // The CORDIC IP below inputs/outputs its data as signed numbers having 2
      // whole bits and 15 fractional bits (17 total bits). To be compliant
      // with AXI, each value is stuffed into a 24-bit vector. On the input, we
      // resize our sc16 inputs to be 24 bits (the upper 7 bits will be ignored
      // by the IP). On the output side, we only need the magnitude, which is
      // in the lower 17 bits. The phase, in the upper bits, is left unused.
      complex_to_magphase_int17 complex_to_magphase_int17_i (
        .aclk                   (clk),
        .aresetn                (~rst),
        .s_axis_cartesian_tvalid(mag_in_tvalid),
        .s_axis_cartesian_tlast (mag_in_tlast),
        .s_axis_cartesian_tready(mag_in_tready),
        .s_axis_cartesian_tdata ({ 24'(signed'(mag_in_tdata[31:16])),
                                   24'(signed'(mag_in_tdata[15:0])) }),
        .m_axis_dout_tvalid     (round_in_tvalid),
        .m_axis_dout_tlast      (round_in_tlast),
        .m_axis_dout_tdata      (m_axis_dout_tdata),
        .m_axis_dout_tready     (round_in_tready)
      );
      assign round_in_tdata_tmp = 32'(m_axis_dout_tdata[16:0]);
    end else if (USE_APPROX_MAG) begin : gen_approx
      complex_to_mag_approx complex_to_mag_approx_i (
        .clk     (clk),
        .reset   (rst),
        .clear   (1'b0),
        .i_tvalid(mag_in_tvalid),
        .i_tlast (mag_in_tlast),
        .i_tready(mag_in_tready),
        .i_tdata (mag_in_tdata),
        .o_tvalid(round_in_tvalid),
        .o_tlast (round_in_tlast),
        .o_tready(round_in_tready),
        .o_tdata (round_in_tdata_tmp[15:0])
      );
      assign round_in_tdata_tmp[31:16] = '0;
    end

    // The magnitude is always positive, so we set the MSB to 0 then clip the
    // result to a signed 16-bit value.
    assign round_in_tdata = {1'b0, round_in_tdata_tmp[15:0]};

    axi_round_and_clip #(
      .WIDTH_IN (17),
      .WIDTH_OUT(16),
      .CLIP_BITS(1)
    ) axi_round_and_clip_i (
      .clk     (clk),
      .reset   (rst),
      .i_tdata (round_in_tdata),
      .i_tlast (round_in_tlast),
      .i_tvalid(round_in_tvalid),
      .i_tready(round_in_tready),
      .o_tdata (mag_out_tdata_tmp),
      .o_tlast (mag_out_tlast),
      .o_tvalid(mag_out_tvalid),
      .o_tready(mag_out_tready)
    );

    // Put the resulting magnitude in the "real" part of the output
    assign mag_out_tdata = {mag_out_tdata_tmp, 16'd0};

  end else begin : gen_no_magnitude
    assign mag_out_tdata  = '0;
    assign mag_out_tlast  = '0;
    assign mag_out_tvalid = '0;
    assign mag_in_tready  = '1;
  end

  //---------------------------------------------------------------------------
  // Magnitude Squared
  //---------------------------------------------------------------------------

  wire [31:0] mag_sq_out_tdata;
  wire        mag_sq_out_tlast;
  wire        mag_sq_out_tvalid;
  wire        mag_sq_out_tready;

  if (EN_MAGNITUDE_SQ) begin : gen_magnitude_squared
    wire [31:0] round_in_tdata;
    wire        round_in_tlast;
    wire        round_in_tvalid;
    wire        round_in_tready;

    wire [15:0] mag_sq_out_tdata_tmp;

    complex_to_magsq complex_to_magsq_i (
      .clk     (clk),
      .reset   (rst),
      .clear   (1'b0),
      .i_tvalid(mag_sq_in_tvalid),
      .i_tlast (mag_sq_in_tlast),
      .i_tready(mag_sq_in_tready),
      .i_tdata (mag_sq_in_tdata),
      .o_tvalid(round_in_tvalid),
      .o_tlast (round_in_tlast),
      .o_tready(round_in_tready),
      .o_tdata (round_in_tdata)
    );

    axi_round_and_clip #(
      .WIDTH_IN (32),
      .WIDTH_OUT(16),
      .CLIP_BITS(1)
    ) axi_round_and_clip_i (
      .clk     (clk),
      .reset   (rst),
      .i_tdata (round_in_tdata),
      .i_tlast (round_in_tlast),
      .i_tvalid(round_in_tvalid),
      .i_tready(round_in_tready),
      .o_tdata (mag_sq_out_tdata_tmp),
      .o_tlast (mag_sq_out_tlast),
      .o_tvalid(mag_sq_out_tvalid),
      .o_tready(mag_sq_out_tready)
    );

    assign mag_sq_out_tdata = {mag_sq_out_tdata_tmp, 16'd0};

  end else begin : gen_no_magnitude_squared
    assign mag_sq_out_tdata  = '0;
    assign mag_sq_out_tlast  = '0;
    assign mag_sq_out_tvalid = '0;
    assign mag_sq_in_tready  = '1;
  end

  //---------------------------------------------------------------------------
  // Combine Magnitude Options
  //---------------------------------------------------------------------------

  if (EN_MAGNITUDE || EN_MAGNITUDE_SQ) begin : gen_mag_mux
    axi_mux #(
      .PRIO          (1),
      .WIDTH         (32),
      .SIZE          (3),
      .PRE_FIFO_SIZE (0),
      .POST_FIFO_SIZE(0)
    ) axi_demux_i (
      .clk     (clk),
      .reset   (rst),
      .clear   (1'b0),
      .i_tdata ({mag_sq_out_tdata , mag_out_tdata , mag_bypass_tdata }),
      .i_tlast ({mag_sq_out_tlast , mag_out_tlast , mag_bypass_tlast }),
      .i_tvalid({mag_sq_out_tvalid, mag_out_tvalid, mag_bypass_tvalid}),
      .i_tready({mag_sq_out_tready, mag_out_tready, mag_bypass_tready}),
      .o_tdata (m_axis_tdata),
      .o_tlast (m_axis_tlast),
      .o_tvalid(m_axis_tvalid),
      .o_tready(m_axis_tready)
    );
  end else begin : gen_no_mag_mux
    assign m_axis_tdata   = reorder_tdata;
    assign m_axis_tlast   = reorder_tlast;
    assign m_axis_tvalid  = reorder_tvalid;
    assign reorder_tready = m_axis_tready;
  end


endmodule : fft_post_processing


`default_nettype wire