File: tx_frontend_gen3.v

package info (click to toggle)
uhd 3.15.0.0-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 202,252 kB
  • sloc: cpp: 182,756; ansic: 94,109; vhdl: 53,420; python: 45,849; xml: 12,956; tcl: 7,046; makefile: 2,248; sh: 1,741; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (173 lines) | stat: -rw-r--r-- 5,287 bytes parent folder | download | duplicates (4)
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