From b8163da1d197aacac208cf0cea60d052b9e4f97f Mon Sep 17 00:00:00 2001 From: michael-west Date: Fri, 2 Dec 2022 11:06:26 -0800 Subject: [PATCH] FPGA: Restore FIR filter in rx_frontend_gen3 The FIR filter applied gain. Removing it caused incorrect scaling and signals where every other sample was zero. Also parameterized heterodyne frontend processing so it would not use resources in images not requiring it. Signed-off-by: michael-west Original-commit: c27519624ec0f3a44ac1130635a03dafcf2e7f13 --- lib/control/fe_control.v | 9 ++- .../rfnoc_block_radio/rx_frontend_gen3.v | 73 ++++++++++++++++--- .../rx_frontend_gen3_tb.sv | 4 +- top/n3xx/n3xx_db_fe_core.v | 2 +- top/x300/x300_core.v | 3 +- 5 files changed, 73 insertions(+), 18 deletions(-) diff --git a/lib/control/fe_control.v b/lib/control/fe_control.v index 9e0c6ce..e79388e 100644 --- a/lib/control/fe_control.v +++ b/lib/control/fe_control.v @@ -14,7 +14,10 @@ module fe_control #( parameter NUM_CHANNELS = 2, parameter [7:0] SR_FE_CHAN_OFFSET = 16, parameter [7:0] SR_TX_FE_BASE = 192, - parameter [7:0] SR_RX_FE_BASE = 200 + parameter [7:0] SR_RX_FE_BASE = 200, + parameter BYPASS_DC_OFFSET_CORR = 0, + parameter BYPASS_IQ_COMP = 0, + parameter BYPASS_HETERODYNE = 1 )( input clk, input reset, // Commands from Radio Core @@ -37,7 +40,7 @@ module fe_control #( tx_frontend_gen3 #( .SR_OFFSET_I(SR_TX_OFFSET_I), .SR_OFFSET_Q(SR_TX_OFFSET_Q),.SR_MAG_CORRECTION(SR_TX_MAG_CORRECTION), .SR_PHASE_CORRECTION(SR_TX_PHASE_CORRECTION), .SR_MUX(SR_TX_MUX), - .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), + .BYPASS_DC_OFFSET_CORR(BYPASS_DC_OFFSET_CORR), .BYPASS_IQ_COMP(BYPASS_IQ_COMP), .DEVICE("7SERIES") ) tx_fe_corr_i ( .clk(clk), .reset(reset), @@ -56,7 +59,7 @@ module fe_control #( rx_frontend_gen3 #( .SR_MAG_CORRECTION(SR_RX_MAG_CORRECTION), .SR_PHASE_CORRECTION(SR_RX_PHASE_CORRECTION), .SR_OFFSET_I(SR_RX_OFFSET_I), .SR_OFFSET_Q(SR_RX_OFFSET_Q), .SR_IQ_MAPPING(SR_RX_IQ_MAPPING), .SR_HET_PHASE_INCR(SR_RX_HET_PHASE_INCR), - .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_REALMODE_DSP(0), + .BYPASS_DC_OFFSET_CORR(BYPASS_DC_OFFSET_CORR), .BYPASS_IQ_COMP(BYPASS_IQ_COMP), .BYPASS_HETERODYNE(BYPASS_HETERODYNE), .DEVICE("7SERIES") ) rx_fe_corr_i ( .clk(clk), .reset(reset), .sync_in(time_sync), diff --git a/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v b/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v index 841bffa..afff90f 100644 --- a/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v +++ b/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v @@ -22,13 +22,12 @@ // This register uses the following bits: // // Bit 0: Set to 1 to swap I and Q. -// Bit 1: Set to 1 to enable real mode (only relevant if -// BYPASS_REALMODE_DSP is set to 0, see below). If it is 1, then +// Bit 1: Set to 1 to enable real mode. If it is 1, then // the Q input signal is ignored and assumed to be zero. // Bit 2: Set to 1 to invert the Q input signal // Bit 3: Set to 1 to invert the I input signal // Bit 4: Set to 1 to enable the quarter-rate downconverter (only relevant -// when BYPASS_REALMODE_DSP is set to 0, see below). +// when BYPASS_HETERODYNE is set to 0, see below). // Bit 7: Disable all corrections in this module. // // 2) DC offset correction. See the rx_dcoffset module for details. This is @@ -49,13 +48,15 @@ // B is the value written to the register at SR_PHASE_CORRECTION. // Set BYPASS_IQ_COMP to 1 to not synthesize this step. // -// 4) Real-to-imaginary conversion. The converter is only enabled when the +// 4) Heterodyne conversion. The converter is only enabled when the // "downconvert" bit in the SR_IQ_MAPPING register is asserted. In this // case, it enables a quarter-rate mixer. The direction of this mixer is // 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). // -// Set BYPASS_REALMODE_DSP to 1 to not synthesize this step. +// The mixer is followed by a non-decimating FIR filter. +// +// Set BYPASS_HETERODYNE to 1 to not synthesize this step. // // // UHD Developers Note: This module is typically controlled by rx_frontend_core_3000 in UHD, @@ -74,7 +75,7 @@ // increment value // BYPASS_DC_OFFSET_CORR : Set to 1 to disable DC offset correction // BYPASS_IQ_COMP : Set to 1 to disable IQ offset correction -// BYPASS_REALMODE_DSP : Set to 1 to disable realmode to complex conversion +// BYPASS_HETERODYNE : Set to 1 to disable heterodyne conversion // DEVICE : Unused. // module rx_frontend_gen3 #( @@ -86,7 +87,7 @@ module rx_frontend_gen3 #( parameter SR_HET_PHASE_INCR = 5, parameter BYPASS_DC_OFFSET_CORR = 0, parameter BYPASS_IQ_COMP = 0, - parameter BYPASS_REALMODE_DSP = 0, + parameter BYPASS_HETERODYNE = 0, parameter DEVICE = "7SERIES" )( input clk, input reset, input sync_in, @@ -248,10 +249,12 @@ module rx_frontend_gen3 #( * - Realmode decimation (by 2) ********************************************************/ generate - if (BYPASS_REALMODE_DSP == 0) begin + if (BYPASS_HETERODYNE == 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( @@ -260,9 +263,57 @@ 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)); - 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; + // Double FIR 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; end else begin assign adc_dsp_stb = adc_comp_stb; diff --git a/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3_tb/rx_frontend_gen3_tb.sv b/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3_tb/rx_frontend_gen3_tb.sv index 711ffe7..d07372b 100644 --- a/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3_tb/rx_frontend_gen3_tb.sv +++ b/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3_tb/rx_frontend_gen3_tb.sv @@ -56,7 +56,7 @@ module rx_frontend_gen3_tb; localparam BYPASS_DC_OFFSET_CORR = 0; localparam BYPASS_IQ_COMP = 0; - localparam BYPASS_REALMODE_DSP = 0; + localparam BYPASS_HETERODYNE = 0; localparam DEVICE = "7SERIES"; logic sync_in; @@ -79,7 +79,7 @@ module rx_frontend_gen3_tb; .SR_HET_PHASE_INCR (SR_HET_PHASE_INCR), .BYPASS_DC_OFFSET_CORR (BYPASS_DC_OFFSET_CORR), .BYPASS_IQ_COMP (BYPASS_IQ_COMP), - .BYPASS_REALMODE_DSP (BYPASS_REALMODE_DSP), + .BYPASS_HETERODYNE (BYPASS_HETERODYNE), .DEVICE (DEVICE) ) rx_frontend_gen3_i ( .clk (clk), diff --git a/top/n3xx/n3xx_db_fe_core.v b/top/n3xx/n3xx_db_fe_core.v index 073d0ff..41e6dc7 100644 --- a/top/n3xx/n3xx_db_fe_core.v +++ b/top/n3xx/n3xx_db_fe_core.v @@ -64,7 +64,7 @@ module n3xx_db_fe_core #( rx_frontend_gen3 #( .SR_MAG_CORRECTION(SR_RX_FE_BASE + 0), .SR_PHASE_CORRECTION(SR_RX_FE_BASE + 1), .SR_OFFSET_I(SR_RX_FE_BASE + 2), .SR_OFFSET_Q(SR_RX_FE_BASE + 3), .SR_IQ_MAPPING(SR_RX_FE_BASE + 4), .SR_HET_PHASE_INCR(SR_RX_FE_BASE + 5), - .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_REALMODE_DSP(1), + .BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_HETERODYNE(1), .DEVICE("7SERIES") ) rx_fe_corr_i ( .clk(clk), .reset(reset), .sync_in(), diff --git a/top/x300/x300_core.v b/top/x300/x300_core.v index 32f0f5b..92ffc40 100644 --- a/top/x300/x300_core.v +++ b/top/x300/x300_core.v @@ -603,7 +603,8 @@ module x300_core #( .NUM_CHANNELS(NUM_CHANNELS_PER_DBOARD), .SR_FE_CHAN_OFFSET(SR_FE_CHAN_OFFSET), .SR_TX_FE_BASE(SR_TX_FE_BASE), - .SR_RX_FE_BASE(SR_RX_FE_BASE) + .SR_RX_FE_BASE(SR_RX_FE_BASE), + .BYPASS_HETERODYNE(0) ) x300_fe_core_i ( .clk(radio_clk), .reset(radio_rst), .set_stb(db_fe_set_stb[i]), .set_addr(db_fe_set_addr[i]), .set_data(db_fe_set_data[i]),