From ad533eb6416c76d2a986c97fff846785ee56567e Mon Sep 17 00:00:00 2001 From: michael-west Date: Mon, 20 Jul 2020 10:15:12 -0700 Subject: TwinRX: Remove frontend filter Removing the FIR filter in the frontend to reclaim resources and remove redundancy when using a DDC block. The default image has a DDC block, so only users making custom RFNoC images and using TwinRX will need to take care to properly downconvert the full bandwidth coming from the radio block. Signed-off-by: michael-west --- .../blocks/rfnoc_block_radio/rx_frontend_gen3.v | 67 +++------------------ host/docs/res/twinrx_alias.png | Bin 0 -> 53359 bytes host/docs/twinrx.dox | 14 +++++ 3 files changed, 22 insertions(+), 59 deletions(-) create mode 100644 host/docs/res/twinrx_alias.png diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v index c11f174e5..841bffaa9 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v @@ -39,10 +39,11 @@ // // 3) IQ imbalance correction. This implements a simple, one-shot IQ imbalance // correction. It will modify the I and Q signals as follows: -// _ _ _ _ _ _ -// | I' | | A/64+1 0 | | I | -// | | = | | | | -// |_Q'_| |_ B/64 1_| |_ Q _| +// _ _ _ _ _ _ +// | I' | | A/64+1 0 | | I | +// | | = | | | | +// | Q' | | B/64 1 | | Q | +// ‾ ‾ ‾ ‾ ‾ ‾ // // Here, A is the value written to the register at SR_MAG_CORRECTION, and // B is the value written to the register at SR_PHASE_CORRECTION. @@ -54,8 +55,6 @@ // controlled by the SR_HET_PHASE_INCR register (a 0 in this register // rotates by pi/2 every clock cycle, a 1 in this register rotates by -pi/2). // -// The mixer is followed by a non-decimating half-band filter. -// // Set BYPASS_REALMODE_DSP to 1 to not synthesize this step. // // @@ -252,9 +251,7 @@ module rx_frontend_gen3 #( if (BYPASS_REALMODE_DSP == 0) begin wire [23:0] adc_i_dsp_cout, adc_q_dsp_cout; - wire [23:0] adc_i_filt, adc_q_filt; wire adc_dsp_cout_stb; - wire adc_filt_stb; // 90 degree mixer quarter_rate_downconverter #(.WIDTH(24)) qr_dc_i( @@ -263,57 +260,9 @@ module rx_frontend_gen3 #( .o_tdata({adc_i_dsp_cout, adc_q_dsp_cout}), .o_tlast(), .o_tvalid(adc_dsp_cout_stb), .o_tready(1'b1), .dirctn(phase_dir)); - // Double FIR and decimator block - localparam HB_COEFS = {-18'd62, 18'd0, 18'd194, 18'd0, -18'd440, 18'd0, 18'd855, 18'd0, -18'd1505, 18'd0, 18'd2478, 18'd0, - -18'd3900, 18'd0, 18'd5990, 18'd0, -18'd9187, 18'd0, 18'd14632, 18'd0, -18'd26536, 18'd0, 18'd83009, 18'd131071, 18'd83009, - 18'd0, -18'd26536, 18'd0, 18'd14632, 18'd0, -18'd9187, 18'd0, 18'd5990, 18'd0, -18'd3900, 18'd0, 18'd2478, 18'd0, -18'd1505, - 18'd0, 18'd855, 18'd0, -18'd440, 18'd0, 18'd194, 18'd0, -18'd62}; - - // FIR filter for real part - axi_fir_filter #(.IN_WIDTH(24), .COEFF_WIDTH(18), .OUT_WIDTH(24), .NUM_COEFFS(47), .COEFFS_VEC(HB_COEFS), - .RELOADABLE_COEFFS(0), .BLANK_OUTPUT(0), .SYMMETRIC_COEFFS(1), .SKIP_ZERO_COEFFS(1), .USE_EMBEDDED_REGS_COEFFS(0) - ) hbfir0( - .clk(clk), - .reset(reset), - .clear(reset), - .s_axis_data_tdata(adc_i_dsp_cout), - .s_axis_data_tlast(1'b1), - .s_axis_data_tvalid(adc_dsp_cout_stb), - .s_axis_data_tready(), - .m_axis_data_tdata(adc_i_filt), - .m_axis_data_tlast(), - .m_axis_data_tvalid(adc_filt_stb), - .m_axis_data_tready(1'b1), - .s_axis_reload_tdata(18'd0), - .s_axis_reload_tvalid(1'b0), - .s_axis_reload_tlast(1'b0), - .s_axis_reload_tready() - ); - - // FIR filter for imag. part - axi_fir_filter #(.IN_WIDTH(24), .COEFF_WIDTH(18), .OUT_WIDTH(24), .NUM_COEFFS(47), .COEFFS_VEC(HB_COEFS), - .RELOADABLE_COEFFS(0), .BLANK_OUTPUT(0), .SYMMETRIC_COEFFS(1), .SKIP_ZERO_COEFFS(1), .USE_EMBEDDED_REGS_COEFFS(0) - ) hbfir1( - .clk(clk), - .reset(reset), - .clear(reset), - .s_axis_data_tdata(adc_q_dsp_cout), - .s_axis_data_tlast(1'b1), - .s_axis_data_tvalid(adc_dsp_cout_stb), - .s_axis_data_tready(), - .m_axis_data_tdata(adc_q_filt), - .m_axis_data_tlast(), - .m_axis_data_tvalid(), - .m_axis_data_tready(1'b1), - .s_axis_reload_tdata(18'd0), - .s_axis_reload_tvalid(1'b0), - .s_axis_reload_tlast(1'b0), - .s_axis_reload_tready() - ); - - assign adc_dsp_stb = downconvert ? adc_filt_stb : adc_comp_stb; - assign adc_i_dsp = downconvert ? adc_i_filt : adc_i_comp; - assign adc_q_dsp = downconvert ? adc_q_filt : adc_q_comp; + assign adc_dsp_stb = downconvert ? adc_dsp_cout_stb : adc_comp_stb; + assign adc_i_dsp = downconvert ? adc_i_dsp_cout : adc_i_comp; + assign adc_q_dsp = downconvert ? adc_q_dsp_cout : adc_q_comp; end else begin assign adc_dsp_stb = adc_comp_stb; diff --git a/host/docs/res/twinrx_alias.png b/host/docs/res/twinrx_alias.png new file mode 100644 index 000000000..1be1ca985 Binary files /dev/null and b/host/docs/res/twinrx_alias.png differ diff --git a/host/docs/twinrx.dox b/host/docs/twinrx.dox index d8dd20f6d..e0d327499 100644 --- a/host/docs/twinrx.dox +++ b/host/docs/twinrx.dox @@ -45,6 +45,20 @@ custom RFNoC FPGA images, but the DDC can be replaced by a custom block as long as it consumes the 400 Msps and produces an aggregate of less than 200 Msps. +Starting with UHD release 4.1, the X310/X300 FPGA images no longer contain a +halfband anti-aliasing filter directly after the ADC when using TwinRX. The +filter was left out because the radio no longer decimates the sampling rate for +TwinRX, instead relying on the DDC RFNoC block to adjust the sampling rate, and +the DSP utilization of the filter was permanent, even when no TwinRX daughtercard +was used. This means the output of the radio block (which produces a 200 Msps +output signal when using TwinRX) contains aliases of the signal. The following +image shows the output of the X310+TwinRX when configuring it to run at 200 Msps. +The center frequency is set to 2.1 GHz, and a signal is applied at approx. 2.13 GHz. + +\image html twinrx_alias.png "TwinRX Signal Aliases" + +Due to aliasing, a mirrored alias signal will also appear at 2.17 GHz. The +statically connected DDC block will remove these aliases. \image html TwinRX_Block_Diagram.png "TwinRX Block Diagram" -- cgit v1.2.3