diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/complex_invert.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/complex_invert.v | 157 |
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 |