aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/complex_invert.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/complex_invert.v')
-rw-r--r--fpga/usrp3/lib/rfnoc/complex_invert.v157
1 files changed, 157 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/complex_invert.v b/fpga/usrp3/lib/rfnoc/complex_invert.v
new file mode 100644
index 000000000..800b5dc97
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/complex_invert.v
@@ -0,0 +1,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