Files
b210-k7-fpga/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v
T
michael-west 4c7ee1eb8e TwinRX: Fix increased noise floor
- Fixed bus width from 25 to 24 bits

Signed-off-by: michael-west <michael.west@ettus.com>


Original-commit: fdd576cda1bab000745af66e565f4eb8c6272942
2020-07-21 11:47:20 -05:00

263 lines
9.1 KiB
Verilog

//
// Copyright 2015 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module rx_frontend_gen3 #(
parameter SR_MAG_CORRECTION = 0,
parameter SR_PHASE_CORRECTION = 1,
parameter SR_OFFSET_I = 2,
parameter SR_OFFSET_Q = 3,
parameter SR_IQ_MAPPING = 4,
parameter SR_HET_PHASE_INCR = 5,
parameter BYPASS_DC_OFFSET_CORR = 0,
parameter BYPASS_IQ_COMP = 0,
parameter BYPASS_REALMODE_DSP = 0,
parameter DEVICE = "7SERIES"
)(
input clk, input reset, input sync_in,
input set_stb, input [7:0] set_addr, input [31:0] set_data,
input adc_stb, input [15:0] adc_i, input [15:0] adc_q,
output rx_stb, output [15:0] rx_i, output [15:0] rx_q
);
wire realmode;
wire swap_iq;
wire invert_i;
wire invert_q;
wire downconvert;
wire bypass_all;
wire [1:0] iq_map_reserved;
wire [17:0] mag_corr, phase_corr;
wire phase_dir;
wire phase_sync;
reg [23:0] adc_i_mux, adc_q_mux;
reg adc_mux_stb;
wire [23:0] adc_i_ofs, adc_q_ofs, adc_i_comp, adc_q_comp;
wire adc_ofs_stb, adc_comp_stb;
reg [1:0] adc_ofs_stb_dly;
wire [23:0] adc_i_dsp, adc_q_dsp;
wire adc_dsp_stb;
wire [15:0] rx_i_out, rx_q_out;
/********************************************************
** Settings Bus Registers
********************************************************/
setting_reg #(.my_addr(SR_MAG_CORRECTION),.width(18)) sr_mag_corr (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(mag_corr),.changed());
setting_reg #(.my_addr(SR_PHASE_CORRECTION),.width(18)) sr_phase_corr (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(phase_corr),.changed());
setting_reg #(.my_addr(SR_IQ_MAPPING), .width(8)) sr_mux_sel (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),
.out({bypass_all,iq_map_reserved,downconvert,invert_i,invert_q,realmode,swap_iq}),
.changed());
// Setting reg: 1 bit to set phase direction: default to 0:
// direction bit == 0: the phase is increased by pi/2 (counter clockwise)
// direction bit == 1: the phase is increased by -pi/2 (clockwise)
setting_reg #(.my_addr(SR_HET_PHASE_INCR), .width(1)) sr_phase_dir (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(phase_dir),.changed(phase_sync));
/********************************************************
** IQ Mapping (swapping, inversion, real-mode)
********************************************************/
// MUX so we can do realmode signals on either input
always @(posedge clk) begin
if (swap_iq) begin
adc_i_mux[23:8] <= invert_q ? ~adc_q : adc_q;
adc_q_mux[23:8] <= realmode ? 16'd0 : invert_i ? ~adc_i : adc_i;
end else begin
adc_i_mux[23:8] <= invert_i ? ~adc_i : adc_i;
adc_q_mux[23:8] <= realmode ? 16'd0 : invert_q ? ~adc_q : adc_q;
end
adc_mux_stb <= adc_stb;
adc_i_mux[7:0] <= 8'd0;
adc_q_mux[7:0] <= 8'd0;
end
/********************************************************
** DC offset Correction
********************************************************/
generate
if (BYPASS_DC_OFFSET_CORR == 0) begin
rx_dcoffset #(.WIDTH(24),.ADDR(SR_OFFSET_I)) rx_dcoffset_i (
.clk(clk),.rst(reset),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
.in_stb(adc_mux_stb),.in(adc_i_mux),
.out_stb(adc_ofs_stb),.out(adc_i_ofs));
rx_dcoffset #(.WIDTH(24),.ADDR(SR_OFFSET_Q)) rx_dcoffset_q (
.clk(clk),.rst(reset),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
.in_stb(adc_mux_stb),.in(adc_q_mux),
.out_stb(),.out(adc_q_ofs));
end else begin
assign adc_ofs_stb = adc_mux_stb;
assign adc_i_ofs = adc_i_mux;
assign adc_q_ofs = adc_q_mux;
end
endgenerate
/********************************************************
** IQ Imbalance Compensation
********************************************************/
generate
if (BYPASS_IQ_COMP == 0) begin
mult_add_clip #(
.WIDTH_A(18),
.BIN_PT_A(17),
.WIDTH_B(18),
.BIN_PT_B(17),
.WIDTH_C(24),
.BIN_PT_C(23),
.WIDTH_O(24),
.BIN_PT_O(23),
.LATENCY(2)
) mult_i (
.clk(clk),
.reset(reset),
.CE(1'b1),
.A(adc_i_ofs[23:6]),
.B(mag_corr),
.C(adc_i_ofs),
.O(adc_i_comp)
);
mult_add_clip #(
.WIDTH_A(18),
.BIN_PT_A(17),
.WIDTH_B(18),
.BIN_PT_B(17),
.WIDTH_C(24),
.BIN_PT_C(23),
.WIDTH_O(24),
.BIN_PT_O(23),
.LATENCY(2)
) mult_q (
.clk(clk),
.reset(reset),
.CE(1'b1),
.A(adc_i_ofs[23:6]),
.B(phase_corr),
.C(adc_q_ofs),
.O(adc_q_comp)
);
// Delay to match path latencies
always @(posedge clk) begin
if (reset) begin
adc_ofs_stb_dly <= 2'b0;
end else begin
adc_ofs_stb_dly <= {adc_ofs_stb_dly[0], adc_ofs_stb};
end
end
assign adc_comp_stb = adc_ofs_stb_dly[1];
end else begin
assign adc_comp_stb = adc_ofs_stb;
assign adc_i_comp = adc_i_ofs;
assign adc_q_comp = adc_q_ofs;
end
endgenerate
/********************************************************
** Realmode DSP:
* - Heterodyne frequency translation
* - Realmode decimation (by 2)
********************************************************/
generate
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(
.clk(clk), .reset(reset || sync_in), .phase_sync(phase_sync),
.i_tdata({adc_i_comp, adc_q_comp}), .i_tlast(1'b1), .i_tvalid(adc_comp_stb), .i_tready(),
.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;
end else begin
assign adc_dsp_stb = adc_comp_stb;
assign adc_i_dsp = adc_i_comp;
assign adc_q_dsp = adc_q_comp;
end
endgenerate
// Round to short complex (sc16)
round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_i (
.clk(clk),.reset(reset), .in(adc_i_dsp),.strobe_in(adc_dsp_stb), .out(rx_i_out), .strobe_out(rx_stb));
round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_q (
.clk(clk),.reset(reset), .in(adc_q_dsp),.strobe_in(adc_dsp_stb), .out(rx_q_out), .strobe_out());
assign rx_i = bypass_all ? adc_i : rx_i_out;
assign rx_q = bypass_all ? adc_q : rx_q_out;
endmodule