1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
//
// Copyright 2015 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
module tx_frontend_gen3 #(
parameter SR_OFFSET_I = 0,
parameter SR_OFFSET_Q = 1,
parameter SR_MAG_CORRECTION = 2,
parameter SR_PHASE_CORRECTION = 3,
parameter SR_MUX = 4,
parameter BYPASS_DC_OFFSET_CORR = 0,
parameter BYPASS_IQ_COMP = 0,
parameter DEVICE = "7SERIES"
)(
input clk, input reset,
input set_stb, input [7:0] set_addr, input [31:0] set_data,
input tx_stb, input [15:0] tx_i, input [15:0] tx_q,
output reg dac_stb, output reg [15:0] dac_i, output reg [15:0] dac_q
);
wire [23:0] i_dco, q_dco;
wire [7:0] mux_ctrl;
wire [17:0] mag_corr, phase_corr;
wire [35:0] corr_i, corr_q;
reg [1:0] tx_stb_dly;
reg [23:0] tx_i_dly, tx_q_dly;
wire tx_comp_stb, tx_ofs_stb;
wire [23:0] tx_i_comp, tx_q_comp, tx_i_ofs, tx_q_ofs;
wire tx_round_stb;
wire [15:0] tx_i_round, tx_q_round;
/********************************************************
** Settings Registers
********************************************************/
setting_reg #(.my_addr(SR_OFFSET_I), .width(24)) sr_i_dc_offset (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(i_dco),.changed());
setting_reg #(.my_addr(SR_OFFSET_Q), .width(24)) sr_q_dc_offset (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(q_dco),.changed());
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_MUX), .width(8), .at_reset(8'h10)) sr_mux_ctrl (
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(mux_ctrl),.changed());
/********************************************************
** DSP
********************************************************/
// I/Q compensation with option to bypass
generate
if (BYPASS_IQ_COMP == 0) begin
mult_add_clip #(
.WIDTH_A(16),
.BIN_PT_A(15),
.WIDTH_B(18),
.BIN_PT_B(17),
.WIDTH_C(16),
.BIN_PT_C(15),
.WIDTH_O(24),
.BIN_PT_O(23),
.LATENCY(2)
) mult_i (
.clk(clk),
.reset(reset),
.CE(1'b1),
.A(tx_i),
.B(mag_corr),
.C(tx_i),
.O(tx_i_comp)
);
mult_add_clip #(
.WIDTH_A(16),
.BIN_PT_A(15),
.WIDTH_B(18),
.BIN_PT_B(17),
.WIDTH_C(16),
.BIN_PT_C(15),
.WIDTH_O(24),
.BIN_PT_O(23),
.LATENCY(2)
) mult_q (
.clk(clk),
.reset(reset),
.CE(1'b1),
.A(tx_i),
.B(phase_corr),
.C(tx_q),
.O(tx_q_comp)
);
// Delay to match path latencies
always @(posedge clk) begin
if (reset) begin
tx_stb_dly <= 2'b0;
end else begin
tx_stb_dly <= {tx_stb_dly[0], tx_stb};
end
end
assign tx_comp_stb = tx_stb_dly[1];
end else begin
assign tx_comp_stb = tx_stb;
assign tx_i_comp = {tx_i,8'd0};
assign tx_q_comp = {tx_q,8'd0};
end
endgenerate
// DC offset correction
generate
if (BYPASS_DC_OFFSET_CORR == 0) begin
add2_and_clip_reg #(.WIDTH(24)) add_dco_i (
.clk(clk), .rst(reset), .in1(i_dco), .in2(tx_i_comp), .strobe_in(tx_comp_stb), .sum(tx_i_ofs), .strobe_out(tx_ofs_stb));
add2_and_clip_reg #(.WIDTH(24)) add_dco_q (
.clk(clk), .rst(reset), .in1(q_dco), .in2(tx_q_comp), .strobe_in(tx_comp_stb), .sum(tx_q_ofs), .strobe_out());
end else begin
assign tx_ofs_stb = tx_comp_stb;
assign tx_i_ofs = tx_i_comp;
assign tx_q_ofs = tx_q_comp;
end
endgenerate
// Round to short complex (sc16)
round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_i (
.clk(clk),.reset(reset), .in(tx_i_ofs),.strobe_in(tx_ofs_stb), .out(tx_i_round), .strobe_out(tx_round_stb));
round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_q (
.clk(clk),.reset(reset), .in(tx_q_ofs),.strobe_in(tx_ofs_stb), .out(tx_q_round), .strobe_out());
// Mux
// Muxing logic matches that in tx_frontend.v, and what tx_frontend_core_200.cpp expects.
//
// mux_ctrl ! 0+0 ! 0+16 ! 1+0 ! 1+16
// =========!======!======!======!========
// DAC_I ! tx_i ! tx_i ! tx_q ! tx_q
// DAC_Q ! tx_i ! tx_q ! tx_i ! tx_q
//
// Most daughterboards will thus use 0x01 or 0x10 as the mux_ctrl value.
always @(posedge clk) begin
if (reset) begin
dac_stb <= 1'b0;
dac_i <= 16'd0;
dac_q <= 16'd0;
end else begin
dac_stb <= tx_round_stb;
case(mux_ctrl[3:0])
0 : dac_i <= tx_i_round;
1 : dac_i <= tx_q_round;
default : dac_i <= 0;
endcase
case(mux_ctrl[7:4])
0 : dac_q <= tx_i_round;
1 : dac_q <= tx_q_round;
default : dac_q <= 0;
endcase
end
end
endmodule
|