aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/complex_to_mag_approx.v
blob: 7455afcad14747f47c0ad96b39f0a81137fec9c3 (plain) (blame)
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
//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Fast magnitude approximation.
// 
// ALPHA_DENOM & BETA_DENOM should be a power of 2
// Multiplierless if ALPHA_NUM & BETA_NUM are 1
//
// Mag ~= Alpha * max(|I|, |Q|) + Beta * min(|I|, |Q|)
//
// (table taken from http://www.dspguru.com/dsp/tricks/magnitude-estimator)
// =========================================
// Alpha  Beta       Avg Err   RMS   Peak
//                   (linear)  (dB)  (dB)
// -----------------------------------------
// 1,     1/2        -0.086775 -20.7 -18.6
// 1,     1/4         0.006456 -27.6 -18.7
// 1,     11/32      -0.028505 -28.0 -24.8
// 1,     3/8        -0.040159 -26.4 -23.4
// 15/16, 15/32      -0.018851 -29.2 -24.1
// 15/16, 1/2        -0.030505 -26.9 -24.1
// 31/32, 11/32      -0.000371 -31.6 -22.9
// 31/32, 3/8        -0.012024 -31.4 -26.1
// 61/64, 3/8         0.002043 -32.5 -24.3
// 61/64, 13/32       0.009611 -31.8 -26.6
// =========================================
//
// Input: Complex, Output: Unsigned Int

`ifndef LOG2
`define LOG2(N) ( \
   N < 2    ? 0 : \
   N < 4    ? 1 : \
   N < 8    ? 2 : \
   N < 16   ? 3 : \
   N < 32   ? 4 : \
   N < 64   ? 5 : \
   N < 128  ? 6 : \
   N < 256  ? 7 : \
   N < 512  ? 8 : \
   N < 1024 ? 9 : \
              10)
