aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v67
-rw-r--r--host/docs/res/twinrx_alias.pngbin0 -> 53359 bytes
-rw-r--r--host/docs/twinrx.dox14
3 files changed, 22 insertions, 59 deletions
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
--- /dev/null
+++ b/host/docs/res/twinrx_alias.png
Binary files 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"