- Fixed bus width from 25 to 24 bits Signed-off-by: michael-west <michael.west@ettus.com> Original-commit: fdd576cda1bab000745af66e565f4eb8c6272942
263 lines
9.1 KiB
Verilog
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
|