`endif

module complex_to_mag_approx #(
  parameter ALPHA_NUM = 1,
  parameter ALPHA_DENOM = 1,
  parameter BETA_NUM = 1,
  parameter BETA_DENOM = 4,
  parameter LATENCY = 3, // 0, 1, 2, or 3
  parameter SAMP_WIDTH = 16)
(
  input clk, input reset, input clear,
  input [2*SAMP_WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready,
  output [SAMP_WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
);

  wire [2*SAMP_WIDTH-1:0] pipeline_i_tdata[0:2], pipeline_o_tdata[0:2];
  wire [2:0] pipeline_i_tvalid, pipeline_i_tlast, pipeline_i_tready;
  wire [2:0] pipeline_o_tvalid, pipeline_o_tlast, pipeline_o_tready;
  wire signed [SAMP_WIDTH-1:0] i, q, max, max_int, min, min_int;
  wire [SAMP_WIDTH-1:0] i_abs, q_abs, i_abs_int, q_abs_int, mag;


  // Absolute value
  assign i = i_tdata[2*SAMP_WIDTH-1:SAMP_WIDTH];
  assign q = i_tdata[SAMP_WIDTH-1:0];
  assign i_abs_int = i[SAMP_WIDTH-1] ? (~i + 1'b1) : i;
  assign q_abs_int = q[SAMP_WIDTH-1] ? (~q + 1'b1) : q;


  // First stage pipeline
  assign pipeline_i_tdata[0]  = {i_abs_int,q_abs_int};
  assign pipeline_i_tlast[0]  = i_tlast;
  assign pipeline_i_tvalid[0] = i_tvalid;
  assign pipeline_o_tready[0] = pipeline_i_tready[1];

  axi_fifo_flop #(.WIDTH(SAMP_WIDTH*2+1))
  pipeline0_axi_fifo_flop (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata({pipeline_i_tlast[0],pipeline_i_tdata[0]}), .i_tvalid(pipeline_i_tvalid[0]), .i_tready(pipeline_i_tready[0]),
    .o_tdata({pipeline_o_tlast[0],pipeline_o_tdata[0]}), .o_tvalid(pipeline_o_tvalid[0]), .o_tready(pipeline_o_tready[0]));


  // Max & Min
  assign i_abs = (LATENCY == 3) ? pipeline_o_tdata[0][2*SAMP_WIDTH-1:SAMP_WIDTH] : i_abs;
  assign q_abs = (LATENCY == 3) ? pipeline_o_tdata[0][SAMP_WIDTH-1:0]            : q_abs;
  assign max_int = (i_abs > q_abs) ? i_abs : q_abs;
  assign min_int = (i_abs > q_abs) ? q_abs : i_abs;


  // Second stage pipeline
  assign pipeline_i_tdata[1]  = {max_int,min_int};
  assign pipeline_i_tlast[1]  = (LATENCY == 2) ? i_tlast              : pipeline_o_tlast[0];
  assign pipeline_i_tvalid[1] = (LATENCY == 2) ? i_tvalid             : pipeline_o_tvalid[0];
  assign pipeline_o_tready[1] = pipeline_i_tready[2];

  axi_fifo_flop #(.WIDTH(SAMP_WIDTH*2+1))
  pipeline1_axi_fifo_flop (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata({pipeline_i_tlast[1],pipeline_i_tdata[1]}), .i_tvalid(pipeline_i_tvalid[1]), .i_tready(pipeline_i_tready[1]),
    .o_tdata({pipeline_o_tlast[1],pipeline_o_tdata[1]}), .o_tvalid(pipeline_o_tvalid[1]), .o_tready(pipeline_o_tready[1]));


  // Magnitude Approx
  assign max = (LATENCY >= 2) ? pipeline_o_tdata[1][2*SAMP_WIDTH-1:SAMP_WIDTH] : max_int;
  assign min = (LATENCY >= 2) ? pipeline_o_tdata[1][SAMP_WIDTH-1:0]            : min_int;
  assign mag = ALPHA_NUM * {{`LOG2(ALPHA_DENOM){1'b0}},max[SAMP_WIDTH-1:`LOG2(ALPHA_DENOM)]} +
                BETA_NUM * {{`LOG2( BETA_DENOM){1'b0}},min[SAMP_WIDTH-1:`LOG2( BETA_DENOM)]};


  // Third stage pipeline
  assign pipeline_i_tdata[2][SAMP_WIDTH-1:0] = mag;
  assign pipeline_i_tlast[2]  = (LATENCY == 1) ? i_tlast  : pipeline_o_tlast[1];
  assign pipeline_i_tvalid[2] = (LATENCY == 1) ? i_tvalid : pipeline_o_tvalid[1];
  assign pipeline_o_tready[2] = o_tready;

  axi_fifo_flop #(.WIDTH(SAMP_WIDTH+1))
  pipeline2_axi_fifo_flop (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata({pipeline_i_tlast[2],pipeline_i_tdata[2][SAMP_WIDTH-1:0]}), .i_tvalid(pipeline_i_tvalid[2]), .i_tready(pipeline_i_tready[2]),
    .o_tdata({pipeline_o_tlast[2],pipeline_o_tdata[2][SAMP_WIDTH-1:0]}), .o_tvalid(pipeline_o_tvalid[2]), .o_tready(pipeline_o_tready[2]));


  // Output based on LATENCY mux
  assign o_tdata  = (LATENCY == 0) ? mag      : pipeline_o_tdata[2][SAMP_WIDTH-1:0];
  assign o_tlast  = (LATENCY == 0) ? i_tlast  : pipeline_o_tlast[2];
  assign o_tvalid = (LATENCY == 0) ? i_tvalid : pipeline_o_tvalid[2];
  assign i_tready = (LATENCY == 0) ? o_tready : 
                    (LATENCY == 1) ? pipeline_i_tready[2] :
                    (LATENCY == 2) ? pipeline_i_tready[1] : pipeline_i_tready[0];

endmodule