aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/complex_invert.v
blob: 800b5dc979e61dedefbf6f8c9107115981555fee (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//
// Copyright 2015 Ettus Research LLC
//
// General complex invert algorithm:
//   1        1      a - bi     a - bi          a          bi
// ------ = ------ * ------ = ----------- = --------- - ---------
// a + bi   a + bi   a - bi   a^2 + b^2     a^2 + b^2   a^2 + b^2
//

module complex_invert
(
  input clk, input reset, input clear,
  input  [31:0] i_tdata, input  i_tlast, input  i_tvalid, output i_tready,
  output [31:0] o_tdata, output o_tlast, output o_tvalid, input  o_tready);

  wire [15:0] a_tdata;
  wire [31:0] a_tdata_int;
  wire        a_tlast;
  wire        a_tvalid;
  wire        a_tready;
  wire [15:0] b_tdata;
  wire [31:0] b_tdata_int;
  wire        b_tlast;
  wire        b_tvalid;
  wire        b_tready;
  wire [31:0] a_b_tdata;
  wire        a_b_tlast;
  wire        a_b_tvalid;
  wire        a_b_tready;

  // Replicate input data into three streams with FIFOing to account for varying latency on the paths
  split_stream_fifo #(
    .WIDTH(32),
    .ACTIVE_MASK(4'b0111),
    .FIFO_SIZE(5))
  input_split_stream_fifo0 (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready),
    .o0_tdata(a_tdata_int), .o0_tlast(a_tlast), .o0_tvalid(a_tvalid), .o0_tready(a_tready),
    .o1_tdata(b_tdata_int), .o1_tlast(b_tlast), .o1_tvalid(b_tvalid), .o1_tready(b_tready),
    .o2_tdata(a_b_tdata), .o2_tlast(a_b_tlast), .o2_tvalid(a_b_tvalid), .o2_tready(a_b_tready),
    .o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b0));

  assign a_tdata = a_tdata_int[31:16];
  assign b_tdata = b_tdata_int[15:0];

  wire [31:0] a2_plus_b2_tdata;
  wire        a2_plus_b2_tlast;
  wire        a2_plus_b2_tvalid;
  wire        a2_plus_b2_tready;

  // a^2 + b^2
  complex_to_magsq
  a2_p_b2_complex_to_magsq (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata(a_b_tdata), .i_tlast(a_b_tlast), .i_tvalid(a_b_tvalid), .i_tready(a_b_tready),
    .o_tdata(a2_plus_b2_tdata), .o_tlast(a2_plus_b2_tlast), .o_tvalid(a2_plus_b2_tvalid), .o_tready(a2_plus_b2_tready));

  wire [31:0] a2_plus_b2_0_tdata;
  wire        a2_plus_b2_0_tlast;
  wire        a2_plus_b2_0_tvalid;
  wire        a2_plus_b2_0_tready;
  wire [31:0] a2_plus_b2_1_tdata;
  wire        a2_plus_b2_1_tlast;
  wire        a2_plus_b2_1_tvalid;
  wire        a2_plus_b2_1_tready;

  // Replicate two a^2 + b^2 streams for dividers
  split_stream_fifo #(
    .WIDTH(32),
    .ACTIVE_MASK(4'b0011),
    .FIFO_SIZE(5))
  input_split_stream_fifo1 (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata(a2_plus_b2_tdata), .i_tlast(a2_plus_b2_tlast), .i_tvalid(a2_plus_b2_tvalid), .i_tready(a2_plus_b2_tready),
    .o0_tdata(a2_plus_b2_0_tdata), .o0_tlast(a2_plus_b2_0_tlast), .o0_tvalid(a2_plus_b2_0_tvalid), .o0_tready(a2_plus_b2_0_tready),
    .o1_tdata(a2_plus_b2_1_tdata), .o1_tlast(a2_plus_b2_1_tlast), .o1_tvalid(a2_plus_b2_1_tvalid), .o1_tready(a2_plus_b2_1_tready),
    .o2_tdata(), .o2_tlast(), .o2_tvalid(), .o2_tready(1'b0),
    .o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b0));

  wire        div_by_zero_a;
  wire [47:0] a_div_a2_plus_b2_tdata_int; // signed bit, 15 integer bits, fraction sign bit, 31 fraction
  wire [47:0] a_div_a2_plus_b2_tdata = div_by_zero_a ? 48'd0 : a_div_a2_plus_b2_tdata_int;
  wire        a_div_a2_plus_b2_tlast;
  wire        a_div_a2_plus_b2_tvalid;
  wire        a_div_a2_plus_b2_tready;

  //     a
  // ---------
  // a^2 + b^2
  // Warning: Divider does not sign extend fractional part into the integer part, although we throw away the integer
  //          part so this issue does not affect our design.
  divide_int16_int32
  a_div_a2_plus_b2_divider (
    .aclk(clk), .aresetn(~reset),
    .s_axis_divisor_tdata(a2_plus_b2_0_tdata), .s_axis_divisor_tlast(a2_plus_b2_0_tlast), .s_axis_divisor_tvalid(a2_plus_b2_0_tvalid), .s_axis_divisor_tready(a2_plus_b2_0_tready),
    .s_axis_dividend_tdata(a_tdata), .s_axis_dividend_tlast(a_tlast), .s_axis_dividend_tvalid(a_tvalid), .s_axis_dividend_tready(a_tready),
    .m_axis_dout_tdata(a_div_a2_plus_b2_tdata_int), .m_axis_dout_tlast(a_div_a2_plus_b2_tlast), .m_axis_dout_tvalid(a_div_a2_plus_b2_tvalid), .m_axis_dout_tready(a_div_a2_plus_b2_tready),
    .m_axis_dout_tuser(div_by_zero_a));

  wire [15:0] neg_b_tdata;
  wire        neg_b_tlast;
  wire        neg_b_tvalid;
  wire        neg_b_tready;
  wire [15:0] neg_b = (b_tdata == -16'sd32768) ? 16'sd32767 : (~b_tdata + 1'b1);

  // Negate b
  axi_fifo_flop #(.WIDTH(17))
  neg_b_axi_fifo_flop (
    .clk(clk), .reset(reset), .clear(clear),
    .i_tdata({b_tlast,neg_b}), .i_tvalid(b_tvalid), .i_tready(b_tready),
    .o_tdata({neg_b_tlast,neg_b_tdata}), .o_tvalid(neg_b_tvalid), .o_tready(neg_b_tready),
    .space(), .occupied());

  wire        div_by_zero_b;
  wire [47:0] neg_b_div_a2_plus_b2_tdata_int;
  wire [47:0] neg_b_div_a2_plus_b2_tdata = div_by_zero_b ? 48'd0 : neg_b_div_a2_plus_b2_tdata_int;
  wire        neg_b_div_a2_plus_b2_tlast;
  wire        neg_b_div_a2_plus_b2_tvalid;
  wire        neg_b_div_a2_plus_b2_tready;

  //     bi
  // ---------
  // a^2 + b^2
  divide_int16_int32
  neg_b_div_a2_plus_b2_divider (
    .aclk(clk), .aresetn(~reset),
    .s_axis_divisor_tdata(a2_plus_b2_1_tdata), .s_axis_divisor_tlast(a2_plus_b2_1_tlast), .s_axis_divisor_tvalid(a2_plus_b2_1_tvalid), .s_axis_divisor_tready(a2_plus_b2_1_tready),
    .s_axis_dividend_tdata(neg_b_tdata), .s_axis_dividend_tlast(neg_b_tlast), .s_axis_dividend_tvalid(neg_b_tvalid), .s_axis_dividend_tready(neg_b_tready),
    .m_axis_dout_tdata(neg_b_div_a2_plus_b2_tdata_int), .m_axis_dout_tlast(neg_b_div_a2_plus_b2_tlast), .m_axis_dout_tvalid(neg_b_div_a2_plus_b2_tvalid), .m_axis_dout_tready(neg_b_div_a2_plus_b2_tready),
    .m_axis_dout_tuser(div_by_zero_b));

  // Throw away integer part as the result will always be a fraction due to a^2 + b^2 > a (or b)
  wire [63:0] one_div_a_plus_bi_tdata = {a_div_a2_plus_b2_tdata[31:0],neg_b_div_a2_plus_b2_tdata[31:0]};
  wire        one_div_a_plus_bi_tlast;
  wire        one_div_a_plus_bi_tvalid;
  wire        one_div_a_plus_bi_tready;

  // Join into one word
  axi_join #(
    .INPUTS(2))
  inst_axi_join (
   .i_tlast({a_div_a2_plus_b2_tlast,neg_b_div_a2_plus_b2_tlast}), .i_tvalid({a_div_a2_plus_b2_tvalid,neg_b_div_a2_plus_b2_tvalid}), .i_tready({a_div_a2_plus_b2_tready,neg_b_div_a2_plus_b2_tready}),
   .o_tlast(one_div_a_plus_bi_tlast), .o_tvalid(one_div_a_plus_bi_tvalid), .o_tready(one_div_a_plus_bi_tready));

  // Truncate to a complex int16
  axi_round_and_clip_complex #(
    .WIDTH_IN(32),
    .WIDTH_OUT(16),
    .CLIP_BITS(11), // Calibrated value
    .FIFOSIZE())
  inst_axi_round_and_clip_complex (
    .clk(clk), .reset(reset),
    .i_tdata(one_div_a_plus_bi_tdata), .i_tlast(one_div_a_plus_bi_tlast), .i_tvalid(one_div_a_plus_bi_tvalid), .i_tready(one_div_a_plus_bi_tready),
    .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready));

endmodule