File: noc_block_fft.v

package info (click to toggle)
uhd 3.15.0.0-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 202,252 kB
  • sloc: cpp: 182,756; ansic: 94,109; vhdl: 53,420; python: 45,849; xml: 12,956; tcl: 7,046; makefile: 2,248; sh: 1,741; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (414 lines) | stat: -rw-r--r-- 17,797 bytes parent folder | download | duplicates (2)
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
//
// Copyright 2014-2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

module noc_block_fft #(
  parameter EN_MAGNITUDE_OUT = 0,        // CORDIC based magnitude calculation
  parameter EN_MAGNITUDE_APPROX_OUT = 1, // Multiplier-less, lower resource usage
  parameter EN_MAGNITUDE_SQ_OUT = 1,     // Magnitude squared
  parameter EN_FFT_SHIFT = 1,            // Center zero frequency bin
  parameter NOC_ID = 64'hFF70_0000_0000_0000,
  parameter STR_SINK_FIFOSIZE = 11)
(
  input bus_clk, input bus_rst,
  input ce_clk, input ce_rst,
  input  [63:0] i_tdata, input  i_tlast, input  i_tvalid, output i_tready,
  output [63:0] o_tdata, output o_tlast, output o_tvalid, input  o_tready,
  output [63:0] debug
);

  ////////////////////////////////////////////////////////////
  //
  // RFNoC Shell
  //
  ////////////////////////////////////////////////////////////
  wire [31:0] set_data;
  wire [7:0]  set_addr;
  wire        set_stb;
  reg  [63:0] rb_data;
  wire [7:0]  rb_addr;

  wire [63:0] cmdout_tdata, ackin_tdata;
  wire        cmdout_tlast, cmdout_tvalid, cmdout_tready, ackin_tlast, ackin_tvalid, ackin_tready;

  wire [63:0] str_sink_tdata, str_src_tdata;
  wire        str_sink_tlast, str_sink_tvalid, str_sink_tready, str_src_tlast, str_src_tvalid, str_src_tready;

  wire        clear_tx_seqnum;
  wire [15:0] next_dst_sid;

  noc_shell #(
    .NOC_ID(NOC_ID),
    .STR_SINK_FIFOSIZE(STR_SINK_FIFOSIZE))
  noc_shell (
    .bus_clk(bus_clk), .bus_rst(bus_rst),
    .i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
    .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready),
    // Computer Engine Clock Domain
    .clk(ce_clk), .reset(ce_rst),
    // Control Sink
    .set_data(set_data), .set_addr(set_addr), .set_stb(set_stb), .set_time(), .set_has_time(),
    .rb_stb(1'b1), .rb_data(rb_data), .rb_addr(rb_addr),
    // Control Source
    .cmdout_tdata(cmdout_tdata), .cmdout_tlast(cmdout_tlast), .cmdout_tvalid(cmdout_tvalid), .cmdout_tready(cmdout_tready),
    .ackin_tdata(ackin_tdata), .ackin_tlast(ackin_tlast), .ackin_tvalid(ackin_tvalid), .ackin_tready(ackin_tready),
    // Stream Sink
    .str_sink_tdata(str_sink_tdata), .str_sink_tlast(str_sink_tlast), .str_sink_tvalid(str_sink_tvalid), .str_sink_tready(str_sink_tready),
    // Stream Source
    .str_src_tdata(str_src_tdata), .str_src_tlast(str_src_tlast), .str_src_tvalid(str_src_tvalid), .str_src_tready(str_src_tready),
    // Misc
    .vita_time(64'd0), .clear_tx_seqnum(clear_tx_seqnum),
    .src_sid(), .next_dst_sid(next_dst_sid), .resp_in_dst_sid(), .resp_out_dst_sid(),
    .debug(debug));

  ////////////////////////////////////////////////////////////
  //
  // AXI Wrapper
  // Convert RFNoC Shell interface into AXI stream interface
  //
  ////////////////////////////////////////////////////////////
  wire [31:0] m_axis_data_tdata;
  wire        m_axis_data_tlast;
  wire        m_axis_data_tvalid;
  wire        m_axis_data_tready;

  wire [31:0] s_axis_data_tdata;
  wire        s_axis_data_tlast;
  wire        s_axis_data_tvalid;
  wire        s_axis_data_tready;

  axi_wrapper #(
    .SIMPLE_MODE(1))
  inst_axi_wrapper (
    .bus_clk(bus_clk), .bus_rst(bus_rst),
    .clk(ce_clk), .reset(ce_rst),
    .clear_tx_seqnum(clear_tx_seqnum),
    .next_dst(next_dst_sid),
    .set_stb(), .set_addr(), .set_data(),
    .i_tdata(str_sink_tdata), .i_tlast(str_sink_tlast), .i_tvalid(str_sink_tvalid), .i_tready(str_sink_tready),
    .o_tdata(str_src_tdata), .o_tlast(str_src_tlast), .o_tvalid(str_src_tvalid), .o_tready(str_src_tready),
    .m_axis_data_tdata(m_axis_data_tdata),
    .m_axis_data_tlast(m_axis_data_tlast),
    .m_axis_data_tvalid(m_axis_data_tvalid),
    .m_axis_data_tready(m_axis_data_tready),
    .m_axis_data_tuser(),
    .s_axis_data_tdata(s_axis_data_tdata),
    .s_axis_data_tlast(s_axis_data_tlast),
    .s_axis_data_tvalid(s_axis_data_tvalid),
    .s_axis_data_tready(s_axis_data_tready),
    .s_axis_data_tuser(),
    .m_axis_config_tdata(),
    .m_axis_config_tlast(),
    .m_axis_config_tvalid(),
    .m_axis_config_tready(),
    .m_axis_pkt_len_tdata(),
    .m_axis_pkt_len_tvalid(),
    .m_axis_pkt_len_tready());
  
  ////////////////////////////////////////////////////////////
  //
  // User code
  //
  ////////////////////////////////////////////////////////////
  
  // Control Source Unused
  assign cmdout_tdata  = 64'd0;
  assign cmdout_tlast  = 1'b0;
  assign cmdout_tvalid = 1'b0;
  assign ackin_tready  = 1'b1;

  localparam MAX_FFT_SIZE_LOG2          = 11;

  localparam [31:0] SR_FFT_RESET        = 131;
  localparam [31:0] SR_FFT_SIZE_LOG2    = 132;
  localparam [31:0] SR_MAGNITUDE_OUT    = 133;
  localparam [31:0] SR_FFT_DIRECTION    = 134;
  localparam [31:0] SR_FFT_SCALING      = 135;
  localparam [31:0] SR_FFT_SHIFT_CONFIG = 136;

  // FFT Output
  localparam [1:0] COMPLEX_OUT = 0;
  localparam [1:0] MAG_OUT     = 1;
  localparam [1:0] MAG_SQ_OUT  = 2;

  // FFT Direction
  localparam [0:0] FFT_REVERSE = 0;
  localparam [0:0] FFT_FORWARD = 1;

  wire [1:0]  magnitude_out;
  wire [31:0] fft_data_o_tdata;
  wire        fft_data_o_tlast;
  wire        fft_data_o_tvalid;
  wire        fft_data_o_tready;
  wire [15:0] fft_data_o_tuser;
  wire [31:0] fft_shift_o_tdata;
  wire        fft_shift_o_tlast;
  wire        fft_shift_o_tvalid;
  wire        fft_shift_o_tready;
  wire [31:0] fft_mag_i_tdata, fft_mag_o_tdata, fft_mag_o_tdata_int;
  wire        fft_mag_i_tlast, fft_mag_o_tlast;
  wire        fft_mag_i_tvalid, fft_mag_o_tvalid;
  wire        fft_mag_i_tready, fft_mag_o_tready;
  wire [31:0] fft_mag_sq_i_tdata, fft_mag_sq_o_tdata;
  wire        fft_mag_sq_i_tlast, fft_mag_sq_o_tlast;
  wire        fft_mag_sq_i_tvalid, fft_mag_sq_o_tvalid;
  wire        fft_mag_sq_i_tready, fft_mag_sq_o_tready;
  wire [31:0] fft_mag_round_i_tdata, fft_mag_round_o_tdata;
  wire        fft_mag_round_i_tlast, fft_mag_round_o_tlast;
  wire        fft_mag_round_i_tvalid, fft_mag_round_o_tvalid;
  wire        fft_mag_round_i_tready, fft_mag_round_o_tready;

  // Settings Registers
  wire fft_reset;
  setting_reg #(
    .my_addr(SR_FFT_RESET), .awidth(8), .width(1))
  sr_fft_reset (
    .clk(ce_clk), .rst(ce_rst),
    .strobe(set_stb), .addr(set_addr), .in(set_data), .out(fft_reset), .changed());

  // Two instances of FFT size register, one for FFT core and one for FFT shift
  localparam DEFAULT_FFT_SIZE = 8; // 256
  wire [7:0] fft_size_log2_tdata ,fft_core_size_log2_tdata;
  wire fft_size_log2_tvalid, fft_core_size_log2_tvalid, fft_size_log2_tready, fft_core_size_log2_tready;
  axi_setting_reg #(
    .ADDR(SR_FFT_SIZE_LOG2), .AWIDTH(8), .WIDTH(8), .DATA_AT_RESET(DEFAULT_FFT_SIZE), .VALID_AT_RESET(1))
  sr_fft_size_log2 (
    .clk(ce_clk), .reset(ce_rst),
    .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
    .o_tdata(fft_size_log2_tdata), .o_tlast(), .o_tvalid(fft_size_log2_tvalid), .o_tready(fft_size_log2_tready));

  axi_setting_reg #(
    .ADDR(SR_FFT_SIZE_LOG2), .AWIDTH(8), .WIDTH(8), .DATA_AT_RESET(DEFAULT_FFT_SIZE), .VALID_AT_RESET(1))
  sr_fft_size_log2_2 (
    .clk(ce_clk), .reset(ce_rst),
    .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
    .o_tdata(fft_core_size_log2_tdata), .o_tlast(), .o_tvalid(fft_core_size_log2_tvalid), .o_tready(fft_core_size_log2_tready));

  // Forward = 0, Reverse = 1
  localparam DEFAULT_FFT_DIRECTION = 0;
  wire fft_direction_tdata;
  wire fft_direction_tvalid, fft_direction_tready;
  axi_setting_reg #(
    .ADDR(SR_FFT_DIRECTION), .AWIDTH(8), .WIDTH(1), .DATA_AT_RESET(DEFAULT_FFT_DIRECTION), .VALID_AT_RESET(1))
  sr_fft_direction (
    .clk(ce_clk), .reset(ce_rst),
    .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
    .o_tdata(fft_direction_tdata), .o_tlast(), .o_tvalid(fft_direction_tvalid), .o_tready(fft_direction_tready));

  localparam [11:0] DEFAULT_FFT_SCALING = 12'b011010101010; // Conservative 1/N scaling
  wire [11:0] fft_scaling_tdata;
  wire fft_scaling_tvalid, fft_scaling_tready;
  axi_setting_reg #(
    .ADDR(SR_FFT_SCALING), .AWIDTH(8), .WIDTH(12), .DATA_AT_RESET(DEFAULT_FFT_SCALING), .VALID_AT_RESET(1))
  sr_fft_scaling (
    .clk(ce_clk), .reset(ce_rst),
    .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
    .o_tdata(fft_scaling_tdata), .o_tlast(), .o_tvalid(fft_scaling_tvalid), .o_tready(fft_scaling_tready));

  wire [1:0] fft_shift_config_tdata;
  wire fft_shift_config_tvalid, fft_shift_config_tready;
  axi_setting_reg #(
    .ADDR(SR_FFT_SHIFT_CONFIG), .AWIDTH(8), .WIDTH(2))
  sr_fft_shift_config (
    .clk(ce_clk), .reset(ce_rst),
    .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
    .o_tdata(fft_shift_config_tdata), .o_tlast(), .o_tvalid(fft_shift_config_tvalid), .o_tready(fft_shift_config_tready));

  // Synchronize writing configuration to the FFT core
  reg fft_config_ready;
  wire fft_config_write = fft_config_ready & m_axis_data_tvalid & m_axis_data_tready;
  always @(posedge ce_clk) begin
    if (ce_rst | fft_reset) begin
      fft_config_ready   <= 1'b1;
    end else begin
      if (fft_config_write) begin
        fft_config_ready <= 1'b0;
      end else if (m_axis_data_tlast) begin
        fft_config_ready <= 1'b1;
      end
    end
  end

  wire [23:0] fft_config_tdata     = {3'd0, fft_scaling_tdata, fft_direction_tdata, fft_core_size_log2_tdata};
  wire fft_config_tvalid           = fft_config_write & (fft_scaling_tvalid | fft_direction_tvalid | fft_core_size_log2_tvalid);
  wire fft_config_tready;
  assign fft_core_size_log2_tready = fft_config_tready & fft_config_write;
  assign fft_direction_tready      = fft_config_tready & fft_config_write;
  assign fft_scaling_tready        = fft_config_tready & fft_config_write;
  axi_fft inst_axi_fft (
    .aclk(ce_clk), .aresetn(~(ce_rst | fft_reset)),
    .s_axis_data_tvalid(m_axis_data_tvalid),
    .s_axis_data_tready(m_axis_data_tready),
    .s_axis_data_tlast(m_axis_data_tlast),
    .s_axis_data_tdata({m_axis_data_tdata[15:0],m_axis_data_tdata[31:16]}),
    .m_axis_data_tvalid(fft_data_o_tvalid),
    .m_axis_data_tready(fft_data_o_tready),
    .m_axis_data_tlast(fft_data_o_tlast),
    .m_axis_data_tdata({fft_data_o_tdata[15:0],fft_data_o_tdata[31:16]}),
    .m_axis_data_tuser(fft_data_o_tuser), // FFT index
    .s_axis_config_tdata(fft_config_tdata),
    .s_axis_config_tvalid(fft_config_tvalid),
    .s_axis_config_tready(fft_config_tready),
    .event_frame_started(),
    .event_tlast_unexpected(),
    .event_tlast_missing(),
    .event_status_channel_halt(),
    .event_data_in_channel_halt(),
    .event_data_out_channel_halt());

  // Mux control signals
  assign fft_shift_o_tready     = (magnitude_out == MAG_OUT)    ? fft_mag_i_tready         :
                                  (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_i_tready      : s_axis_data_tready;
  assign fft_mag_i_tvalid       = (magnitude_out == MAG_OUT)    ? fft_shift_o_tvalid       : 1'b0;
  assign fft_mag_i_tlast        = (magnitude_out == MAG_OUT)    ? fft_shift_o_tlast        : 1'b0;
  assign fft_mag_i_tdata        = fft_shift_o_tdata;
  assign fft_mag_o_tready       = (magnitude_out == MAG_OUT)    ? fft_mag_round_i_tready   : 1'b0;
  assign fft_mag_sq_i_tvalid    = (magnitude_out == MAG_SQ_OUT) ? fft_shift_o_tvalid       : 1'b0;
  assign fft_mag_sq_i_tlast     = (magnitude_out == MAG_SQ_OUT) ? fft_shift_o_tlast        : 1'b0;
  assign fft_mag_sq_i_tdata     = fft_shift_o_tdata;
  assign fft_mag_sq_o_tready    = (magnitude_out == MAG_SQ_OUT) ? fft_mag_round_i_tready   : 1'b0;
  assign fft_mag_round_i_tvalid = (magnitude_out == MAG_OUT)    ? fft_mag_o_tvalid         :
                                  (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_o_tvalid      : 1'b0;
  assign fft_mag_round_i_tlast  = (magnitude_out == MAG_OUT)    ? fft_mag_o_tlast          :
                                  (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_o_tlast       : 1'b0;
  assign fft_mag_round_i_tdata  = (magnitude_out == MAG_OUT)    ? fft_mag_o_tdata          : fft_mag_sq_o_tdata;
  assign fft_mag_round_o_tready = s_axis_data_tready;
  assign s_axis_data_tvalid     = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tvalid : fft_shift_o_tvalid;
  assign s_axis_data_tlast      = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tlast  : fft_shift_o_tlast;
  assign s_axis_data_tdata      = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tdata  : fft_shift_o_tdata;

  // Conditionally synth magnitude / magnitude^2 logic
  generate
    if (EN_MAGNITUDE_OUT | EN_MAGNITUDE_APPROX_OUT | EN_MAGNITUDE_SQ_OUT) begin
      setting_reg #(
        .my_addr(SR_MAGNITUDE_OUT), .awidth(8), .width(2))
      sr_magnitude_out (
        .clk(ce_clk), .rst(ce_rst),
        .strobe(set_stb), .addr(set_addr), .in(set_data), .out(magnitude_out), .changed());
    end else begin
      // Magnitude calculation logic not included, so always bypass
      assign magnitude_out = 2'd0;
    end

    if (EN_FFT_SHIFT) begin
      fft_shift #(
        .MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2),
        .WIDTH(32))
      inst_fft_shift (
        .clk(ce_clk), .reset(ce_rst | fft_reset),
        .config_tdata(fft_shift_config_tdata),
        .config_tvalid(fft_shift_config_tvalid),
        .config_tready(fft_shift_config_tready),
        .fft_size_log2_tdata(fft_size_log2_tdata[$clog2(MAX_FFT_SIZE_LOG2)-1:0]),
        .fft_size_log2_tvalid(fft_size_log2_tvalid),
        .fft_size_log2_tready(fft_size_log2_tready),
        .i_tdata(fft_data_o_tdata),
        .i_tlast(fft_data_o_tlast),
        .i_tvalid(fft_data_o_tvalid),
        .i_tready(fft_data_o_tready),
        .i_tuser(fft_data_o_tuser[MAX_FFT_SIZE_LOG2-1:0]),
        .o_tdata(fft_shift_o_tdata),
        .o_tlast(fft_shift_o_tlast),
        .o_tvalid(fft_shift_o_tvalid),
        .o_tready(fft_shift_o_tready));
    end else begin
      assign fft_shift_o_tdata = fft_data_o_tdata;
      assign fft_shift_o_tlast = fft_data_o_tlast;
      assign fft_shift_o_tvalid = fft_data_o_tvalid;
      assign fft_data_o_tready = fft_shift_o_tready;
    end

    // More accurate magnitude calculation takes precedence if enabled
    if (EN_MAGNITUDE_OUT) begin
      complex_to_magphase
      inst_complex_to_magphase (
        .aclk(ce_clk), .aresetn(~(ce_rst | fft_reset)),
        .s_axis_cartesian_tvalid(fft_mag_i_tvalid),
        .s_axis_cartesian_tlast(fft_mag_i_tlast),
        .s_axis_cartesian_tready(fft_mag_i_tready),
        .s_axis_cartesian_tdata(fft_mag_i_tdata),
        .m_axis_dout_tvalid(fft_mag_o_tvalid),
        .m_axis_dout_tlast(fft_mag_o_tlast),
        .m_axis_dout_tdata(fft_mag_o_tdata_int),
        .m_axis_dout_tready(fft_mag_o_tready));
      assign fft_mag_o_tdata = {1'b0, fft_mag_o_tdata_int[15:0], 15'd0};
    end else if (EN_MAGNITUDE_APPROX_OUT) begin
      complex_to_mag_approx
      inst_complex_to_mag_approx (
        .clk(ce_clk), .reset(ce_rst | fft_reset), .clear(1'b0),
        .i_tvalid(fft_mag_i_tvalid),
        .i_tlast(fft_mag_i_tlast),
        .i_tready(fft_mag_i_tready),
        .i_tdata(fft_mag_i_tdata),
        .o_tvalid(fft_mag_o_tvalid),
        .o_tlast(fft_mag_o_tlast),
        .o_tready(fft_mag_o_tready),
        .o_tdata(fft_mag_o_tdata_int[15:0]));
      assign fft_mag_o_tdata = {1'b0, fft_mag_o_tdata_int[15:0], 15'd0};
    end else begin
      assign fft_mag_o_tdata = fft_mag_i_tdata;
      assign fft_mag_o_tlast = fft_mag_i_tlast;
      assign fft_mag_o_tvalid = fft_mag_i_tvalid;
      assign fft_mag_i_tready = fft_mag_o_tready;
    end

    if (EN_MAGNITUDE_SQ_OUT) begin
      complex_to_magsq
      inst_complex_to_magsq (
        .clk(ce_clk), .reset(ce_rst | fft_reset), .clear(1'b0),
        .i_tvalid(fft_mag_sq_i_tvalid),
        .i_tlast(fft_mag_sq_i_tlast),
        .i_tready(fft_mag_sq_i_tready),
        .i_tdata(fft_mag_sq_i_tdata),
        .o_tvalid(fft_mag_sq_o_tvalid),
        .o_tlast(fft_mag_sq_o_tlast),
        .o_tready(fft_mag_sq_o_tready),
        .o_tdata(fft_mag_sq_o_tdata));
    end else begin
      assign fft_mag_sq_o_tdata = fft_mag_sq_i_tdata;
      assign fft_mag_sq_o_tlast = fft_mag_sq_i_tlast;
      assign fft_mag_sq_o_tvalid = fft_mag_sq_i_tvalid;
      assign fft_mag_sq_i_tready = fft_mag_sq_o_tready;
    end

    // Convert to SC16
    if (EN_MAGNITUDE_OUT | EN_MAGNITUDE_APPROX_OUT | EN_MAGNITUDE_SQ_OUT) begin
      axi_round_and_clip #(
        .WIDTH_IN(32),
        .WIDTH_OUT(16),
        .CLIP_BITS(1))
      inst_axi_round_and_clip (
        .clk(ce_clk), .reset(ce_rst | fft_reset),
        .i_tdata(fft_mag_round_i_tdata),
        .i_tlast(fft_mag_round_i_tlast),
        .i_tvalid(fft_mag_round_i_tvalid),
        .i_tready(fft_mag_round_i_tready),
        .o_tdata(fft_mag_round_o_tdata[31:16]),
        .o_tlast(fft_mag_round_o_tlast),
        .o_tvalid(fft_mag_round_o_tvalid),
        .o_tready(fft_mag_round_o_tready));
      assign fft_mag_round_o_tdata[15:0] = {16{16'd0}};
    end else begin
      assign fft_mag_round_o_tdata = fft_mag_round_i_tdata;
      assign fft_mag_round_o_tlast = fft_mag_round_i_tlast;
      assign fft_mag_round_o_tvalid = fft_mag_round_i_tvalid;
      assign fft_mag_round_i_tready = fft_mag_round_o_tready;
    end
  endgenerate

  // Readback registers
  always @*
    case(rb_addr)
      3'd0    : rb_data <= {63'd0, fft_reset};
      3'd1    : rb_data <= {62'd0, magnitude_out};
      3'd2    : rb_data <= {fft_size_log2_tdata};
      3'd3    : rb_data <= {63'd0, fft_direction_tdata};
      3'd4    : rb_data <= {52'd0, fft_scaling_tdata};
      3'd5    : rb_data <= {62'd0, fft_shift_config_tdata};
      default : rb_data <= 64'h0BADC0DE0BADC0DE;
  endcase

endmodule