aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/dds_freq_tune.v
blob: 2491c01a137069872ad4e28b3700b629ab3237a5 (plain)
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// DDS frequency shift with complex multiply

module dds_freq_tune  #(
  parameter WIDTH = 24,
  parameter PHASE_WIDTH = 24,
  parameter SIN_COS_WIDTH = 16,
  parameter OUTPUT_WIDTH = 24
)(
  input         clk,
  input         reset,
  input         eob,
  input         rate_changed,
  input [15:0]  dds_input_fifo_occupied,
  /* IQ input */
  input [WIDTH*2-1:0]  s_axis_din_tdata,
  input         s_axis_din_tlast,
  input         s_axis_din_tvalid,
  output        s_axis_din_tready,
  /* Phase input from NCO */
  input [PHASE_WIDTH-1:0]  s_axis_phase_tdata,
  input         s_axis_phase_tlast,
  input         s_axis_phase_tvalid,
  output        s_axis_phase_tready,
  /* IQ output */
  output [OUTPUT_WIDTH*2-1:0] m_axis_dout_tdata,
  output        m_axis_dout_tlast,
  output        m_axis_dout_tvalid,
  input         m_axis_dout_tready,

  //debug signals
  output [2:0] state_out,
  output phase_valid_hold_out,
  output [7:0] phase_invalid_wait_count_out,
  output reset_dds_out,
  output m_axis_dds_tlast_out,
  output m_axis_dds_tvalid_out,
  output m_axis_dds_tready_out,
  output [SIN_COS_WIDTH*2-1:0] m_axis_dds_tdata_out //[31:16] = sin|q [15:0] cos|i
);

  //wires for dds output
  wire m_axis_dds_tlast;
  wire m_axis_dds_tvalid;
  wire m_axis_dds_tready;
  wire [SIN_COS_WIDTH*2-1:0] m_axis_dds_tdata; //[31:16] = sin|q [15:0] cos|i
  reg reset_reg;
  reg phase_valid_hold;
  reg [7:0] phase_invalid_wait_count;
  reg [2:0] state;
  reg reset_dds     = 1'b1;  // Init DDS resets to 1, since simulation model 
  reg reset_dds_reg = 1'b1;  // requires reset at time 0 to avoid failure.
  reg phase_ready_wait;
  wire s_axis_phase_tready_dds;

  //when we're holding valid, make ready low so no new data comes in.
  assign s_axis_phase_tready = s_axis_phase_tready_dds & ~phase_valid_hold;

  localparam INIT = 3'b000;
  localparam VALID = 3'b001;
  localparam WAIT = 3'b010;
  localparam HOLD_VALID = 3'b011;

  //reset needs to be 2 clk cycles minimum for Xilinx DDS IP
  always @(posedge clk) begin
    reset_reg <= reset;
    reset_dds_reg <= reset_dds;
  end

  //some logic to reset the dds when data is goes from valid to not valid
  //also holds valid high until the pipeline has passed tlast through.
  always @(posedge clk) begin
    if(reset) begin
      state <= INIT;
      phase_valid_hold <= 1'b0;
      phase_invalid_wait_count <= 16'h00;
      reset_dds <= 1'b0;
    end
    else begin
      case(state)
        INIT: begin//init case
          phase_valid_hold <= 1'b0;
          phase_invalid_wait_count <= 16'h0000;
          reset_dds <= 1'b0;
          if(s_axis_phase_tvalid) begin
            state <= VALID;
          end
        end
        VALID: begin //valid data
          if(~s_axis_phase_tvalid) begin
            state <= WAIT;
          end
        end
        WAIT: begin //wait until we either get valid data or don't
          if(m_axis_dds_tready) begin //only increment when the downstream can accept data.
            phase_invalid_wait_count <= phase_invalid_wait_count + 4'b1;
          end
          if(s_axis_phase_tvalid) begin //if we get valid data shortly after, then don't push data through and reset
            state <= INIT;
          end else begin
            if(eob | (phase_invalid_wait_count >= 16'h40) | rate_changed ) begin //if a valid never comes, aka eob
              state <= HOLD_VALID;
            end
          end
        end
        HOLD_VALID: begin//hold valid to finish pipeline. Apparently the dds IP won't empty without additional valids.
          phase_valid_hold <= 1'b1;
          // Wait for input FIFO to be empty
          if (~s_axis_din_tvalid) begin
            state <= INIT;
            reset_dds <= 1'b1;
          end
        end
      endcase
    end
  end

  //dds to generate sin/cos data from phase
  dds_sin_cos_lut_only dds_inst (
    .aclk(clk),                                // input wire aclk
    .aresetn(~(reset | reset_reg | reset_dds | reset_dds_reg)),            // input wire aresetn active low rst
    .s_axis_phase_tvalid(s_axis_phase_tvalid | phase_valid_hold),  // input wire s_axis_phase_tvalid
    .s_axis_phase_tready(s_axis_phase_tready_dds),  // output wire s_axis_phase_tready
    .s_axis_phase_tlast(s_axis_phase_tlast),     //tlast
    .s_axis_phase_tdata(s_axis_phase_tdata),    // input wire [23 : 0] s_axis_phase_tdata
    .m_axis_data_tvalid(m_axis_dds_tvalid),    // output wire m_axis_data_tvalid
    .m_axis_data_tready(m_axis_dds_tready),    // input wire m_axis_data_tready
    .m_axis_data_tlast(m_axis_dds_tlast),      // input wire m_axis_data_tready
    .m_axis_data_tdata(m_axis_dds_tdata)      // output wire [31 : 0] m_axis_data_tdata
  );

  wire [WIDTH*2-1:0] mult_in_a_tdata;
  wire mult_in_a_tvalid;
  wire mult_in_a_tready;
  wire mult_in_a_tlast;
  wire [SIN_COS_WIDTH*2-1:0] mult_in_b_tdata;
  wire mult_in_b_tvalid;
  wire mult_in_b_tready;
  wire mult_in_b_tlast; //no connect
  wire [2*32-1:0] mult_out_tdata;
  wire mult_out_tvalid;
  wire mult_out_tready;
  wire mult_out_tlast;

  axi_sync #(
    .SIZE(2),
    .WIDTH_VEC({SIN_COS_WIDTH*2, WIDTH*2}),
    .FIFO_SIZE(0))
  axi_sync (
    .clk(clk), .reset(reset), .clear(),
    .i_tdata({m_axis_dds_tdata,s_axis_din_tdata}),
    .i_tlast({m_axis_dds_tlast,s_axis_din_tlast}),
    .i_tvalid({m_axis_dds_tvalid,s_axis_din_tvalid}),
    .i_tready({m_axis_dds_tready,s_axis_din_tready}),
    .o_tdata({mult_in_b_tdata,mult_in_a_tdata}),
    .o_tlast({mult_in_b_tlast,mult_in_a_tlast}),
    .o_tvalid({mult_in_b_tvalid,mult_in_a_tvalid}),
    .o_tready({mult_in_b_tready,mult_in_a_tready}));

  //a = input i/q data stream 48 bit i/q lower bits i, upper bits q
  //b = output of dds 32 bit cos/sin. lower cos, upper sin
  complex_multiplier_dds complex_mult_inst (
    .aclk(clk),                              // input wire aclk
    .aresetn(~(reset | reset_reg)),                        // input wire aresetn
    .s_axis_a_tvalid(mult_in_a_tvalid),        // input wire s_axis_a_tvalid
    .s_axis_a_tready(mult_in_a_tready),        // output wire s_axis_a_tready
    .s_axis_a_tlast(mult_in_a_tlast),          // input wire s_axis_a_tlast
    .s_axis_a_tdata({mult_in_a_tdata}),          // input wire [47 : 0] s_axis_a_tdata
    .s_axis_b_tvalid(mult_in_b_tvalid),        // input wire s_axis_b_tvalid
    .s_axis_b_tready(mult_in_b_tready),        // output wire s_axis_b_tready
    .s_axis_b_tlast(mult_in_b_tlast),        // output wire s_axis_b_tlast
    .s_axis_b_tdata(mult_in_b_tdata),          // input wire [31 : 0] s_axis_b_tdata
    .m_axis_dout_tvalid(mult_out_tvalid),  // output wire m_axis_dout_tvalid
    .m_axis_dout_tready(mult_out_tready),  // input wire m_axis_dout_tready
    .m_axis_dout_tlast(mult_out_tlast),    // output wire m_axis_dout_tlast
    .m_axis_dout_tdata(mult_out_tdata)    // output wire [63 : 0] m_axis_dout_tdata
  );

  axi_round_complex #(
    .WIDTH_IN(32),
    .WIDTH_OUT(OUTPUT_WIDTH))
  axi_round_complex_inst (
    .clk(clk),
    .reset(reset | reset_reg),
    .i_tdata(mult_out_tdata),
    .i_tlast(mult_out_tlast),
    .i_tvalid(mult_out_tvalid),
    .i_tready(mult_out_tready),
    .o_tdata(m_axis_dout_tdata),
    .o_tlast(m_axis_dout_tlast),
    .o_tvalid(m_axis_dout_tvalid),
    .o_tready(m_axis_dout_tready));

  //debug
  assign state_out = state;
  assign phase_valid_hold_out = phase_valid_hold;
  assign phase_invalid_wait_count_out = phase_invalid_wait_count;
  assign reset_dds_out = reset_dds;
  assign m_axis_dds_tlast_out = m_axis_dds_tlast;
  assign m_axis_dds_tvalid_out = m_axis_dds_tvalid;
  assign m_axis_dds_tready_out = m_axis_dds_tready;
  assign m_axis_dds_tdata_out = m_axis_dds_tdata;

endmodule